From: Jeff Layton <jlayton@kernel.org>
To: Li Lingfeng <lilingfeng3@huawei.com>,
chuck.lever@oracle.com, neilb@suse.de, okorniev@redhat.com,
Dai.Ngo@oracle.com, tom@talpey.com, linux-nfs@vger.kernel.org,
linux-kernel@vger.kernel.org
Cc: yukuai1@huaweicloud.com, houtao1@huawei.com, yi.zhang@huawei.com,
yangerkun@huawei.com, lilingfeng@huaweicloud.com
Subject: Re: [PATCH v2] nfsd: Invoke tracking callbacks only after initialization is complete
Date: Tue, 17 Jun 2025 16:49:20 -0400 [thread overview]
Message-ID: <69593538dafb344d2a597e2f9c80cc3394948fe6.camel@kernel.org> (raw)
In-Reply-To: <20250612035506.3651985-1-lilingfeng3@huawei.com>
On Thu, 2025-06-12 at 11:55 +0800, Li Lingfeng wrote:
> Checking whether tracking callbacks can be called based on whether
> nn->client_tracking_ops is NULL may lead to callbacks being invoked
> before tracking initialization completes, causing resource access
> violations (UAF, NULL pointer dereference). Examples:
>
> 1) nfsd4_client_tracking_init
> // set nn->client_tracking_ops
> nfsd4_cld_tracking_init
> nfs4_cld_state_init
> nn->reclaim_str_hashtbl = kmalloc_array
> ... // error path, goto err
> nfs4_cld_state_shutdown
> kfree(nn->reclaim_str_hashtbl)
> write_v4_end_grace
> nfsd4_end_grace
> nfsd4_record_grace_done
> nfsd4_cld_grace_done
> nfs4_release_reclaim
> nn->reclaim_str_hashtbl[i]
> // UAF
> // clear nn->client_tracking_ops
>
> 2) nfsd4_client_tracking_init
> // set nn->client_tracking_ops
> nfsd4_cld_tracking_init
> write_v4_end_grace
> nfsd4_end_grace
> nfsd4_record_grace_done
> nfsd4_cld_grace_done
> alloc_cld_upcall
> cn = nn->cld_net
> spin_lock // cn->cn_lock
> // NULL deref
> // error path, skip init pipe
> __nfsd4_init_cld_pipe
> cn = kzalloc
> nn->cld_net = cn
> // clear nn->client_tracking_ops
>
> After nfsd mounts, users can trigger grace_done callbacks via
> /proc/fs/nfsd/v4_end_grace. If resources are uninitialized or freed
> in error paths, this causes access violations.
>
> Resolve the issue by leveraging nfsd_mutex to prevent concurrency.
>
> Fixes: 52e19c09a183 ("nfsd: make reclaim_str_hashtbl allocated per net")
> Signed-off-by: Li Lingfeng <lilingfeng3@huawei.com>
> ---
> Changes in v2:
> Use nfsd_mutex instead of adding a new flag to prevent concurrency.
> fs/nfsd/nfs4recover.c | 8 ++++++++
> fs/nfsd/nfs4state.c | 4 ++++
> fs/nfsd/nfsctl.c | 2 ++
> 3 files changed, 14 insertions(+)
>
> diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
> index 82785db730d9..8ac089f8134c 100644
> --- a/fs/nfsd/nfs4recover.c
> +++ b/fs/nfsd/nfs4recover.c
> @@ -162,7 +162,9 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error)
> if (error == -ENOENT) {
> printk(KERN_ERR "NFSD: disabling legacy clientid tracking. "
> "Reboot recovery will not function correctly!\n");
> + mutex_lock(&nfsd_mutex);
> nfsd4_client_tracking_exit(clp->net);
> + mutex_unlock(&nfsd_mutex);
> }
> }
>
> @@ -2083,8 +2085,10 @@ nfsd4_client_record_create(struct nfs4_client *clp)
> {
> struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
>
> + mutex_lock(&nfsd_mutex);
> if (nn->client_tracking_ops)
> nn->client_tracking_ops->create(clp);
> + mutex_unlock(&nfsd_mutex);
> }
>
> void
> @@ -2092,8 +2096,10 @@ nfsd4_client_record_remove(struct nfs4_client *clp)
> {
> struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
>
> + mutex_lock(&nfsd_mutex);
> if (nn->client_tracking_ops)
> nn->client_tracking_ops->remove(clp);
> + mutex_unlock(&nfsd_mutex);
> }
>
> int
> @@ -2101,8 +2107,10 @@ nfsd4_client_record_check(struct nfs4_client *clp)
> {
> struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
>
> + mutex_lock(&nfsd_mutex);
> if (nn->client_tracking_ops)
> return nn->client_tracking_ops->check(clp);
> + mutex_unlock(&nfsd_mutex);
>
> return -EOPNOTSUPP;
> }
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index d5694987f86f..2794fdc8b678 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -2529,7 +2529,9 @@ static void inc_reclaim_complete(struct nfs4_client *clp)
> nn->reclaim_str_hashtbl_size) {
> printk(KERN_INFO "NFSD: all clients done reclaiming, ending NFSv4 grace period (net %x)\n",
> clp->net->ns.inum);
> + mutex_lock(&nfsd_mutex);
> nfsd4_end_grace(nn);
> + mutex_unlock(&nfsd_mutex);
> }
> }
>
> @@ -6773,7 +6775,9 @@ nfs4_laundromat(struct nfsd_net *nn)
> lt.new_timeo = 0;
> goto out;
> }
> + mutex_lock(&nfsd_mutex);
> nfsd4_end_grace(nn);
> + mutex_unlock(&nfsd_mutex);
>
> spin_lock(&nn->s2s_cp_lock);
> idr_for_each_entry(&nn->s2s_cp_stateids, cps_t, i) {
> diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> index 3f3e9f6c4250..649850b4bb60 100644
> --- a/fs/nfsd/nfsctl.c
> +++ b/fs/nfsd/nfsctl.c
> @@ -1085,7 +1085,9 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
> if (!nn->nfsd_serv)
> return -EBUSY;
> trace_nfsd_end_grace(netns(file));
> + mutex_lock(&nfsd_mutex);
> nfsd4_end_grace(nn);
> + mutex_lock(&nfsd_mutex);
> break;
> default:
> return -EINVAL;
This seems like a very heavyweight way to ensure that this doesn't
race, especially since the client tracking ops are per net-namespace
and the nfsd_mutex is global.
Also, how do you get two different tasks calling
nfsd4_client_tracking_init() at the same time? That's called when the
net namespace is set up, so there shouldn't be more than one copy
running.
I thought from an earlier patch that we were going to change this to
just ensure that the client_tracking_ops pointer did a NULL -> non-NULL
transition only once?
I think that's sufficient to ensure that this race can't occur.
--
Jeff Layton <jlayton@kernel.org>
next prev parent reply other threads:[~2025-06-17 20:49 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-12 3:55 [PATCH v2] nfsd: Invoke tracking callbacks only after initialization is complete Li Lingfeng
2025-06-17 20:49 ` Jeff Layton [this message]
2025-06-18 12:35 ` Jeff Layton
2025-06-18 12:50 ` Jeff Layton
2025-06-18 20:45 ` NeilBrown
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=69593538dafb344d2a597e2f9c80cc3394948fe6.camel@kernel.org \
--to=jlayton@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=chuck.lever@oracle.com \
--cc=houtao1@huawei.com \
--cc=lilingfeng3@huawei.com \
--cc=lilingfeng@huaweicloud.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neilb@suse.de \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
--cc=yangerkun@huawei.com \
--cc=yi.zhang@huawei.com \
--cc=yukuai1@huaweicloud.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.