CVE-2026-89708
Gravedad CVSS v3.1:
CRÍTICA
Tipo:
No Disponible / Otro tipo
Fecha de publicación:
11/09/2026
Última modificación:
21/09/2026
Descripción
*** Pendiente de traducción *** In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
nfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown<br />
<br />
After a DESTROY_SESSION the per-session teardown path can free a<br />
session while rpciod still holds an inflight callback rpc_task that<br />
dereferences clp->cl_cb_session. nfsd4_probe_callback_sync() flushes<br />
cl_callback_wq, but once nfsd4_run_cb_work() has called<br />
rpc_call_async() the rpc_task lives on rpciod; flushing the workqueue<br />
does not wait for it. rpc_shutdown_client() does drain rpciod tasks,<br />
but uses a 1-second wait_event_timeout — tasks stuck in rpc_delay()<br />
(e.g. 2-second NFS4ERR_DELAY retries) can outlive the drain.<br />
<br />
destroy path rpciod<br />
------------ ------<br />
unhash_session(ses)<br />
nfsd4_probe_callback_sync(clp)<br />
flush_workqueue(cl_callback_wq)<br />
/* returns; rpc_task still live */<br />
nfsd4_put_session_locked(ses)<br />
free_session(ses) -> kfree(ses)<br />
nfsd4_cb_sequence_done()<br />
reads cb_clp->cl_cb_session<br />
/* freed slab */<br />
<br />
A second window exists in nfsd4_process_cb_update(). When<br />
__nfsd4_find_backchannel() returns NULL because unhash_session() has<br />
already removed the destroyed session from cl_sessions,<br />
setup_callback_client() takes the v4.1 early return so<br />
clp->cl_cb_session = ses never fires and the field retains a pointer<br />
to the about-to-be-freed session.<br />
<br />
Fix both by converting cl_cb_session to an RCU-protected pointer:<br />
<br />
- Move the cl_cb_session = ses assignment in setup_callback_client()<br />
to after rpc_create() succeeds, so it is only published when a<br />
working backchannel exists. Clear cl_cb_session on the error<br />
return in nfsd4_process_cb_update(). Both stores use<br />
rcu_assign_pointer().<br />
<br />
- Annotate cl_cb_session with __rcu. All rpciod-side readers use<br />
rcu_read_lock()/rcu_dereference() and check for NULL, bailing to<br />
the appropriate error or requeue path:<br />
encode_cb_sequence4args(), decode_cb_sequence4resok(),<br />
nfsd41_cb_get_slot(), nfsd41_cb_release_slot(),<br />
nfsd4_cb_prepare(), and nfsd4_cb_sequence_done().<br />
<br />
- Switch __free_session() from kfree() to kfree_rcu() so the<br />
session slab is not reclaimed until after an RCU grace period,<br />
guaranteeing that rpciod readers inside rcu_read_lock() never<br />
dereference freed memory.<br />
<br />
- Pass the session pointer to the nfsd_cb_seq_status and<br />
nfsd_cb_free_slot tracepoints instead of having them re-read<br />
cl_cb_session.<br />
<br />
- nfsd4_cb_prepare() calls rpc_exit() when the session is NULL,<br />
routing through the done/release path to requeue the callback.
Impacto
Puntuación base 3.x
9.80
Gravedad 3.x
CRÍTICA


