CVE-2026-90036: NFSD: Prevent client use-after-free during blocked-lock reaping
In the Linux kernel, the following vulnerability has been resolved:
NFSD: Prevent client use-after-free during blocked-lock reaping
A bare lock owner -- its only remaining reference a blocked lock on nn->blockedlockslru -- holds a raw pointer to its nfs4client but no reference keeping the client alive. When the per-net laundromat reaps such a lock, freeing the nbl drops the owner reference held through flcowner, and the final nfs4putstateowner() takes the client's cllock. Because the laundromat detaches the nbl first, destroyclient() no longer finds it, so a concurrent forceexpireclient() can free the client before nfs4putstateowner() runs, dereferencing cllock in freed memory.
Pin the client with clrpcusers before dropping nn->blockedlockslock, and skip clients already expiring, whose blocked locks destroyclient() frees while holding an owner reference. Take nn->clientlock outside nn->blockedlockslock. Every other site holds nn->blockedlockslock as a leaf, acquiring no further lock, so placing nn->clientlock outside it cannot form a lock-order cycle.
Affected Software
Event History
Frequently Asked Questions
Which systems are exposed to this race condition?
Systems using the Linux kernel NFSD implementation are exposed when blocked locks are reaped by the per-network-namespace laundromat while a client is concurrently being force-expired. The issue involves NFSv4 client and lock-owner lifecycle handling.
What conditions are required to trigger the use-after-free?
A lock owner must have only a blocked lock remaining on nn->blocked_locks_lru, leaving it with a raw nfs4_client pointer but no reference that keeps the client alive. Concurrent blocked-lock reaping and force_expire_client() can then free the client before nfs4_put_stateowner() accesses the client's cl_lock.
What does the fix change?
The fix pins the client through cl_rpc_users before releasing nn->blocked_locks_lock, preventing the client from being freed during reaping. It also skips clients already expiring, because __destroy_client() frees their blocked locks while retaining an owner reference.