* FAILED: patch "[PATCH] sunrpc: fix use-after-free in __rpc_clnt_handle_event and" failed to apply to 6.12-stable tree
@ 2026-09-03 14:07 gregkh
2026-09-08 22:37 ` [PATCH 6.12.y 1/4] rpc_populate(): lift cleanup into callers Sasha Levin
0 siblings, 1 reply; 5+ messages in thread
From: gregkh @ 2026-09-03 14:07 UTC (permalink / raw)
To: rakukuip, dstsmallbird, enjou1224z, trond.myklebust, yuantan098; +Cc: stable
The patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 932a8cf6abb2b2f8677b79153a823108d8861fe2
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026090317-curled-track-17d1@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 932a8cf6abb2b2f8677b79153a823108d8861fe2 Mon Sep 17 00:00:00 2001
From: Luxiao Xu <rakukuip@gmail.com>
Date: Tue, 7 Jul 2026 13:20:47 +0800
Subject: [PATCH] sunrpc: fix use-after-free in __rpc_clnt_handle_event and
__rpc_clnt_remove_pipedir
Normal client creation goes through rpc_setup_pipedir(), which records
clnt->pipefs_sb, but the mount-event path in __rpc_clnt_handle_event()
calls rpc_setup_pipedir_sb() directly and never refreshes that field.
The umount path also removes the directory without clearing
clnt->pipefs_sb.
After a late pipefs mount or any remount, rpc_clnt_remove_pipedir()
compares the current superblock against a stale pipefs_sb pointer and
skips cleanup, leaving pipefs dentries whose inode private data still
points at a freed rpc_clnt, leading to a potential use-after-free during
subsequent rpc_info_open() or rpc_show_info() calls.
Fix this by properly updating clnt->pipefs_sb upon mount events and
clearing it during unmount or failure paths.
Fixes: bfca5fb4e97c ("SUNRPC: Fix RPC client cleaned up the freed pipefs dentries")
Cc: stable@vger.kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Xin Liu <dstsmallbird@foxmail.com>
Reviewed-by: Ren Wei <enjou1224z@gmail.com>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Luxiao Xu <rakukuip@gmail.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index efa26899bc7d..6cedc824cf82 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -96,7 +96,10 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)
static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
{
- rpc_remove_client_dir(clnt);
+ if (clnt->pipefs_sb) {
+ rpc_remove_client_dir(clnt);
+ clnt->pipefs_sb = NULL;
+ }
}
static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
@@ -177,19 +180,28 @@ static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event)
}
static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
- struct super_block *sb)
+ struct super_block *sb)
{
+ int err = 0;
+
switch (event) {
case RPC_PIPEFS_MOUNT:
- return rpc_setup_pipedir_sb(sb, clnt);
+ clnt->pipefs_sb = sb;
+ err = rpc_setup_pipedir_sb(sb, clnt);
+ if (err)
+ clnt->pipefs_sb = NULL;
+ break;
case RPC_PIPEFS_UMOUNT:
- __rpc_clnt_remove_pipedir(clnt);
+ if (clnt->pipefs_sb == sb) {
+ __rpc_clnt_remove_pipedir(clnt);
+ clnt->pipefs_sb = NULL;
+ }
break;
default:
printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event);
return -ENOTSUPP;
}
- return 0;
+ return err;
}
static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event,
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 6.12.y 1/4] rpc_populate(): lift cleanup into callers
2026-09-03 14:07 FAILED: patch "[PATCH] sunrpc: fix use-after-free in __rpc_clnt_handle_event and" failed to apply to 6.12-stable tree gregkh
@ 2026-09-08 22:37 ` Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 2/4] rpc_mkpipe_dentry(): saner calling conventions Sasha Levin
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-08 22:37 UTC (permalink / raw)
To: stable; +Cc: Al Viro, Jeff Layton, Sasha Levin
From: Al Viro <viro@zeniv.linux.org.uk>
[ Upstream commit 8be22c49646e47452e74aa0b97ea50fb04c271ed ]
rpc_populate() is called either from fill_super (where we don't
need to remove any files on failure - rpc_kill_sb() will take
them all out anyway) or from rpc_mkdir_populate(), where we need
to remove the directory we'd been trying to populate along with
whatever we'd put into it before we failed. Simpler to combine
that into simple_recursive_removal() there.
Note that rpc_pipe is overlocking directories quite a bit -
locked parent is no obstacle to finding a child in dcache, so
keeping it locked won't prevent userland observing a partially
built subtree.
All we need is to follow minimal VFS requirements; it's not
as if clients used directory locking for exclusion - tree
changes are serialized, but that's done on ->pipefs_sb_lock.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Stable-dep-of: 932a8cf6abb2 ("sunrpc: fix use-after-free in __rpc_clnt_handle_event and __rpc_clnt_remove_pipedir")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/linux/sunrpc/rpc_pipe_fs.h | 2 +-
net/sunrpc/clnt.c | 36 ++++++++++++------------------
net/sunrpc/rpc_pipe.c | 26 ++++++++++-----------
3 files changed, 28 insertions(+), 36 deletions(-)
diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h
index 3b35b6f6533aa..f4b9ea5c83ea1 100644
--- a/include/linux/sunrpc/rpc_pipe_fs.h
+++ b/include/linux/sunrpc/rpc_pipe_fs.h
@@ -98,7 +98,7 @@ static inline bool rpc_msg_is_inflight(const struct rpc_pipe_msg *msg) {
}
struct rpc_clnt;
-extern struct dentry *rpc_create_client_dir(struct dentry *, const char *, struct rpc_clnt *);
+extern int rpc_create_client_dir(struct dentry *, const char *, struct rpc_clnt *);
extern int rpc_remove_client_dir(struct rpc_clnt *);
extern void rpc_init_pipe_dir_head(struct rpc_pipe_dir_head *pdh);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index e88e21c2ab329..1eca77bd03930 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -112,47 +112,46 @@ static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
}
}
-static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
+static int rpc_setup_pipedir_sb(struct super_block *sb,
struct rpc_clnt *clnt)
{
static uint32_t clntid;
const char *dir_name = clnt->cl_program->pipe_dir_name;
char name[15];
- struct dentry *dir, *dentry;
+ struct dentry *dir;
+ int err;
dir = rpc_d_lookup_sb(sb, dir_name);
if (dir == NULL) {
pr_info("RPC: pipefs directory doesn't exist: %s\n", dir_name);
- return dir;
+ return -ENOENT;
}
for (;;) {
snprintf(name, sizeof(name), "clnt%x", (unsigned int)clntid++);
name[sizeof(name) - 1] = '\0';
- dentry = rpc_create_client_dir(dir, name, clnt);
- if (!IS_ERR(dentry))
+ err = rpc_create_client_dir(dir, name, clnt);
+ if (!err)
break;
- if (dentry == ERR_PTR(-EEXIST))
+ if (err == -EEXIST)
continue;
printk(KERN_INFO "RPC: Couldn't create pipefs entry"
- " %s/%s, error %ld\n",
- dir_name, name, PTR_ERR(dentry));
+ " %s/%s, error %d\n",
+ dir_name, name, err);
break;
}
dput(dir);
- return dentry;
+ return err;
}
static int
rpc_setup_pipedir(struct super_block *pipefs_sb, struct rpc_clnt *clnt)
{
- struct dentry *dentry;
-
clnt->pipefs_sb = pipefs_sb;
if (clnt->cl_program->pipe_dir_name != NULL) {
- dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt);
- if (IS_ERR(dentry))
- return PTR_ERR(dentry);
+ int err = rpc_setup_pipedir_sb(pipefs_sb, clnt);
+ if (err && err != -ENOENT)
+ return err;
}
return 0;
}
@@ -180,16 +179,9 @@ static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event)
static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
struct super_block *sb)
{
- struct dentry *dentry;
-
switch (event) {
case RPC_PIPEFS_MOUNT:
- dentry = rpc_setup_pipedir_sb(sb, clnt);
- if (!dentry)
- return -ENOENT;
- if (IS_ERR(dentry))
- return PTR_ERR(dentry);
- break;
+ return rpc_setup_pipedir_sb(sb, clnt);
case RPC_PIPEFS_UMOUNT:
__rpc_clnt_remove_pipedir(clnt);
break;
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index eadc00410ebc5..43119907010e3 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -725,7 +725,6 @@ static int rpc_populate(struct dentry *parent,
inode_unlock(dir);
return 0;
out_bad:
- __rpc_depopulate(parent, files, start, eof);
inode_unlock(dir);
printk(KERN_WARNING "%s: %s failed to populate directory %pd\n",
__FILE__, __func__, parent);
@@ -749,14 +748,15 @@ static struct dentry *rpc_mkdir_populate(struct dentry *parent,
goto out_err;
if (populate != NULL) {
error = populate(dentry, args_populate);
- if (error)
- goto err_rmdir;
+ if (error) {
+ inode_unlock(dir);
+ simple_recursive_removal(dentry, NULL);
+ return ERR_PTR(error);
+ }
}
out:
inode_unlock(dir);
return dentry;
-err_rmdir:
- __rpc_rmdir(dir, dentry);
out_err:
dentry = ERR_PTR(error);
goto out;
@@ -1046,19 +1046,19 @@ static void rpc_clntdir_depopulate(struct dentry *dentry)
* information about the client, together with any "pipes" that may
* later be created using rpc_mkpipe().
*/
-struct dentry *rpc_create_client_dir(struct dentry *dentry,
- const char *name,
- struct rpc_clnt *rpc_client)
+int rpc_create_client_dir(struct dentry *dentry,
+ const char *name,
+ struct rpc_clnt *rpc_client)
{
struct dentry *ret;
ret = rpc_mkdir_populate(dentry, name, 0555, NULL,
rpc_clntdir_populate, rpc_client);
- if (!IS_ERR(ret)) {
- rpc_client->cl_pipedir_objects.pdh_dentry = ret;
- rpc_create_pipe_dir_objects(&rpc_client->cl_pipedir_objects);
- }
- return ret;
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ rpc_client->cl_pipedir_objects.pdh_dentry = ret;
+ rpc_create_pipe_dir_objects(&rpc_client->cl_pipedir_objects);
+ return 0;
}
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 6.12.y 2/4] rpc_mkpipe_dentry(): saner calling conventions
2026-09-08 22:37 ` [PATCH 6.12.y 1/4] rpc_populate(): lift cleanup into callers Sasha Levin
@ 2026-09-08 22:37 ` Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 3/4] rpc_pipe: don't overdo directory locking Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 4/4] sunrpc: fix use-after-free in __rpc_clnt_handle_event and __rpc_clnt_remove_pipedir Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-08 22:37 UTC (permalink / raw)
To: stable; +Cc: Al Viro, Jeff Layton, Sasha Levin
From: Al Viro <viro@zeniv.linux.org.uk>
[ Upstream commit 19a6314a997f6adde0c100ecf9224d1ab43c9603 ]
Instead of returning a dentry or ERR_PTR(-E...), return 0 and store
dentry into pipe->dentry on success and return -E... on failure.
Callers are happier that way...
NOTE: dummy rpc_pipe is getting ->dentry set; we never access that,
since we
1) never call rpc_unlink() for it (dentry is taken out by
->kill_sb())
2) never call rpc_queue_upcall() for it (writing to that
sucker fails; no downcalls are ever submitted, so no replies are
going to arrive)
IOW, having that ->dentry set (and left dangling) is harmless,
if ugly; cleaner solution will take more massage.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Stable-dep-of: 932a8cf6abb2 ("sunrpc: fix use-after-free in __rpc_clnt_handle_event and __rpc_clnt_remove_pipedir")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/nfs/blocklayout/rpc_pipefs.c | 41 ++++++++++++------------------
fs/nfs/nfs4idmap.c | 8 +-----
fs/nfsd/nfs4recover.c | 37 ++++++++++-----------------
include/linux/sunrpc/rpc_pipe_fs.h | 2 +-
net/sunrpc/auth_gss/auth_gss.c | 7 +----
net/sunrpc/rpc_pipe.c | 31 +++++++++++-----------
6 files changed, 49 insertions(+), 77 deletions(-)
diff --git a/fs/nfs/blocklayout/rpc_pipefs.c b/fs/nfs/blocklayout/rpc_pipefs.c
index d8d50a88de04f..fda75dfe97b79 100644
--- a/fs/nfs/blocklayout/rpc_pipefs.c
+++ b/fs/nfs/blocklayout/rpc_pipefs.c
@@ -141,17 +141,18 @@ static const struct rpc_pipe_ops bl_upcall_ops = {
.destroy_msg = bl_pipe_destroy_msg,
};
-static struct dentry *nfs4blocklayout_register_sb(struct super_block *sb,
+static int nfs4blocklayout_register_sb(struct super_block *sb,
struct rpc_pipe *pipe)
{
- struct dentry *dir, *dentry;
+ struct dentry *dir;
+ int err;
dir = rpc_d_lookup_sb(sb, NFS_PIPE_DIRNAME);
if (dir == NULL)
- return ERR_PTR(-ENOENT);
- dentry = rpc_mkpipe_dentry(dir, "blocklayout", NULL, pipe);
+ return -ENOENT;
+ err = rpc_mkpipe_dentry(dir, "blocklayout", NULL, pipe);
dput(dir);
- return dentry;
+ return err;
}
static void nfs4blocklayout_unregister_sb(struct super_block *sb,
@@ -167,7 +168,6 @@ static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,
struct super_block *sb = ptr;
struct net *net = sb->s_fs_info;
struct nfs_net *nn = net_generic(net, nfs_net_id);
- struct dentry *dentry;
int ret = 0;
if (!try_module_get(THIS_MODULE))
@@ -180,12 +180,7 @@ static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,
switch (event) {
case RPC_PIPEFS_MOUNT:
- dentry = nfs4blocklayout_register_sb(sb, nn->bl_device_pipe);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
- break;
- }
- nn->bl_device_pipe->dentry = dentry;
+ ret = nfs4blocklayout_register_sb(sb, nn->bl_device_pipe);
break;
case RPC_PIPEFS_UMOUNT:
if (nn->bl_device_pipe->dentry)
@@ -203,18 +198,17 @@ static struct notifier_block nfs4blocklayout_block = {
.notifier_call = rpc_pipefs_event,
};
-static struct dentry *nfs4blocklayout_register_net(struct net *net,
- struct rpc_pipe *pipe)
+static int nfs4blocklayout_register_net(struct net *net, struct rpc_pipe *pipe)
{
struct super_block *pipefs_sb;
- struct dentry *dentry;
+ int ret;
pipefs_sb = rpc_get_sb_net(net);
if (!pipefs_sb)
- return NULL;
- dentry = nfs4blocklayout_register_sb(pipefs_sb, pipe);
+ return 0;
+ ret = nfs4blocklayout_register_sb(pipefs_sb, pipe);
rpc_put_sb_net(net);
- return dentry;
+ return ret;
}
static void nfs4blocklayout_unregister_net(struct net *net,
@@ -232,20 +226,17 @@ static void nfs4blocklayout_unregister_net(struct net *net,
static int nfs4blocklayout_net_init(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
- struct dentry *dentry;
+ int err;
mutex_init(&nn->bl_mutex);
init_waitqueue_head(&nn->bl_wq);
nn->bl_device_pipe = rpc_mkpipe_data(&bl_upcall_ops, 0);
if (IS_ERR(nn->bl_device_pipe))
return PTR_ERR(nn->bl_device_pipe);
- dentry = nfs4blocklayout_register_net(net, nn->bl_device_pipe);
- if (IS_ERR(dentry)) {
+ err = nfs4blocklayout_register_net(net, nn->bl_device_pipe);
+ if (unlikely(err))
rpc_destroy_pipe_data(nn->bl_device_pipe);
- return PTR_ERR(dentry);
- }
- nn->bl_device_pipe->dentry = dentry;
- return 0;
+ return err;
}
static void nfs4blocklayout_net_exit(struct net *net)
diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c
index 25a7c771cfd89..86d1e91f1e3bd 100644
--- a/fs/nfs/nfs4idmap.c
+++ b/fs/nfs/nfs4idmap.c
@@ -436,14 +436,8 @@ static int nfs_idmap_pipe_create(struct dentry *dir,
struct rpc_pipe_dir_object *pdo)
{
struct idmap *idmap = pdo->pdo_data;
- struct rpc_pipe *pipe = idmap->idmap_pipe;
- struct dentry *dentry;
- dentry = rpc_mkpipe_dentry(dir, "idmap", idmap, pipe);
- if (IS_ERR(dentry))
- return PTR_ERR(dentry);
- pipe->dentry = dentry;
- return 0;
+ return rpc_mkpipe_dentry(dir, "idmap", idmap, idmap->idmap_pipe);
}
static const struct rpc_pipe_dir_object_ops nfs_idmap_pipe_dir_object_ops = {
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index a8182962a2861..5ec98a95db8f7 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -926,17 +926,18 @@ static const struct rpc_pipe_ops cld_upcall_ops = {
.destroy_msg = cld_pipe_destroy_msg,
};
-static struct dentry *
+static int
nfsd4_cld_register_sb(struct super_block *sb, struct rpc_pipe *pipe)
{
- struct dentry *dir, *dentry;
+ struct dentry *dir;
+ int err;
dir = rpc_d_lookup_sb(sb, NFSD_PIPE_DIR);
if (dir == NULL)
- return ERR_PTR(-ENOENT);
- dentry = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe);
+ return -ENOENT;
+ err = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe);
dput(dir);
- return dentry;
+ return err;
}
static void
@@ -946,18 +947,18 @@ nfsd4_cld_unregister_sb(struct rpc_pipe *pipe)
rpc_unlink(pipe->dentry);
}
-static struct dentry *
+static int
nfsd4_cld_register_net(struct net *net, struct rpc_pipe *pipe)
{
struct super_block *sb;
- struct dentry *dentry;
+ int err;
sb = rpc_get_sb_net(net);
if (!sb)
- return NULL;
- dentry = nfsd4_cld_register_sb(sb, pipe);
+ return 0;
+ err = nfsd4_cld_register_sb(sb, pipe);
rpc_put_sb_net(net);
- return dentry;
+ return err;
}
static void
@@ -977,7 +978,6 @@ static int
__nfsd4_init_cld_pipe(struct net *net)
{
int ret;
- struct dentry *dentry;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct cld_net *cn;
@@ -998,13 +998,10 @@ __nfsd4_init_cld_pipe(struct net *net)
spin_lock_init(&cn->cn_lock);
INIT_LIST_HEAD(&cn->cn_list);
- dentry = nfsd4_cld_register_net(net, cn->cn_pipe);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ ret = nfsd4_cld_register_net(net, cn->cn_pipe);
+ if (unlikely(ret))
goto err_destroy_data;
- }
- cn->cn_pipe->dentry = dentry;
#ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING
cn->cn_has_legacy = false;
#endif
@@ -2132,7 +2129,6 @@ rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr)
struct net *net = sb->s_fs_info;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct cld_net *cn = nn->cld_net;
- struct dentry *dentry;
int ret = 0;
if (!try_module_get(THIS_MODULE))
@@ -2145,12 +2141,7 @@ rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr)
switch (event) {
case RPC_PIPEFS_MOUNT:
- dentry = nfsd4_cld_register_sb(sb, cn->cn_pipe);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
- break;
- }
- cn->cn_pipe->dentry = dentry;
+ ret = nfsd4_cld_register_sb(sb, cn->cn_pipe);
break;
case RPC_PIPEFS_UMOUNT:
if (cn->cn_pipe->dentry)
diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h
index f4b9ea5c83ea1..c3bab2313e8cc 100644
--- a/include/linux/sunrpc/rpc_pipe_fs.h
+++ b/include/linux/sunrpc/rpc_pipe_fs.h
@@ -127,7 +127,7 @@ extern void rpc_remove_cache_dir(struct dentry *);
struct rpc_pipe *rpc_mkpipe_data(const struct rpc_pipe_ops *ops, int flags);
void rpc_destroy_pipe_data(struct rpc_pipe *pipe);
-extern struct dentry *rpc_mkpipe_dentry(struct dentry *, const char *, void *,
+extern int rpc_mkpipe_dentry(struct dentry *, const char *, void *,
struct rpc_pipe *);
extern int rpc_unlink(struct dentry *);
extern int register_rpc_pipefs(void);
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 759d1e03f8c31..8407a4464680e 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -902,13 +902,8 @@ static int gss_pipe_dentry_create(struct dentry *dir,
struct rpc_pipe_dir_object *pdo)
{
struct gss_pipe *p = pdo->pdo_data;
- struct dentry *dentry;
- dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe);
- if (IS_ERR(dentry))
- return PTR_ERR(dentry);
- p->pipe->dentry = dentry;
- return 0;
+ return rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe);
}
static const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = {
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 43119907010e3..5556e25ffc35a 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -799,7 +799,7 @@ static int rpc_rmdir_depopulate(struct dentry *dentry,
* The @private argument passed here will be available to all these methods
* from the file pointer, via RPC_I(file_inode(file))->private.
*/
-struct dentry *rpc_mkpipe_dentry(struct dentry *parent, const char *name,
+int rpc_mkpipe_dentry(struct dentry *parent, const char *name,
void *private, struct rpc_pipe *pipe)
{
struct dentry *dentry;
@@ -814,21 +814,19 @@ struct dentry *rpc_mkpipe_dentry(struct dentry *parent, const char *name,
inode_lock_nested(dir, I_MUTEX_PARENT);
dentry = __rpc_lookup_create_exclusive(parent, name);
- if (IS_ERR(dentry))
- goto out;
+ if (IS_ERR(dentry)) {
+ inode_unlock(dir);
+ return PTR_ERR(dentry);
+ }
err = __rpc_mkpipe_dentry(dir, dentry, umode, &rpc_pipe_fops,
private, pipe);
- if (err)
- goto out_err;
-out:
+ if (unlikely(err))
+ pr_warn("%s() failed to create pipe %pd/%s (errno = %d)\n",
+ __func__, parent, name, err);
+ else
+ pipe->dentry = dentry;
inode_unlock(dir);
- return dentry;
-out_err:
- dentry = ERR_PTR(err);
- printk(KERN_WARNING "%s: %s() failed to create pipe %pd/%s (errno = %d)\n",
- __FILE__, __func__, parent, name,
- err);
- goto out;
+ return err;
}
EXPORT_SYMBOL_GPL(rpc_mkpipe_dentry);
@@ -1326,10 +1324,13 @@ rpc_gssd_dummy_populate(struct dentry *root, struct rpc_pipe *pipe_data)
goto out;
}
- pipe_dentry = rpc_mkpipe_dentry(clnt_dentry, "gssd", NULL, pipe_data);
- if (IS_ERR(pipe_dentry)) {
+ ret = rpc_mkpipe_dentry(clnt_dentry, "gssd", NULL, pipe_data);
+ if (ret) {
__rpc_depopulate(clnt_dentry, gssd_dummy_info_file, 0, 1);
__rpc_depopulate(gssd_dentry, gssd_dummy_clnt_dir, 0, 1);
+ pipe_dentry = ERR_PTR(ret);
+ } else {
+ pipe_dentry = pipe_data->dentry;
}
out:
dput(clnt_dentry);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 6.12.y 3/4] rpc_pipe: don't overdo directory locking
2026-09-08 22:37 ` [PATCH 6.12.y 1/4] rpc_populate(): lift cleanup into callers Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 2/4] rpc_mkpipe_dentry(): saner calling conventions Sasha Levin
@ 2026-09-08 22:37 ` Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 4/4] sunrpc: fix use-after-free in __rpc_clnt_handle_event and __rpc_clnt_remove_pipedir Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-08 22:37 UTC (permalink / raw)
To: stable; +Cc: Al Viro, Jeff Layton, Sasha Levin
From: Al Viro <viro@zeniv.linux.org.uk>
[ Upstream commit 41a6b9e52b21544c334830bfe68fb78d6677caa0 ]
Don't try to hold directories locked more than VFS requires;
lock just before getting a child to be made positive (using
simple_start_creating()) and unlock as soon as the child is
created. There's no benefit in keeping the parent locked
while populating the child - it won't stop dcache lookups anyway.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Stable-dep-of: 932a8cf6abb2 ("sunrpc: fix use-after-free in __rpc_clnt_handle_event and __rpc_clnt_remove_pipedir")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/sunrpc/rpc_pipe.c | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 5556e25ffc35a..76a9d86ab8ac7 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -698,12 +698,14 @@ static int rpc_populate(struct dentry *parent,
struct dentry *dentry;
int i, err;
- inode_lock(dir);
for (i = start; i < eof; i++) {
+ inode_lock(dir);
dentry = __rpc_lookup_create_exclusive(parent, files[i].name);
err = PTR_ERR(dentry);
- if (IS_ERR(dentry))
+ if (IS_ERR(dentry)) {
+ inode_unlock(dir);
goto out_bad;
+ }
switch (files[i].mode & S_IFMT) {
default:
BUG();
@@ -712,20 +714,20 @@ static int rpc_populate(struct dentry *parent,
files[i].mode,
files[i].i_fop,
private);
+ inode_unlock(dir);
break;
case S_IFDIR:
err = __rpc_mkdir(dir, dentry,
files[i].mode,
NULL,
private);
+ inode_unlock(dir);
}
if (err != 0)
goto out_bad;
}
- inode_unlock(dir);
return 0;
out_bad:
- inode_unlock(dir);
printk(KERN_WARNING "%s: %s failed to populate directory %pd\n",
__FILE__, __func__, parent);
return err;
@@ -741,25 +743,22 @@ static struct dentry *rpc_mkdir_populate(struct dentry *parent,
inode_lock_nested(dir, I_MUTEX_PARENT);
dentry = __rpc_lookup_create_exclusive(parent, name);
- if (IS_ERR(dentry))
- goto out;
+ if (IS_ERR(dentry)) {
+ inode_unlock(dir);
+ return dentry;
+ }
error = __rpc_mkdir(dir, dentry, mode, NULL, private);
+ inode_unlock(dir);
if (error != 0)
- goto out_err;
+ return ERR_PTR(error);
if (populate != NULL) {
error = populate(dentry, args_populate);
if (error) {
- inode_unlock(dir);
simple_recursive_removal(dentry, NULL);
return ERR_PTR(error);
}
}
-out:
- inode_unlock(dir);
return dentry;
-out_err:
- dentry = ERR_PTR(error);
- goto out;
}
static int rpc_rmdir_depopulate(struct dentry *dentry,
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 6.12.y 4/4] sunrpc: fix use-after-free in __rpc_clnt_handle_event and __rpc_clnt_remove_pipedir
2026-09-08 22:37 ` [PATCH 6.12.y 1/4] rpc_populate(): lift cleanup into callers Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 2/4] rpc_mkpipe_dentry(): saner calling conventions Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 3/4] rpc_pipe: don't overdo directory locking Sasha Levin
@ 2026-09-08 22:37 ` Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-08 22:37 UTC (permalink / raw)
To: stable; +Cc: Luxiao Xu, Yuan Tan, Xin Liu, Ren Wei, Trond Myklebust,
Sasha Levin
From: Luxiao Xu <rakukuip@gmail.com>
[ Upstream commit 932a8cf6abb2b2f8677b79153a823108d8861fe2 ]
Normal client creation goes through rpc_setup_pipedir(), which records
clnt->pipefs_sb, but the mount-event path in __rpc_clnt_handle_event()
calls rpc_setup_pipedir_sb() directly and never refreshes that field.
The umount path also removes the directory without clearing
clnt->pipefs_sb.
After a late pipefs mount or any remount, rpc_clnt_remove_pipedir()
compares the current superblock against a stale pipefs_sb pointer and
skips cleanup, leaving pipefs dentries whose inode private data still
points at a freed rpc_clnt, leading to a potential use-after-free during
subsequent rpc_info_open() or rpc_show_info() calls.
Fix this by properly updating clnt->pipefs_sb upon mount events and
clearing it during unmount or failure paths.
Fixes: bfca5fb4e97c ("SUNRPC: Fix RPC client cleaned up the freed pipefs dentries")
Cc: stable@vger.kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Xin Liu <dstsmallbird@foxmail.com>
Reviewed-by: Ren Wei <enjou1224z@gmail.com>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Luxiao Xu <rakukuip@gmail.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/sunrpc/clnt.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 1eca77bd03930..f23a17ebd0391 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -96,7 +96,10 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)
static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
{
- rpc_remove_client_dir(clnt);
+ if (clnt->pipefs_sb) {
+ rpc_remove_client_dir(clnt);
+ clnt->pipefs_sb = NULL;
+ }
}
static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
@@ -177,19 +180,28 @@ static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event)
}
static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
- struct super_block *sb)
+ struct super_block *sb)
{
+ int err = 0;
+
switch (event) {
case RPC_PIPEFS_MOUNT:
- return rpc_setup_pipedir_sb(sb, clnt);
+ clnt->pipefs_sb = sb;
+ err = rpc_setup_pipedir_sb(sb, clnt);
+ if (err)
+ clnt->pipefs_sb = NULL;
+ break;
case RPC_PIPEFS_UMOUNT:
- __rpc_clnt_remove_pipedir(clnt);
+ if (clnt->pipefs_sb == sb) {
+ __rpc_clnt_remove_pipedir(clnt);
+ clnt->pipefs_sb = NULL;
+ }
break;
default:
printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event);
return -ENOTSUPP;
}
- return 0;
+ return err;
}
static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event,
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-08 22:37 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 14:07 FAILED: patch "[PATCH] sunrpc: fix use-after-free in __rpc_clnt_handle_event and" failed to apply to 6.12-stable tree gregkh
2026-09-08 22:37 ` [PATCH 6.12.y 1/4] rpc_populate(): lift cleanup into callers Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 2/4] rpc_mkpipe_dentry(): saner calling conventions Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 3/4] rpc_pipe: don't overdo directory locking Sasha Levin
2026-09-08 22:37 ` [PATCH 6.12.y 4/4] sunrpc: fix use-after-free in __rpc_clnt_handle_event and __rpc_clnt_remove_pipedir Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox