6.6-stable review patch. If anyone has any objections, please let me know. ------------------ From: Luxiao Xu [ 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 Reported-by: Xin Liu Reviewed-by: Ren Wei Assisted-by: Codex:gpt-5.4 Signed-off-by: Luxiao Xu Signed-off-by: Trond Myklebust Signed-off-by: Sasha Levin --- net/sunrpc/clnt.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 85cfd7b7c212a..29b73519220fe 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -101,7 +101,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) @@ -183,26 +186,33 @@ 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) { struct dentry *dentry; + int err = 0; switch (event) { case RPC_PIPEFS_MOUNT: + clnt->pipefs_sb = sb; dentry = rpc_setup_pipedir_sb(sb, clnt); if (!dentry) - return -ENOENT; - if (IS_ERR(dentry)) - return PTR_ERR(dentry); + err = -ENOENT; + else if (IS_ERR(dentry)) + err = PTR_ERR(dentry); + 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