sunrpc: defer rq_argp and rq_resp free until after RCU grace period

svc_rqst_free() frees rqstp->rq_argp and rqstp->rq_resp synchronously
via kfree(), but defers the rqstp struct free via kfree_rcu().  After
svc_exit_thread() calls list_del_rcu() and svc_rqst_free(), there is
a window where RCU readers that started before list_del_rcu() can still
traverse the thread list and find the rqstp.  These readers (e.g.
nfsd_nl_rpc_status_get_dumpit()) dereference rqstp->rq_argp, which has
already been freed — a use-after-free.

Fix this by moving the kfree of rq_argp and rq_resp into an explicit
call_rcu() callback alongside the struct free.  Resources not accessed
by RCU readers (bvec, buffer pages, scratch folio, auth_data) remain
synchronously freed.

Fixes: 812443865c ("sunrpc: add a rcu_head to svc_rqst and use kfree_rcu to free it")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260611-nfsd-testing-v2-4-5b90e276f2d9@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
This commit is contained in:
Jeff Layton 2026-06-11 16:00:47 -04:00 committed by Chuck Lever
parent e13d505af7
commit c479bde671

View File

@ -716,6 +716,15 @@ svc_release_buffer(struct svc_rqst *rqstp)
}
}
static void svc_rqst_free_rcu(struct rcu_head *head)
{
struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head);
kfree(rqstp->rq_resp);
kfree(rqstp->rq_argp);
kfree(rqstp);
}
static void
svc_rqst_free(struct svc_rqst *rqstp)
{
@ -724,10 +733,8 @@ svc_rqst_free(struct svc_rqst *rqstp)
svc_release_buffer(rqstp);
if (rqstp->rq_scratch_folio)
folio_put(rqstp->rq_scratch_folio);
kfree(rqstp->rq_resp);
kfree(rqstp->rq_argp);
kfree(rqstp->rq_auth_data);
kfree_rcu(rqstp, rq_rcu_head);
call_rcu(&rqstp->rq_rcu_head, svc_rqst_free_rcu);
}
static struct svc_rqst *