[PATCH] SUNRPC: update clnt->pipefs_sb on pipefs MOUNT/UMOUNT events

Shigeru Yoshida <[email protected]>
Newsgroups org.kernel.vger.linux-nfs,org.kernel.vger.linux-kernel,org.kernel.vger.netdev
Message-ID <[email protected]>
Commit bfca5fb4e97c ("SUNRPC: Fix RPC client cleaned up the freed
pipefs dentries") added a clnt->pipefs_sb field and a consistency
check in rpc_clnt_remove_pipedir() so that pipedir cleanup is skipped
when the current superblock does not match the one recorded in the
client.

However, __rpc_clnt_handle_event() never updates clnt->pipefs_sb when
it creates or removes pipedirs in response to RPC_PIPEFS_MOUNT and
RPC_PIPEFS_UMOUNT notifications.

This causes a problem for RPC clients that are registered before
rpc_pipefs is mounted (e.g., before rpc_pipefs is mounted during boot,
or after it has been unmounted). Such clients have clnt->pipefs_sb set
to NULL at registration time because rpc_get_sb_net() returns NULL.
When rpc_pipefs is mounted later, __rpc_clnt_handle_event(MOUNT)
creates the pipedir but leaves clnt->pipefs_sb as NULL. When the
client is later destroyed, rpc_clnt_remove_pipedir() sees that the
current superblock does not match clnt->pipefs_sb and skips cleanup,
leaving orphaned dentries whose RPC_I(inode)->private pointers
reference freed rpc_clnt memory. Any subsequent access to these
orphaned info files triggers a use-after-free [1].

Fix this by setting clnt->pipefs_sb in __rpc_clnt_handle_event() on
MOUNT and clearing it on UMOUNT, mirroring what rpc_setup_pipedir()
already does at client registration time.

[1]
BUG: KASAN: slab-use-after-free in rpc_info_open (net/sunrpc/rpc_pipe.c:426)
Read of size 4 at addr ffff88802ebe0800 by task cat/5079

Call Trace:
 <TASK>
 ...
 rpc_info_open (net/sunrpc/rpc_pipe.c:426)
 do_dentry_open (fs/open.c:947)
 vfs_open (fs/open.c:1052)
 path_openat (fs/namei.c:4700 fs/namei.c:4863)
 do_file_open (fs/namei.c:4892)
 do_sys_openat2 (fs/open.c:1368)
 __x64_sys_openat (fs/open.c:1385)
 do_syscall_64 (arch/x86/entry/syscall_64.c:94)
 entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
 </TASK>

Allocated by task 5048:
 ...
 rpc_new_client (net/sunrpc/clnt.c:377)
 __rpc_clone_client (net/sunrpc/clnt.c:645)
 rpc_clone_client_set_auth (net/sunrpc/clnt.c:706)
 nfs_init_server_rpcclient (fs/nfs/client.c:640)
 nfs_clone_server (fs/nfs/client.c:1247)
 nfs4_clone_server (fs/nfs/nfs4proc.c:10683)
 nfs_do_submount (fs/nfs/namespace.c:288)
 ...

Freed by task 24:
 ...
 kfree (mm/slub.c:6692)
 rpc_free_client_work (net/sunrpc/clnt.c:987)
 process_scheduled_works (kernel/workqueue.c:3405)
 worker_thread (kernel/workqueue.c:3486)
 ...

Fixes: bfca5fb4e97c ("SUNRPC: Fix RPC client cleaned up the freed pipefs dentries")
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Shigeru Yoshida <[email protected]>
---
 net/sunrpc/clnt.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index efa26899bc7d..55617d263d2f 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -179,11 +179,17 @@ 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)
 {
+	int err;
+
 	switch (event) {
 	case RPC_PIPEFS_MOUNT:
-		return rpc_setup_pipedir_sb(sb, clnt);
+		err = rpc_setup_pipedir_sb(sb, clnt);
+		if (!err)
+			clnt->pipefs_sb = sb;
+		return err;
 	case RPC_PIPEFS_UMOUNT:
 		__rpc_clnt_remove_pipedir(clnt);
+		clnt->pipefs_sb = NULL;
 		break;
 	default:
 		printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event);
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.