From: Jeff Layton <jlayton@kernel.org>
To: Chris Mason <clm@meta.com>, Chuck Lever <chuck.lever@oracle.com>,
"J. Bruce Fields" <bfields@fieldses.org>,
Trond Myklebust <trondmy@gmail.com>,
linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 1/2] nfsd: clear cl_cb_session on DESTROY_SESSION
Date: Tue, 19 May 2026 15:48:07 -0400 [thread overview]
Message-ID: <92813369f8e3e62960c21be4822b1b7c97b9729b.camel@kernel.org> (raw)
In-Reply-To: <20260519180032.1852793-3-clm@meta.com>
Dueling agent review. Looks like both problems are valid:
> The parent commit drains in-flight backchannel callbacks before
> nfsd4_destroy_session() frees the session, which closes the primary
> use-after-free. clp->cl_cb_session still caches the freed pointer
> though: no destroy, expire, or shutdown path clears it, so the
> stale value survives until the next CREATE_SESSION overwrites it.
The drain is added by the companion patch "nfsd: drain inflight
callbacks in probe_callback_sync", which is patch 2/2 in this
series and applied after this one. Should "The parent commit
drains" be rephrased, or should the series order be swapped so
the drain lands first?
> diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
> index 50827405468d..8af2d0cc37c2 100644
> --- a/fs/nfsd/nfs4callback.c
> +++ b/fs/nfsd/nfs4callback.c
[ ... ]
> @@ -1278,10 +1280,12 @@ static int grab_slot(struct nfsd4_session *ses)
> static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task)
> {
> struct nfs4_client *clp = cb->cb_clp;
> - struct nfsd4_session *ses = clp->cl_cb_session;
> + struct nfsd4_session *ses = READ_ONCE(clp->cl_cb_session);
>
> if (cb->cb_held_slot >= 0)
> return true;
> + if (!ses)
> + return false;
When ses is NULL, this returns false without calling
rpc_sleep_on(). The existing slot-unavailable path a few lines
below sleeps the task on cl_cb_waitq before returning false:
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
cb->cb_held_slot = grab_slot(ses);
if (cb->cb_held_slot < 0)
return false;
nfsd41_cb_get_slot is the rpc_call_prepare callback, and
rpc_init_task() sets tk_action to rpc_prepare_task when
rpc_call_prepare is provided. When prepare returns without
calling rpc_call_start() and without sleeping the task,
__rpc_execute() loops back and calls prepare again because
tk_action is unchanged and the task is not queued:
__rpc_execute():
for (;;) {
do_action = task->tk_action; /* rpc_prepare_task */
...
do_action(task); /* nfsd4_cb_prepare */
if (!RPC_IS_QUEUED(task)) { /* true, not sleeping */
cond_resched();
continue; /* back to the top */
}
Can a callback reach this path? After nfsd4_destroy_session()
clears cl_cb_session but before cl_cb_client is torn down,
another client could trigger a delegation recall.
nfsd4_run_cb() queues the work, nfsd4_run_cb_work() sees
cl_cb_client is still set and calls rpc_call_async(), and the
task enters the prepare loop above.
Would something like the following avoid the spin?
if (!ses) {
task->tk_rpc_status = -EIO;
return false;
}
That would route the task through rpc_exit_task ->
nfsd4_cb_sequence_done (which already handles NULL session via
goto requeue) and on to nfsd4_cb_release, breaking the loop.
> cb->cb_held_slot = grab_slot(ses);
> if (cb->cb_held_slot < 0) {
> rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
[ ... ]
next prev parent reply other threads:[~2026-05-19 19:48 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-19 17:49 [PATCH] nfsd: drain backchannel callbacks before freeing a session Chris Mason
2026-05-19 17:49 ` [PATCH 1/2] nfsd: clear cl_cb_session on DESTROY_SESSION Chris Mason
2026-05-19 19:48 ` Jeff Layton [this message]
2026-05-19 17:49 ` [PATCH 2/2] nfsd: drain inflight callbacks in probe_callback_sync Chris Mason
2026-05-19 18:44 ` J. Bruce Fields
2026-05-19 20:01 ` [PATCH] nfsd: drain backchannel callbacks before freeing a session Jeff Layton
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=92813369f8e3e62960c21be4822b1b7c97b9729b.camel@kernel.org \
--to=jlayton@kernel.org \
--cc=bfields@fieldses.org \
--cc=chuck.lever@oracle.com \
--cc=clm@meta.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=trondmy@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®