Vulnerabilidad en kernel de Linux (CVE-2025-38365)
Fecha de publicación:
25/07/2025
En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad: btrfs: corrige una ejecución entre los cambios de nombre y el registro de directorios Tenemos una ejecución entre un cambio de nombre y el registro del inodo del directorio que, si ocurre y nos bloqueamos o falla la energía antes de que se complete el cambio de nombre, la próxima vez que se monte el sistema de archivos, el código de reproducción del registro terminará eliminando el archivo que se estaba cambiando de nombre. Esto se explica mejor siguiendo un análisis paso a paso de un intercalado de pasos que conducen a esta situación. Considere las condiciones iniciales: 1) Estamos en la transacción N; 2) Tenemos los directorios A y B creados en una transacción anterior (< N); 3) Tenemos el inodo X correspondiente a un archivo que tiene 2 enlaces duros, uno en el directorio A y el otro en el directorio B, por lo que los nombraremos como "A/foo_link1" y "B/foo_link2". Ambos enlaces duros persistieron en una transacción anterior (< N); 4) Tenemos el inodo Y, correspondiente a un archivo con un único enlace físico ubicado en el directorio A, al que llamaremos "A/bar". Este archivo también se conservó en una transacción anterior (< N). Los pasos que conducen a la pérdida del archivo son los siguientes, y para todos ellos, estamos en la transacción N: 1) Se elimina el enlace "A/foo_link1", por lo que el campo X last_unlink_trans del inodo se actualiza a N mediante btrfs_unlink() -> btrfs_record_unlink_dir(); 2) La tarea A inicia un cambio de nombre para el inodo Y, con el objetivo de cambiar de "A/bar" a "A/baz", por lo que introducimos btrfs_rename(); 3) La tarea A inserta la nueva clave BTRFS_INODE_REF_KEY para el inodo Y mediante la llamada a btrfs_insert_inode_ref(); 4) Debido a que el cambio de nombre ocurre en el mismo directorio, no establecemos el campo last_unlink_trans del inodo del directorio A en el id de transacción actual, es decir, no llamamos a btrfs_record_unlink_dir(); 5) Luego, la tarea A elimina las entradas del directorio A (elementos BTRFS_DIR_ITEM_KEY y BTRFS_DIR_INDEX_KEY) cuando llama a __btrfs_unlink_inode() (en realidad, el elemento de índice del directorio se agrega como un elemento retrasado, pero el efecto es el mismo); 6) Ahora, antes de que la tarea A agregue la nueva entrada "A/baz" al directorio A llamando a btrfs_add_link(), otra tarea, la tarea B, está registrando el inodo X; 7) La tarea B inicia una sincronización fsync del inodo X y, tras registrarlo, en btrfs_log_inode_parent() llama a btrfs_log_all_parents(), ya que el inodo X tiene un valor de last_unlink_trans de N, establecido en el paso 1. 8) En btrfs_log_all_parents() buscamos todos los directorios padre del inodo X utilizando un root commit, por lo que encontramos los directorios A y B y los registramos. Sin embargo, al registrar directamente A, ya no tenemos un elemento de índice de directorio para el inodo Y, ni para el nombre antiguo "A/bar" ni para el nuevo nombre "A/baz", ya que el cambio de nombre ha eliminado el nombre antiguo, pero aún no ha insertado el nuevo. La tarea A aún no ha llamado a btrfs_add_link() para hacerlo. Tenga en cuenta que registrar el directorio A no recurre a un commit de transacción porque su valor de last_unlink_trans es menor que el ID de la transacción actual (véase el paso 4). 9) La tarea B finaliza el registro de los directorios A y B y regresa a btrfs_sync_file(), donde invoca btrfs_sync_log() para persistir el árbol de registro. 10) La tarea B persistió correctamente el árbol de registro, btrfs_sync_log() se completó correctamente y se produjo un corte de energía. Tenemos un árbol de registro sin ninguna entrada de directorio para el inodo Y, por lo que el código de reproducción del registro elimina la entrada del inodo Y, llamada "A/bar", del árbol de subvolumen, ya que no existe en el árbol de registro y este es autoritario para su índice (registramos un elemento BTRFS_DIR_LOG_INDEX_KEY que cubre el rango de índices de la entrada dentry correspondiente a "A/bar"). ---truncado---
Gravedad: Pendiente de análisis
Última modificación:
25/07/2025