5.10-stable review patch. If anyone has any objections, please let me know. ------------------ From: Ruoyu Wang [ Upstream commit d05c2007b3d84ccba11dc6e9cb3202768cc72f14 ] 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: f4eecd5da342 ("NFS implement v4.0 callback_ident") Signed-off-by: Ruoyu Wang Signed-off-by: Trond Myklebust Signed-off-by: Sasha Levin --- fs/nfs/client.c | 14 +++++++++++++- fs/nfs/internal.h | 1 + fs/nfs/nfs4client.c | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 6134101184fae..43393dddbfbe2 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -208,9 +208,21 @@ 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) { diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index fd15280e827a0..abd0ba394e42f 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -217,6 +217,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 *, diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 5fd63df2db8b1..51a1eb407c393 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -267,6 +267,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); } -- 2.53.0