mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 15:43:08 -04:00
NFSD: Prevent client use-after-free during export state revocation
nfsd4_revoke_export_states() has the same use-after-free as
nfsd4_revoke_states(): it drops nn->client_lock across
revoke_one_stid() and the following read of clp->cl_minorversion, but
the stateid reference it holds does not pin the client. A teardown
racing the dropped lock can free the client while revoke_one_stid()
still dereferences it.
exportfs -u drives this path through NFSD_CMD_UNLOCK_EXPORT, so an
administrator removing an export can race a client expiry.
Skip a client that is already expiring and otherwise pin it with
cl_rpc_users under client_lock before dropping the lock, matching
nfsd4_revoke_states().
Fixes: 2eac189bb0 ("NFSD: Add NFSD_CMD_UNLOCK_EXPORT netlink command")
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260709-cel-v4-4-1d519d9be0cb@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
This commit is contained in:
@@ -2055,10 +2055,14 @@ void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path)
|
||||
struct nfs4_client *clp;
|
||||
retry:
|
||||
list_for_each_entry(clp, head, cl_idhash) {
|
||||
struct nfs4_stid *stid = find_one_export_stid(
|
||||
clp, path,
|
||||
sc_types);
|
||||
struct nfs4_stid *stid;
|
||||
|
||||
/* Skip or pin clp as in nfsd4_revoke_states(). */
|
||||
if (is_client_expired(clp))
|
||||
continue;
|
||||
stid = find_one_export_stid(clp, path, sc_types);
|
||||
if (stid) {
|
||||
atomic_inc(&clp->cl_rpc_users);
|
||||
spin_unlock(&nn->client_lock);
|
||||
revoke_one_stid(nn, clp, stid);
|
||||
nfs4_put_stid(stid);
|
||||
@@ -2066,6 +2070,9 @@ void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path)
|
||||
if (clp->cl_minorversion == 0)
|
||||
nn->nfs40_last_revoke =
|
||||
ktime_get_boottime_seconds();
|
||||
if (atomic_dec_and_test(&clp->cl_rpc_users) &&
|
||||
is_client_expired(clp))
|
||||
wake_up_all(&expiry_wq);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user