diff options
| author | Jeff Layton <jlayton@kernel.org> | 2026-06-11 16:00:47 -0400 |
|---|---|---|
| committer | Chuck Lever <cel@kernel.org> | 2026-07-05 21:07:22 -0400 |
| commit | 3d1f7950ee03fc0c39bd355f67d335063f76a5e4 (patch) | |
| tree | 69f85857e5fab9bc8ad7b4481003e4a84d299483 | |
| parent | 616a95b09c186de8a4f89fb58681ec6795f2fadd (diff) | |
| download | linux-next-3d1f7950ee03fc0c39bd355f67d335063f76a5e4.tar.gz linux-next-3d1f7950ee03fc0c39bd355f67d335063f76a5e4.zip | |
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: 812443865c5f ("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>
| -rw-r--r-- | net/sunrpc/svc.c | 13 |
1 files changed, 10 insertions, 3 deletions
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 86d39610cf0a..dd80a2eaaa74 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -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 * |
