[PATCH 1/1] sunrpc: fix use-after-free in __rpc_clnt_handle_event and __rpc_clnt_remove_pipedir
Ren Wei <[email protected]>
| Newsgroups | gmane.linux.nfs,gmane.linux.network |
|---|---|
| Message-ID | <7cc1079bdd86c196d5c714c91df38dfc73aa40b6.1783393213.git.rakukuip@gmail.com> |
From: Luxiao Xu <[email protected]> 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: [email protected] Reported-by: Yuan Tan <[email protected]> Reported-by: Xin Liu <[email protected]> Reviewed-by: Ren Wei <[email protected]> Assisted-by: Codex:gpt-5.4 Signed-off-by: Luxiao Xu <[email protected]> --- 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 bc8ca470718b..85fdd8ba94a4 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.43.0