NFSv4: remove callback IDR entry on client allocation failure

nfs4_alloc_client() allocates an NFSv4.0 callback identifier before it
finishes setting up the client. If any later initialization step fails,
the error path frees the nfs_client directly with nfs_free_client(). That
bypasses nfs_put_client(), which is where the callback IDR entry is
removed during normal teardown.

A failed allocation can therefore leave cb_ident_idr pointing at a freed
nfs_client. A later NFSv4.0 callback lookup by cb_ident would find the
stale pointer and take a reference to it.

Make the callback IDR removal helper callable by the allocation failure
path, and remove the callback identifier before freeing the client.

This was found by a local static-analysis checker for publish-before-free
lifetime bugs and confirmed by manual inspection.

Fixes: f4eecd5da3 ("NFS implement v4.0 callback_ident")
Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
Ruoyu Wang 2026-06-24 11:58:58 +08:00 committed by Trond Myklebust
parent 187bfc974e
commit d05c2007b3
3 changed files with 15 additions and 1 deletions

View File

@ -215,10 +215,22 @@ static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
{
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
if (clp->cl_cb_ident)
if (clp->cl_cb_ident) {
idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident);
clp->cl_cb_ident = 0;
}
}
void nfs_cb_idr_remove(struct nfs_client *clp)
{
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
spin_lock(&nn->nfs_client_lock);
nfs_cb_idr_remove_locked(clp);
spin_unlock(&nn->nfs_client_lock);
}
EXPORT_SYMBOL_GPL(nfs_cb_idr_remove);
static void pnfs_init_server(struct nfs_server *server)
{
rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC");

View File

@ -225,6 +225,7 @@ void nfs_server_copy_userdata(struct nfs_server *, struct nfs_server *);
extern void nfs_put_client(struct nfs_client *);
extern void nfs_free_client(struct nfs_client *);
void nfs_cb_idr_remove(struct nfs_client *clp);
extern struct nfs_client *nfs4_find_client_ident(struct net *, int);
extern struct nfs_client *
nfs4_find_client_sessionid(struct net *, const struct sockaddr *,

View File

@ -261,6 +261,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
return clp;
error:
nfs_cb_idr_remove(clp);
nfs_free_client(clp);
return ERR_PTR(err);
}