CVE-2026-89675: nfsd: fix UAF in async copy cancel and shutdown
In the Linux kernel, the following vulnerability has been resolved:
nfsd: fix UAF in async copy cancel and shutdown
An async copy could be freed or used after free while a teardown caller (OFFLOADCANCEL, nfsd4shutdowncopy, nfsd4cancelcopybysb) raced the copy kthread:
- findasynccopy() bumped copy->refcount but left the copy on clp->asynccopies, so the reaper's cleanupasynccopy() could run releasecopyfiles() concurrently with a cancel/shutdown caller. Both put and NULL nfsrc/nfdst without a common lock, double-putting the nfsdfile and freeing it early.
- nfsd4doasynccopy() set NFSD4COPYFSTOPPED before its final uses of the copy (nfsdupdatecmtimeattr() on copy->nfdst, nfsd4sendcboffload()). nfsd4stopcopy() treats a set STOPPED bit as "kthread done, skip kthreadstop()", so a teardown caller ran releasecopyfiles() -- which puts and NULLs nfdst -- while the kthread still dereferenced it (NULL/UAF).
- copy->copytask was never pinned. The one-shot kthread self-reaps on return, so kthreadstop()'s gettaskstruct() could touch a freed taskstruct.
- cocb is embedded in the copy, but nfsd4sendcboffload() held a reference only on the client, so a concurrent teardown could free the copy while the CBOFFLOAD callback was in flight.
Fix the teardown lifetime as a whole:
- findasynccopy() unlinks the copy (clear cpclp, listdelinit) under asynclock; the cancel, shutdown, and sb-cancel paths drop the list-membership reference via nfs4putcopy() after nfsd4stopcopy(). Drop the now-redundant listdel fixup from cleanupasynccopy().
- Because unlinking hides the copy from the reaper, its cleanupasynccopy() can no longer remove the copy's s2scpstateids entry; the cancel/shutdown/sb-cancel paths now call nfs4freecopystate() themselves (while cpclp is still valid) so the entry does not dangle at freed memory for the laundromat and managecpntfstate() to dereference.
- Give the kthread its own reference, taken in nfsd4copy() before wakeupprocess() and dropped at the end of nfsd4doasynccopy(); call wakeupprocess() before listadd().
- Pin the taskstruct with gettaskstruct() in nfsd4copy(), released in nfs4putcopy(), so kthreadstop() is safe whenever the kthread exits. Set NFSD4COPYFSTOPPED only in nfsd4stopcopy(), which now always kthreadstop()s before releasecopyfiles(); completion is still reported via NFSD4COPYFCOMPLETED, so nfsd4hasactiveasynccopies() is unaffected. Each teardown caller removes the copy from clp->asynccopies first, so kthreadstop() runs exactly once.
- Take a copy reference in nfsd4sendcboffload(), dropped in nfsd4cboffloadrelease(). The kthread still holds its own reference there, so the refcountinc() cannot race the final free.
- Read cpclp with smploadacquire() to pair with the unordered setbit()/clearbit() writers (Documentation/atomicbitops.rst).
Affected Software
Event History
Frequently Asked Questions
Which systems are exposed to this issue?
Linux systems running the kernel NFS server are exposed when asynchronous copy operations are in use. The affected paths involve async-copy teardown through OFFLOAD_CANCEL, server copy shutdown, or cancellation by superblock.
What race conditions are required to trigger the flaw?
A teardown or cancellation operation must race with the asynchronous copy kernel thread. The race can cause concurrent cleanup of source or destination files, dereference of a destination file after it has been released, or access to an already-freed kernel-thread task structure.
How can I determine whether a kernel includes the resolution?
Check whether the applicable stable-kernel fix is present: 9031493ef7369d5c59c4bacc96f0c85965f09a98, a385cf5e016b748babf94cc664e43a26e17db117, or 62c0f6eaf050bb9284c1f9cac6ed1770092e6b95. The provided data does not identify affected or fixed kernel version ranges.