CVE-2026-98980 in Linux
Resumen
por VulDB • 2026-10-23
En el kernel de Linux se ha resuelto la siguiente vulnerabilidad:
namei: no revelar entradas sobre montadas durante el recorrido por referencias (refwalk)
En rcuwalk, el dentry es validado antes de ser aceptado. step_into() vuelve a verificar d_seq y __follow_mount_rcu() vuelve a verificar mount_lock. Una entrada que se desvincula en medio provoca que la búsqueda reintente y falle.
Un refwalk no hace esto. lookup_fast() toma una referencia sobre el dentry hashado y simplemente lo acepta. Por tanto, un unlink que ocurra después de que se haya tomado la referencia no es detectado por el refwalk. Esto está bien para un archivo simple; simplemente abrimos antes de que fuera desvinculado, sin problema.
Para un punto de montaje (mountpoint) y específicamente para un punto de montaje bloqueado, esto no está bien. El unlink desconecta todos los montajes y luego elimina el nombre. Cualquier refwalk que aún no haya atravesado los montajes simplemente revela la entrada subyacente. Es una ventana muy estrecha pero puede ser alcanzada:
lecturas de un archivo cubierto en 60 s, con 3 caminantes (walkers), ~15000 desvinculaciones sin ampliación 27 ese paso + 200 us 3741
Vea el parche adjunto para obtener un reproductor más fiable.
Por lo tanto, compruebe la entrada después de step_into(). unlink(), rmdir() y rename() marcan el dentry con dont_mount() antes de desconectar los montajes y eliminar el dentry.
Así que un dentry marcado con DCACHE_CANT_MOUNT y deshashado para cuando se revisaron sus montajes es un nombre que fue desvinculado durante el refwalk. DCACHE_CANT_MOUNT se lee después de haber buscado los montajes y se establece antes de que sean desconectados por detach_mounts(). Un refwalk que haya perdido la referencia a los montajes verá DCACHE_CANT_MOUNT.
d_unlinked() simple está bien. Una rename quita el dentry de su cadena hash pero ___d_drop() deja d_hash.pprev establecido. Por lo tanto, solo __d_drop() y una rename sobre el dentry lo deshashan. El único movimiento que invierte IS_ROOT es un splice en un alias desconectado. Esto no tiene DCACHE_CANT_MOUNT configurado.
Añada esto a step_into_slowpath(). ".." y LOOKUP_DOWN pueden aterrizar legítimamente en un directorio deshashado. Un refwalk que haya cruzado hacia un montaje tendrá path.mnt diferente de nd->path.mnt. Un dentry que el sistema de archivos eliminó por su cuenta (d_invalidate(), d_drop()) no tiene la bandera configurada y se trata como antes.
// SPDX-License-Identifier: GPL-2.0 /* * unlink_covered: desvincular un archivo que es un punto de montaje en una copia desconectada * de su montaje, contra caminantes (walkers) que lo abren a través de esa copia. * * A es un tmpfs con el archivo f1 (contenido SECRET_F) y el archivo plano MARK_A. * A' es una copia open_tree(OPEN_TREE_CLONE) de A con MARK_A vinculado en A'/f1, * mantenida a través de un fd O_PATH sobre su raíz una vez que se cierra el fd del árbol. * Los caminantes abren f1 a través de ese fd mientras el controlador desvincula A/f1, * donde no hay nada montado en él. Un caminante puede leer MARK_A o obtener ENOENT. * Una lectura de SECRET_F es un acierto: se encontró el nombre después de que su montaje * hubiera desaparecido. Los recorridos sin bloqueo utilizan openat2(RESOLVE_CACHED). * * uso: unlink_covered [-t segundos] [-w caminantes]
*/ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdatomic.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/syscall.h> #include <linux/openat2.h>
#ifndef __NR_open_tree #define __NR_open_tree 428 #endif #ifndef __NR_move_mount #define __NR_move_mount 429 #endif #ifndef __NR_openat2 #define __NR_openat2 437 #endif #ifndef OPEN_TREE_CLONE #define OPEN_TREE_CLONE 1 #endif #ifndef OPEN_TREE_CLOEXEC #define OPEN_TREE_CLOEXEC O_CLOEXEC #endif #ifndef MOVE_MOUNT_F_EMPTY_PATH #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 #endif
#define WORK "/tmp/uc" #define ADIR WORK "/A"
static int duration = 60, nwalkers = 3; static atomic_int stop, writer_waiting; static pthread_rwlock_t cur_lock = PTHREAD_RWLOCK_INITIALIZER; static int cur_fd = -1; /* la raíz de A', -1 mientras no haya ninguna */ static atomic_long n_unlink, n_walk, n_mark, n_enoent, n_othe ---truncado---
VulDB is the best source for vulnerability data and more expert information about this specific topic.