From 0fd76f1be20d19ac593138ceec502cb044c909bd Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 25 Feb 2026 08:57:45 -0500 Subject: [PATCH 1/5] 9p: fix memory leak in v9fs_init_fs_context error path Move the assignments of fc->ops and fc->fs_private to right after the kzalloc, before any fallible operations. Previously these were assigned at the end of the function, after the kstrdup calls for uname and aname. If either kstrdup failed, the error path would set fc->need_free but leave fc->ops NULL, so put_fs_context() would never call v9fs_free_fc() to free the allocated context and any already-duplicated strings. Fixes: 1f3e4142c0eb ("9p: convert to the new mount API") Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Sasha Levin Message-ID: <20260225135745.351984-1-sashal@kernel.org> Signed-off-by: Dominique Martinet --- fs/9p/vfs_super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 0a1c4f7cb001..431f24938a1d 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -312,6 +312,9 @@ static int v9fs_init_fs_context(struct fs_context *fc) if (!ctx) return -ENOMEM; + fc->ops = &v9fs_context_ops; + fc->fs_private = ctx; + /* initialize core options */ ctx->session_opts.afid = ~0; ctx->session_opts.cache = CACHE_NONE; @@ -345,9 +348,6 @@ static int v9fs_init_fs_context(struct fs_context *fc) ctx->rdma_opts.timeout = P9_RDMA_TIMEOUT; ctx->rdma_opts.privport = false; - fc->ops = &v9fs_context_ops; - fc->fs_private = ctx; - return 0; error: fc->need_free = 1; From da2346a48a5a1fed86c3fe3d73c0b60e7b3027c9 Mon Sep 17 00:00:00 2001 From: Pierre Barre Date: Thu, 2 Apr 2026 12:03:12 +0200 Subject: [PATCH 2/5] 9p: fix access mode flags being ORed instead of replaced Since commit 1f3e4142c0eb ("9p: convert to the new mount API"), v9fs_apply_options() applies parsed mount flags with |= onto flags already set by v9fs_session_init(). For 9P2000.L, session_init sets V9FS_ACCESS_CLIENT as the default, so when the user mounts with "access=user", both bits end up set. Access mode checks compare against exact values, so having both bits set matches neither mode. This causes v9fs_fid_lookup() to fall through to the default switch case, using INVALID_UID (nobody/65534) instead of current_fsuid() for all fid lookups. Root is then unable to chown or perform other privileged operations. Fix by clearing the access mask before applying the user's choice. Fixes: 1f3e4142c0eb ("9p: convert to the new mount API") Signed-off-by: Pierre Barre Reviewed-by: Christian Schoenebeck Message-ID: <0ddc72da-d196-4f01-8755-0086f670e779@app.fastmail.com> Cc: stable@vger.kernel.org Signed-off-by: Dominique Martinet --- fs/9p/v9fs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 057487efaaeb..acda42499ca9 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -413,7 +413,11 @@ static void v9fs_apply_options(struct v9fs_session_info *v9ses, /* * Note that we must |= flags here as session_init already * set basic flags. This adds in flags from parsed options. + * Default access flags must be cleared if session options + * changes them to avoid mangling the setting. */ + if (ctx->session_opts.flags & V9FS_ACCESS_MASK) + v9ses->flags &= ~V9FS_ACCESS_MASK; v9ses->flags |= ctx->session_opts.flags; #ifdef CONFIG_9P_FSCACHE v9ses->cachetag = ctx->session_opts.cachetag; From 890d56964c62dfbe228b30b157811088cf64f9f1 Mon Sep 17 00:00:00 2001 From: Kit Dallege Date: Sun, 15 Mar 2026 20:06:33 +0100 Subject: [PATCH 3/5] 9p: document missing enum values in kernel-doc comments Add kernel-doc entries for all undocumented enum values: - p9_debug_flags: P9_DEBUG_CACHE, P9_DEBUG_MMAP - p9_msg_t: all 9P2000.L message types (TLOPEN/RLOPEN through TUNLINKAT/RUNLINKAT) - p9_open_mode_t: P9L_MODE_MASK, P9L_DIRECT, P9L_NOWRITECACHE, P9L_LOOSE Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Kit Dallege Reviewed-by: Christian Schoenebeck Message-ID: <20260315190633.73536-1-xaum.io@gmail.com> Signed-off-by: Dominique Martinet --- include/net/9p/9p.h | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 60cad0d200a4..fd7a034b8278 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -24,6 +24,8 @@ * @P9_DEBUG_PKT: packet marshalling/unmarshalling * @P9_DEBUG_FSC: FS-cache tracing * @P9_DEBUG_VPKT: Verbose packet debugging (full packet dump) + * @P9_DEBUG_CACHE: cache operations tracing + * @P9_DEBUG_MMAP: memory-mapped I/O tracing * * These flags are passed at mount time to turn on various levels of * verbosity and tracing which will be output to the system logs. @@ -68,13 +70,39 @@ void _p9_debug(enum p9_debug_flags level, const char *func, * @P9_RSYMLINK: make symlink response * @P9_TMKNOD: create a special file object request * @P9_RMKNOD: create a special file object response + * @P9_TLOPEN: open a file for I/O (9P2000.L) + * @P9_RLOPEN: response with qid and iounit (9P2000.L) * @P9_TLCREATE: prepare a handle for I/O on an new file for 9P2000.L * @P9_RLCREATE: response with file access information for 9P2000.L * @P9_TRENAME: rename request * @P9_RRENAME: rename response - * @P9_TMKDIR: create a directory request - * @P9_RMKDIR: create a directory response - * @P9_TVERSION: version handshake request + * @P9_TREADLINK: read symbolic link target (9P2000.L) + * @P9_RREADLINK: response with symbolic link target (9P2000.L) + * @P9_TGETATTR: get file attributes request (9P2000.L) + * @P9_RGETATTR: get file attributes response (9P2000.L) + * @P9_TSETATTR: set file attributes request (9P2000.L) + * @P9_RSETATTR: set file attributes response (9P2000.L) + * @P9_TXATTRWALK: prepare to read/list extended attributes (9P2000.L) + * @P9_RXATTRWALK: response with extended attribute size (9P2000.L) + * @P9_TXATTRCREATE: prepare to set extended attribute (9P2000.L) + * @P9_RXATTRCREATE: set extended attribute response (9P2000.L) + * @P9_TREADDIR: read directory entries request (9P2000.L) + * @P9_RREADDIR: read directory entries response (9P2000.L) + * @P9_TFSYNC: flush cached file data to storage request (9P2000.L) + * @P9_RFSYNC: flush cached file data to storage response (9P2000.L) + * @P9_TLOCK: acquire or release a POSIX record lock (9P2000.L) + * @P9_RLOCK: POSIX record lock response (9P2000.L) + * @P9_TGETLOCK: test for existence of POSIX record lock (9P2000.L) + * @P9_RGETLOCK: POSIX record lock test response (9P2000.L) + * @P9_TLINK: create a hard link (9P2000.L) + * @P9_RLINK: hard link response (9P2000.L) + * @P9_TRENAMEAT: safely rename across directories (9P2000.L) + * @P9_RRENAMEAT: rename response (9P2000.L) + * @P9_TUNLINKAT: unlink a file or directory (9P2000.L) + * @P9_RUNLINKAT: unlink response (9P2000.L) + * @P9_TMKDIR: create a directory request (9P2000.L) + * @P9_RMKDIR: create a directory response (9P2000.L) + * @P9_TVERSION: negotiate protocol version and message size * @P9_RVERSION: version handshake response * @P9_TAUTH: request to establish authentication channel * @P9_RAUTH: response with authentication information @@ -194,6 +222,10 @@ enum p9_msg_t { * @P9_ORCLOSE: remove the file when the file is closed * @P9_OAPPEND: open the file and seek to the end * @P9_OEXCL: only create a file, do not open it + * @P9L_MODE_MASK: mask for protocol mode bits (client-side only) + * @P9L_DIRECT: disable client-side caching for this file + * @P9L_NOWRITECACHE: disable write caching for this file + * @P9L_LOOSE: enable loose cache consistency * * 9P open modes differ slightly from Posix standard modes. * In particular, there are extra modes which specify different From 72cb9ee4f6d80962df17c9763b14e62e28fd85a2 Mon Sep 17 00:00:00 2001 From: Yufan Chen Date: Tue, 24 Mar 2026 23:30:22 +0800 Subject: [PATCH 4/5] 9p/trans_xen: make cleanup idempotent after dataring alloc errors xen_9pfs_front_alloc_dataring() tears down resources on failure but leaves ring fields stale. If xen_9pfs_front_init() later jumps to the common error path, xen_9pfs_front_free() may touch the same resources again, causing duplicate/invalid gnttab_end_foreign_access() calls and potentially dereferencing a freed intf pointer. Initialize dataring sentinels before allocation, gate teardown on those sentinels, and clear ref/intf/data/irq immediately after each release. This keeps cleanup idempotent for partially initialized rings and prevents repeated teardown during init failure handling. Signed-off-by: Yufan Chen Reviewed-by: Stefano Stabellini Message-ID: <20260324153023.86853-2-ericterminal@gmail.com> Signed-off-by: Dominique Martinet --- net/9p/trans_xen.c | 51 +++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c index 47af5a10e921..85b9ebfaa17a 100644 --- a/net/9p/trans_xen.c +++ b/net/9p/trans_xen.c @@ -283,25 +283,33 @@ static void xen_9pfs_front_free(struct xen_9pfs_front_priv *priv) cancel_work_sync(&ring->work); - if (!priv->rings[i].intf) + if (!ring->intf) break; - if (priv->rings[i].irq > 0) - unbind_from_irqhandler(priv->rings[i].irq, ring); - if (priv->rings[i].data.in) { - for (j = 0; - j < (1 << priv->rings[i].intf->ring_order); + if (ring->irq >= 0) { + unbind_from_irqhandler(ring->irq, ring); + ring->irq = -1; + } + if (ring->data.in) { + for (j = 0; j < (1 << ring->intf->ring_order); j++) { grant_ref_t ref; - ref = priv->rings[i].intf->ref[j]; + ref = ring->intf->ref[j]; gnttab_end_foreign_access(ref, NULL); + ring->intf->ref[j] = INVALID_GRANT_REF; } - free_pages_exact(priv->rings[i].data.in, - 1UL << (priv->rings[i].intf->ring_order + - XEN_PAGE_SHIFT)); + free_pages_exact(ring->data.in, + 1UL << (ring->intf->ring_order + + XEN_PAGE_SHIFT)); + ring->data.in = NULL; + ring->data.out = NULL; } - gnttab_end_foreign_access(priv->rings[i].ref, NULL); - free_page((unsigned long)priv->rings[i].intf); + if (ring->ref != INVALID_GRANT_REF) { + gnttab_end_foreign_access(ring->ref, NULL); + ring->ref = INVALID_GRANT_REF; + } + free_page((unsigned long)ring->intf); + ring->intf = NULL; } kfree(priv->rings); } @@ -334,6 +342,12 @@ static int xen_9pfs_front_alloc_dataring(struct xenbus_device *dev, int ret = -ENOMEM; void *bytes = NULL; + ring->intf = NULL; + ring->data.in = NULL; + ring->data.out = NULL; + ring->ref = INVALID_GRANT_REF; + ring->irq = -1; + init_waitqueue_head(&ring->wq); spin_lock_init(&ring->lock); INIT_WORK(&ring->work, p9_xen_response); @@ -379,9 +393,18 @@ static int xen_9pfs_front_alloc_dataring(struct xenbus_device *dev, for (i--; i >= 0; i--) gnttab_end_foreign_access(ring->intf->ref[i], NULL); free_pages_exact(bytes, 1UL << (order + XEN_PAGE_SHIFT)); + ring->data.in = NULL; + ring->data.out = NULL; } - gnttab_end_foreign_access(ring->ref, NULL); - free_page((unsigned long)ring->intf); + if (ring->ref != INVALID_GRANT_REF) { + gnttab_end_foreign_access(ring->ref, NULL); + ring->ref = INVALID_GRANT_REF; + } + if (ring->intf) { + free_page((unsigned long)ring->intf); + ring->intf = NULL; + } + ring->irq = -1; return ret; } From 8fc518e489c1386fd0cf7f4256d055960ed6a2e4 Mon Sep 17 00:00:00 2001 From: Yufan Chen Date: Tue, 24 Mar 2026 23:30:23 +0800 Subject: [PATCH 5/5] 9p/trans_xen: replace simple_strto* with kstrtouint In xen_9pfs_front_init(), parse the backend version list as comma-separated tokens with kstrtouint(), keep strict token validation, and explicitly require protocol version 1 to be present. This replaces the deprecated simple_strtoul(), improves error reporting consistency, and avoids partially parsed values in control paths. Signed-off-by: Yufan Chen Reviewed-by: Stefano Stabellini Message-ID: <20260324153023.86853-3-ericterminal@gmail.com> Signed-off-by: Dominique Martinet --- net/9p/trans_xen.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c index 85b9ebfaa17a..f9fb2db7a066 100644 --- a/net/9p/trans_xen.c +++ b/net/9p/trans_xen.c @@ -413,23 +413,29 @@ static int xen_9pfs_front_init(struct xenbus_device *dev) int ret, i; struct xenbus_transaction xbt; struct xen_9pfs_front_priv *priv; - char *versions, *v; - unsigned int max_rings, max_ring_order, len = 0; + char *versions, *v, *token; + bool version_1 = false; + unsigned int max_rings, max_ring_order, len = 0, version; versions = xenbus_read(XBT_NIL, dev->otherend, "versions", &len); if (IS_ERR(versions)) return PTR_ERR(versions); - for (v = versions; *v; v++) { - if (simple_strtoul(v, &v, 10) == 1) { - v = NULL; - break; + for (v = versions; (token = strsep(&v, ",")); ) { + if (!*token) + continue; + + ret = kstrtouint(token, 10, &version); + if (ret) { + kfree(versions); + return ret; } - } - if (v) { - kfree(versions); - return -EINVAL; + if (version == 1) + version_1 = true; } kfree(versions); + if (!version_1) + return -EINVAL; + max_rings = xenbus_read_unsigned(dev->otherend, "max-rings", 0); if (max_rings < XEN_9PFS_NUM_RINGS) return -EINVAL;