Descripción
En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad: nfsd: proteger cl_cb_session con RCU para corregir un uso después de liberar memoria (use-after-free) al desmantelar la sesión Después de un DESTROY_SESSION, la ruta de desmantelamiento por sesión puede liberar una sesión mientras rpciod aún mantiene un rpc_task de callback en curso que desreferencia clp->cl_cb_session. nfsd4_probe_callback_sync() vacía cl_callback_wq, pero una vez que nfsd4_run_cb_work() ha llamado a rpc_call_async(), el rpc_task reside en rpciod; vaciar la cola de trabajo no espera por él. rpc_shutdown_client() sí drena las tareas de rpciod, pero usa un wait_event_timeout de 1 segundo; las tareas atascadas en rpc_delay() (por ejemplo, reintentos NFS4ERR_DELAY de 2 segundos) pueden sobrevivir al drenado. ruta de destrucción rpciod ------------ ------ unhash_session(ses) nfsd4_probe_callback_sync(clp) flush_workqueue(cl_callback_wq) /* retorna; rpc_task sigue activo */ nfsd4_put_session_locked(ses) free_session(ses) -> kfree(ses) nfsd4_cb_sequence_done() lee cb_clp->cl_cb_session /* slab liberado */ Existe una segunda ventana en nfsd4_process_cb_update(). Cuando __nfsd4_find_backchannel() retorna NULL porque unhash_session() ya eliminó la sesión destruida de cl_sessions, setup_callback_client() toma el retorno anticipado de v4.1, de modo que clp->cl_cb_session = ses nunca se ejecuta y el campo conserva un puntero a la sesión que está por liberarse. Se corrigen ambas convirtiendo cl_cb_session en un puntero protegido por RCU: - Se mueve la asignación cl_cb_session = ses en setup_callback_client() a después de que rpc_create() tenga éxito, de modo que solo se publique cuando exista un canal de retorno funcional. Se limpia cl_cb_session en el retorno de error en nfsd4_process_cb_update(). Ambas escrituras usan rcu_assign_pointer(). - Se anota cl_cb_session con __rcu. Todos los lectores del lado de rpciod usan rcu_read_lock()/rcu_dereference() y verifican si es NULL, desviándose a la ruta de error o de reencolado correspondiente: encode_cb_sequence4args(), decode_cb_sequence4resok(), nfsd41_cb_get_slot(), nfsd41_cb_release_slot(), nfsd4_cb_prepare() y nfsd4_cb_sequence_done(). - Se cambia __free_session() de kfree() a kfree_rcu() para que el slab de la sesión no se recupere hasta después de un período de gracia de RCU, garantizando que los lectores de rpciod dentro de rcu_read_lock() nunca desreferencien memoria liberada. - Se pasa el puntero de sesión a los tracepoints nfsd_cb_seq_status y nfsd_cb_free_slot en lugar de que estos vuelvan a leer cl_cb_session. - nfsd4_cb_prepare() llama a rpc_exit() cuando la sesión es NULL, encaminando por la ruta done/release para reencolar el callback.
Qué hacer
Revisa si usas el producto afectado y en qué versión, y aplica la actualización o la mitigación que indique el aviso del fabricante. CISA no ha confirmado que se esté explotando; si eso cambia, esta ficha pasa a «explotada» y te avisamos en el boletín.
Versiones afectadas
- Linux Afectadas: dcbeaa68dbbdacbbb330a86c7fc95a28473fc209 y anteriores a 2775ec1d617e665eab71ce0f13ab1f7959713a2b · dcbeaa68dbbdacbbb330a86c7fc95a28473fc209 y anteriores a f164eb52b6f3cbf40f07fe379f9f421f88e02f76 · dcbeaa68dbbdacbbb330a86c7fc95a28473fc209 y anteriores a 13bdd486c3aad4fc19e6d8b9c3556a4b4c190b25 · dcbeaa68dbbdacbbb330a86c7fc95a28473fc209 y anteriores a 01c5d5f58a5db9b0ee5afba2e49d3157788687b2
- Linux Afectadas: 2.6.38 No afectadas: 6.12.111 · 6.18.50 · 7.2.4 · 7.3
Según el registro de la vulnerabilidad en CVE.org, tal como lo publicó el fabricante. Confirma tu versión exacta en el aviso del fabricante.
Referencias
- Referencia en git.kernel.orggit.kernel.org/stable/c/2775ec1d617e665eab71ce0f13ab1f7959713a2b
- Referencia en git.kernel.orggit.kernel.org/stable/c/f164eb52b6f3cbf40f07fe379f9f421f88e02f76
- Referencia en git.kernel.orggit.kernel.org/stable/c/13bdd486c3aad4fc19e6d8b9c3556a4b4c190b25
- Referencia en git.kernel.orggit.kernel.org/stable/c/01c5d5f58a5db9b0ee5afba2e49d3157788687b2
Ver el texto original en inglés (CVE.org)
nfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown
In the Linux kernel, the following vulnerability has been resolved: nfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown After a DESTROY_SESSION the per-session teardown path can free a session while rpciod still holds an inflight callback rpc_task that dereferences clp->cl_cb_session. nfsd4_probe_callback_sync() flushes cl_callback_wq, but once nfsd4_run_cb_work() has called rpc_call_async() the rpc_task lives on rpciod; flushing the workqueue does not wait for it. rpc_shutdown_client() does drain rpciod tasks, but uses a 1-second wait_event_timeout — tasks stuck in rpc_delay() (e.g. 2-second NFS4ERR_DELAY retries) can outlive the drain. destroy path rpciod ------------ ------ unhash_session(ses) nfsd4_probe_callback_sync(clp) flush_workqueue(cl_callback_wq) /* returns; rpc_task still live */ nfsd4_put_session_locked(ses) free_session(ses) -> kfree(ses) nfsd4_cb_sequence_done() reads cb_clp->cl_cb_session /* freed slab */ A second window exists in nfsd4_process_cb_update(). When __nfsd4_find_backchannel() returns NULL because unhash_session() has already removed the destroyed session from cl_sessions, setup_callback_client() takes the v4.1 early return so clp->cl_cb_session = ses never fires and the field retains a pointer to the about-to-be-freed session. Fix both by converting cl_cb_session to an RCU-protected pointer: - Move the cl_cb_session = ses assignment in setup_callback_client() to after rpc_create() succeeds, so it is only published when a working backchannel exists. Clear cl_cb_session on the error return in nfsd4_process_cb_update(). Both stores use rcu_assign_pointer(). - Annotate cl_cb_session with __rcu. All rpciod-side readers use rcu_read_lock()/rcu_dereference() and check for NULL, bailing to the appropriate error or requeue path: encode_cb_sequence4args(), decode_cb_sequence4resok(), nfsd41_cb_get_slot(), nfsd41_cb_release_slot(), nfsd4_cb_prepare(), and nfsd4_cb_sequence_done(). - Switch __free_session() from kfree() to kfree_rcu() so the session slab is not reclaimed until after an RCU grace period, guaranteeing that rpciod readers inside rcu_read_lock() never dereference freed memory. - Pass the session pointer to the nfsd_cb_seq_status and nfsd_cb_free_slot tracepoints instead of having them re-read cl_cb_session. - nfsd4_cb_prepare() calls rpc_exit() when the session is NULL, routing through the done/release path to requeue the callback.