[PATCH v4 0/9] NFSD: Fix UAFs in client teardown and state revocation
Chuck Lever <[email protected]>
| Newsgroups | org.kernel.vger.linux-nfs |
|---|---|
| Message-ID | <[email protected]> |
An NFSv4 stateid, and a bare lock owner reachable through the client's owner hash, hold only a raw pointer to the owning nfs4_client. That client outlives its state only because __destroy_client() drains every stateid and owner before free_client() runs. A walk that dereferences the client, or the owner, through one of those raw pointers after dropping the lock that kept it reachable races a concurrent teardown that can free it first. The NULL-pointer dereference reported during client teardown was one such race; auditing the pattern found more across the revocation and laundromat-reaping paths. Where the racing object is the client, the fix is uniform: pin it with cl_rpc_users across the window in which nn->client_lock is dropped. Skipping a client that is already expiring is part of the correctness argument, not cleanup. force_expire_client() stops consulting cl_rpc_users once its wait has passed, so a pin taken then would not hold the client; the walk must instead leave that client's state for its own teardown to drain. Testing the expiry and taking the pin under nn->client_lock is what makes the choice atomic against the expiry. Pinning the client from these paths introduces a lock-order edge that did not exist before: nn->client_lock now nests outside the inner region of nn->deleg_lock and outside nn->blocked_locks_lock, both leaf acquisitions everywhere else. The nesting is one-directional, and netns.h records the deleg_lock relationship; a reaping path added later must take nn->client_lock first. Two changes sit apart from the lifetime races. The revocation and reaping fixes left four open-coded copies of the pin-drop idiom, consolidated here into put_client_no_renew() helpers (patch 6). The same audit turned up an unrelated svc_export leak: free_ol_stateid_reaplist() reaps open and lock stateids through ->sc_free() directly, bypassing the reference drop nfs4_put_stid() performs, so the export stays pinned and blocks unmount (patch 9). --- Changes in v4: - Fix svc_export double put in nfs4_put_stid() (sashiko) - Link to v3: https://patch.msgid.link/[email protected] Changes in v3: - Fix client UAF in laundromat blocked-lock reaping (Neil) - Fix client UAF in laundromat close_lru reaping (sashiko) - Fix svc_export leak when reaping open stateids (sashiko) - Link to v2: https://patch.msgid.link/[email protected] Changes in v2: - Add matching UAF fixes in several other paths --- Chuck Lever (9): NFSD: Prevent lock owner use-after-free during client teardown NFSD: Prevent client use-after-free during delegation revoke NFSD: Prevent client use-after-free during admin state revocation NFSD: Prevent client use-after-free during export state revocation NFSD: Prevent client use-after-free during NFSv4.0 revoked-state cleanup NFSD: Consolidate the revocation-path client unpin NFSD: Prevent client use-after-free during blocked-lock reaping NFSD: Prevent client use-after-free during close_lru reaping NFSD: Release the export reference when reaping open stateids fs/nfsd/netns.h | 6 ++- fs/nfsd/nfs4state.c | 149 ++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 131 insertions(+), 24 deletions(-) --- base-commit: ee6ae4a6bf3565b880dfb420017337475dfbc9ea change-id: 20260705-cel-61c1c70caa03 Best regards, -- Chuck Lever