* [PATCH 1/3] fuse: reject embedded NULs in readdir entry names
2026-11-15 21:09 [PATCH 0/3] fuse, fsnotify: handle embedded NUL names safely Junyoung Jang
@ 2026-11-15 21:09 ` Junyoung Jang
2026-11-15 21:09 ` [PATCH 2/3] inotify: zero-pad event names after an embedded NUL Junyoung Jang
2026-11-15 21:09 ` [PATCH 3/3] fanotify: zero-pad copied " Junyoung Jang
2 siblings, 0 replies; 4+ messages in thread
From: Junyoung Jang @ 2026-11-15 21:09 UTC (permalink / raw)
To: Miklos Szeredi, Jan Kara
Cc: Junyoung Jang, Amir Goldstein, Matt Bobrowski, Gwanhyun Lee,
fuse-devel, linux-fsdevel, linux-kernel, stable
parse_dirfile() and parse_dirplusfile() reject '/' in dirent->name, but
do not check for embedded NUL bytes.
Unlike plain getdents() buffers covered by verify_dirent_name() in
fs/readdir.c, FUSE READDIRPLUS passes dirent->namelen directly to
QSTR_INIT() in fuse_direntplus_link() and instantiates or aliases live
dentries via d_splice_alias(). A dirent name containing an embedded NUL
produces a dentry whose d_name.len extends past the NUL terminator,
which allows callers such as inotify to copy uninitialized kernel slab
memory past the NUL byte to userspace.
Reject dirent names containing NUL alongside '/' in the FUSE readdir
parsers.
Fixes: 0b05b18381ee ("fuse: implement NFS-like readdirplus support")
Cc: stable@vger.kernel.org
Co-developed-by: Gwanhyun Lee <gwanhyunlee@gmail.com>
Signed-off-by: Gwanhyun Lee <gwanhyunlee@gmail.com>
Signed-off-by: Junyoung Jang <graypanda.inzag@gmail.com>
---
fs/fuse/readdir.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
index 1f6cf565477f..e8b47c2a9103 100644
--- a/fs/fuse/readdir.c
+++ b/fs/fuse/readdir.c
@@ -126,6 +126,12 @@ static bool fuse_emit(struct file *file, struct dir_context *ctx,
dirent->type | FILLDIR_FLAG_NOINTR);
}
+static bool fuse_invalid_dirent_name(const struct fuse_dirent *dirent)
+{
+ return memchr(dirent->name, '/', dirent->namelen) ||
+ memchr(dirent->name, '\0', dirent->namelen);
+}
+
static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
struct dir_context *ctx)
{
@@ -136,7 +142,7 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
return -EIO;
if (reclen > nbytes)
break;
- if (memchr(dirent->name, '/', dirent->namelen) != NULL)
+ if (fuse_invalid_dirent_name(dirent))
return -EIO;
if (!fuse_emit(file, ctx, dirent))
@@ -307,7 +313,7 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
return -EIO;
if (reclen > nbytes)
break;
- if (memchr(dirent->name, '/', dirent->namelen) != NULL)
+ if (fuse_invalid_dirent_name(dirent))
return -EIO;
if (!over) {
@@ -461,7 +467,7 @@ static enum fuse_parse_result fuse_parse_cache(struct fuse_file *ff,
return FOUND_ERR;
if (WARN_ON(reclen > nbytes))
return FOUND_ERR;
- if (WARN_ON(memchr(dirent->name, '/', dirent->namelen) != NULL))
+ if (WARN_ON(fuse_invalid_dirent_name(dirent)))
return FOUND_ERR;
if (ff->readdir.pos == ctx->pos) {
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 2/3] inotify: zero-pad event names after an embedded NUL
2026-11-15 21:09 [PATCH 0/3] fuse, fsnotify: handle embedded NUL names safely Junyoung Jang
2026-11-15 21:09 ` [PATCH 1/3] fuse: reject embedded NULs in readdir entry names Junyoung Jang
@ 2026-11-15 21:09 ` Junyoung Jang
2026-11-15 21:09 ` [PATCH 3/3] fanotify: zero-pad copied " Junyoung Jang
2 siblings, 0 replies; 4+ messages in thread
From: Junyoung Jang @ 2026-11-15 21:09 UTC (permalink / raw)
To: Miklos Szeredi, Jan Kara
Cc: Junyoung Jang, Amir Goldstein, Matt Bobrowski, Gwanhyun Lee,
fuse-devel, linux-fsdevel, linux-kernel, stable
inotify_handle_inode_event() allocates the variable-length event with
kmalloc() without zeroing the name buffer, sets event->name_len to
name->len, and copies name->name with strscpy().
If a dentry qstr contains an embedded NUL byte (for example, via a
crafted FUSE READDIRPLUS entry aliasing an open directory), strscpy()
stops at the first NUL while event->name_len still covers the trailing
bytes. copy_event_to_user() then copies event->name_len bytes to
userspace, leaking uninitialized slab memory from the tail of the
event allocation.
This mismatch dates back to
commit ce163918cd33 ("inotify_handle_event(): don't bother with strlen()"),
which replaced strlen(file_name->name) with file_name->len, and remained
in commit b8f2688258f8 ("inotify: Use strscpy() for event->name copies").
Switch to strscpy_pad() so any trailing bytes up to event->name_len + 1
are zeroed.
Fixes: ce163918cd33 ("inotify_handle_event(): don't bother with strlen()")
Cc: stable@vger.kernel.org
Co-developed-by: Gwanhyun Lee <gwanhyunlee@gmail.com>
Signed-off-by: Gwanhyun Lee <gwanhyunlee@gmail.com>
Signed-off-by: Junyoung Jang <graypanda.inzag@gmail.com>
---
fs/notify/inotify/inotify_fsnotify.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c
index 6e3af6452dcc..78eb1e92a14c 100644
--- a/fs/notify/inotify/inotify_fsnotify.c
+++ b/fs/notify/inotify/inotify_fsnotify.c
@@ -121,7 +121,7 @@ int inotify_handle_inode_event(struct fsnotify_mark *inode_mark, u32 mask,
event->sync_cookie = cookie;
event->name_len = len;
if (len)
- strscpy(event->name, name->name, event->name_len + 1);
+ strscpy_pad(event->name, name->name, event->name_len + 1);
ret = fsnotify_add_event(group, fsn_event, inotify_merge);
if (ret) {
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 3/3] fanotify: zero-pad copied event names after an embedded NUL
2026-11-15 21:09 [PATCH 0/3] fuse, fsnotify: handle embedded NUL names safely Junyoung Jang
2026-11-15 21:09 ` [PATCH 1/3] fuse: reject embedded NULs in readdir entry names Junyoung Jang
2026-11-15 21:09 ` [PATCH 2/3] inotify: zero-pad event names after an embedded NUL Junyoung Jang
@ 2026-11-15 21:09 ` Junyoung Jang
2 siblings, 0 replies; 4+ messages in thread
From: Junyoung Jang @ 2026-11-15 21:09 UTC (permalink / raw)
To: Miklos Szeredi, Jan Kara
Cc: Junyoung Jang, Amir Goldstein, Matt Bobrowski, Gwanhyun Lee,
fuse-devel, linux-fsdevel, linux-kernel, stable
fanotify_alloc_name_event() sizes the variable-length event buffer from
the qstr lengths and allocates it via kvmalloc() without zeroing.
fanotify_info_copy_name() and fanotify_info_copy_name2() record
name->len in info->name_len and info->name2_len, but copy the strings
with strscpy().
If a qstr contains an embedded NUL byte, strscpy() stops at the first
NUL and leaves the trailing bytes of the allocated name buffer
uninitialized. copy_fid_info_to_user() then copies name_len + 1 bytes
to userspace, leaking uninitialized kernel heap memory.
This mismatch dates back to
commit 929943b38daf ("fanotify: add support for FAN_REPORT_NAME") and
remained after commit 0fdbe845534f
("fanotify: replace deprecated strcpy in fanotify_info_copy_{name,name2}").
Switch both helpers to strscpy_pad() so any trailing bytes up to the
recorded length are zeroed.
Fixes: 929943b38daf ("fanotify: add support for FAN_REPORT_NAME")
Cc: stable@vger.kernel.org
Co-developed-by: Gwanhyun Lee <gwanhyunlee@gmail.com>
Signed-off-by: Gwanhyun Lee <gwanhyunlee@gmail.com>
Signed-off-by: Junyoung Jang <graypanda.inzag@gmail.com>
---
Note: default FUSE mounts report a zero fsid (-ENODEV on fanotify_mark()
with FAN_REPORT_DFID_NAME), so this patch was runtime-tested on tmpfs
with FAN_REPORT_DFID_NAME create and FAN_RENAME events.
fs/notify/fanotify/fanotify.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index c25cf5c1fc70..4a1f14aa4974 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -225,14 +225,14 @@ static inline void fanotify_info_copy_name(struct fanotify_info *info,
return;
info->name_len = name->len;
- strscpy(fanotify_info_name(info), name->name, name->len + 1);
+ strscpy_pad(fanotify_info_name(info), name->name, name->len + 1);
}
static inline void fanotify_info_copy_name2(struct fanotify_info *info,
const struct qstr *name)
{
info->name2_len = name->len;
- strscpy(fanotify_info_name2(info), name->name, name->len + 1);
+ strscpy_pad(fanotify_info_name2(info), name->name, name->len + 1);
}
/*
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread