* [RFC PATCH v4 0/2] vfs: add O_CREAT|O_DIRECTORY to open*(2)
@ 2026-05-18 16:52 Jori Koolstra
2026-05-18 16:52 ` [RFC PATCH v4 1/2] " Jori Koolstra
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Jori Koolstra @ 2026-05-18 16:52 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Aleksa Sarai
Cc: Jori Koolstra, linux-kernel, linux-fsdevel, cmirabil,
Jori Koolstra
This series implements new semantics for the O_CREAT|O_DIRECTORY flag
combination for open*(2): perform a mkdir and open the resulting
directory; return a pinning fd (which mkdir does not).
Feedback by Christian Brauner and Aleksa Sarai on the v2 rfc of this
patch was to not introduce a new syscall (mkdirat2) but implement this
functionality as O_CREAT|O_DIRECTORY in open*(2). I had some very silly
bugs that syzbot alerted me of in v3, so here is v4...
Three comments from me upfront:
- This patch just EINVAL bans O_CREAT|O_DIRECTORY for filesystems that
define atomic_open(). I figure it is better to (dis)allow on a fs per
fs basis. So feedback per filesystem on what is the appropriate course
of action on receiving O_CREAT|O_DIRECTORY would be very welcome.
- If we create a regular file with mknod, before creation
security_path_mknod() is called, and after creation
security_path_post_mknod(). If we create a regular file using O_CREAT
(and this is also pre-patch) only security_path_mknod() is called. Is
this the correct behaviour?
- open_last_lookups() locks the parent inode like like:
inode_lock(dir->d_inode);
should this perhaps be
inode_lock_nested(dir, I_MUTEX_PARENT);
to stay consistent with the start_dirop() path that is used by
filename_create() for instance in mknod(2)? I get that we are only
locking one inode here at most, so it does not really matter, but
now one regular file create path does set the lockdep and the other
does not.
Jori Koolstra (2):
vfs: add O_CREAT|O_DIRECTORY to open*(2)
selftest: add tests for open*(O_CREAT|O_DIRECTORY)
fs/namei.c | 180 +++++++++++-----
fs/open.c | 25 ++-
include/uapi/asm-generic/fcntl.h | 2 +
.../testing/selftests/filesystems/.gitignore | 1 +
tools/testing/selftests/filesystems/Makefile | 4 +-
tools/testing/selftests/filesystems/fclog.c | 1 +
.../filesystems/open_o_creat_o_dir.c | 200 ++++++++++++++++++
7 files changed, 342 insertions(+), 71 deletions(-)
create mode 100644 tools/testing/selftests/filesystems/open_o_creat_o_dir.c
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [RFC PATCH v4 1/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) 2026-05-18 16:52 [RFC PATCH v4 0/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) Jori Koolstra @ 2026-05-18 16:52 ` Jori Koolstra 2026-05-18 16:52 ` [RFC PATCH v4 2/2] selftest: add tests for open*(O_CREAT|O_DIRECTORY) Jori Koolstra 2026-05-19 6:59 ` [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) syzbot ci 2 siblings, 0 replies; 9+ messages in thread From: Jori Koolstra @ 2026-05-18 16:52 UTC (permalink / raw) To: Alexander Viro, Christian Brauner, Jan Kara, Aleksa Sarai Cc: Jori Koolstra, linux-kernel, linux-fsdevel, cmirabil, Jori Koolstra Currently there is no way to race-freely create and open a directory. For regular files we have open(O_CREAT) for creating a new file inode, and returning a pinning fd to it. The lack of such functionality for directories means that when populating a directory tree there's always a race involved: the inodes first need to be created, and then opened to adjust their permissions/ownership/labels/timestamps/acls/xattrs/..., but in the time window between the creation and the opening they might be replaced by something else. Addressing this race without proper APIs is possible (by immediately fstat()ing what was opened, to verify that it has the right inode type), but difficult to get right. Hence, adding support for a new flag combo O_CREAT|O_DIRECTORY to open*(2) that creates a directory (if it does not exist already) and returns an O_DIRECTORY fd is very useful. Historically, the O_CREAT|O_DIRECTORY behaviour was to return ENOTDIR if a regular file exists at the open path; EISDIR if a directory exists at the path; and to create a regular file if no file exists at the path. This behaviour changed accidentally with 973d4b73fbaf ("do_last(): rejoin the common path even earlier in FMODE_{OPENED,CREATED} case") causing ENOTDIR to return in the last case while still creating the file. As this change was not detected for a long time, Brauner proposed to adopt the more consistent NetBSD behaviour, i.e. to return EINVAL on the the O_CREAT|O_DIRECTORY combination. This change was applied in 43b450632676 ("open: return EINVAL for O_DIRECTORY | O_CREAT") in March, 2023. As the EINVAL behaviour has been in the kernel for about 3 year now, no rollback is expected as a result of userspace reliance on old behaviour, leaving us free to reassign the O_CREAT|O_DIRECTORY semantics. This commit also changes the error returned when a filesystem operation is unsupported (i_op->mkdir/creat) to EOPNOTSUPP. Current error values are inconsistent (both EPERM and EACCES are used) and confusing. This feature idea (and some of its description) is taken from the UAPI group: https://github.com/uapi-group/kernel-features?tab=readme-ov-file#race-free-creation-and-opening-of-non-file-inodes Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl> --- fs/namei.c | 180 +++++++++++++++++++++---------- fs/open.c | 25 +++-- include/uapi/asm-generic/fcntl.h | 2 + 3 files changed, 138 insertions(+), 69 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index c7fac83c9a85..60223278f9ec 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2777,9 +2777,14 @@ static const char *path_init(struct nameidata *nd, unsigned flags) return s; } +static inline bool trailing_slashes(struct nameidata *nd) +{ + return (bool)nd->last.name[nd->last.len]; +} + static inline const char *lookup_last(struct nameidata *nd) { - if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) + if (nd->last_type == LAST_NORM && trailing_slashes(nd)) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; return walk_component(nd, WALK_TRAILING); @@ -4166,6 +4171,16 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap, return mode; } +static int __vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode, + struct delegated_inode *di, bool excl) +{ + struct inode *dir = d_inode(dentry->d_parent); + int error = try_break_deleg(dir, di); + if (error) + return error; + return dir->i_op->create(idmap, dir, dentry, mode, excl); +} + /** * vfs_create - create new file * @idmap: idmap of the mount the inode was found from @@ -4192,16 +4207,14 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode, return error; if (!dir->i_op->create) - return -EACCES; /* shouldn't it be ENOSYS? */ + return -EOPNOTSUPP; mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG); error = security_inode_create(dir, dentry, mode); if (error) return error; - error = try_break_deleg(dir, di); - if (error) - return error; - error = dir->i_op->create(idmap, dir, dentry, mode, true); + + error = __vfs_create(idmap, dentry, mode, di, true); if (!error) fsnotify_create(dir, dentry); return error; @@ -4321,21 +4334,32 @@ static inline int open_to_namei_flags(int flag) static int may_o_create(struct mnt_idmap *idmap, const struct path *dir, struct dentry *dentry, - umode_t mode) + umode_t mode, bool create_dir) { - int error = security_path_mknod(dir, dentry, mode, 0); + struct inode *dir_inode = dir->dentry->d_inode; + int error; + + error = create_dir ? security_path_mkdir(dir, dentry, mode) + : security_path_mknod(dir, dentry, mode, 0); if (error) return error; if (!fsuidgid_has_mapping(dir->dentry->d_sb, idmap)) return -EOVERFLOW; - error = inode_permission(idmap, dir->dentry->d_inode, - MAY_WRITE | MAY_EXEC); + error = inode_permission(idmap, dir_inode, MAY_WRITE | MAY_EXEC); if (error) return error; - return security_inode_create(dir->dentry->d_inode, dentry, mode); + return create_dir ? security_inode_mkdir(dir_inode, dentry, mode) + : security_inode_create(dir_inode, dentry, mode); +} + +static inline umode_t o_create_mode(struct mnt_idmap *idmap, + const struct inode *dir, umode_t mode, bool create_dir) +{ + return create_dir ? vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, 0) + : vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG); } /* @@ -4359,6 +4383,11 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry struct inode *dir = path->dentry->d_inode; int error; + if ((open_flag & O_MKDIR_MASK) == O_MKDIR_MASK) { + error = -EOPNOTSUPP; + goto out; + } + file->__f_path.dentry = DENTRY_NOT_SET; file->__f_path.mnt = path->mnt; error = dir->i_op->atomic_open(dir, dentry, file, @@ -4381,6 +4410,7 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry error = -ENOENT; } } +out: if (error) { dput(dentry); dentry = ERR_PTR(error); @@ -4388,6 +4418,9 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry return dentry; } +static struct dentry *__vfs_mkdir(struct mnt_idmap *, struct inode *, + struct dentry *, umode_t, + struct delegated_inode *); /* * Look up and maybe create and open the last component. * @@ -4412,8 +4445,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, struct inode *dir_inode = dir->d_inode; int open_flag = op->open_flag; struct dentry *dentry; - int error, create_error = 0; + int error = 0, create_error = 0; umode_t mode = op->mode; + bool create_dir = (open_flag & O_MKDIR_MASK) == O_MKDIR_MASK; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); if (unlikely(IS_DEADDIR(dir_inode))) @@ -4462,10 +4496,10 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, if (open_flag & O_CREAT) { if (open_flag & O_EXCL) open_flag &= ~O_TRUNC; - mode = vfs_prepare_mode(idmap, dir->d_inode, mode, mode, mode); + mode = o_create_mode(idmap, dir_inode, mode, create_dir); if (likely(got_write)) create_error = may_o_create(idmap, &nd->path, - dentry, mode); + dentry, mode, create_dir); else create_error = -EROFS; } @@ -4494,29 +4528,37 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, } } + if (unlikely(create_error) && !dentry->d_inode) { + error = create_error; + goto out_dput; + } + /* Negative dentry, just create the file */ if (!dentry->d_inode && (open_flag & O_CREAT)) { - /* but break the directory lease first! */ - error = try_break_deleg(dir_inode, delegated_inode); - if (error) - goto out_dput; file->f_mode |= FMODE_CREATED; audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE); - if (!dir_inode->i_op->create) { - error = -EACCES; + if ((create_dir && !dir_inode->i_op->mkdir) + || (!create_dir && !dir_inode->i_op->create)) { + error = -EOPNOTSUPP; goto out_dput; } - error = dir_inode->i_op->create(idmap, dir_inode, dentry, - mode, open_flag & O_EXCL); + if (create_dir) { + struct dentry *res = __vfs_mkdir(idmap, dir_inode, dentry, mode, + delegated_inode); + if (IS_ERR(res)) + error = PTR_ERR(res); + else + dentry = res; + } else { + error = __vfs_create(idmap, dentry, mode, delegated_inode, + open_flag & O_EXCL); + } if (error) goto out_dput; } - if (unlikely(create_error) && !dentry->d_inode) { - error = create_error; - goto out_dput; - } + return dentry; out_dput: @@ -4524,17 +4566,12 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, return ERR_PTR(error); } -static inline bool trailing_slashes(struct nameidata *nd) -{ - return (bool)nd->last.name[nd->last.len]; -} - static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_flag) { struct dentry *dentry; if (open_flag & O_CREAT) { - if (trailing_slashes(nd)) + if (trailing_slashes(nd) && !(open_flag & O_DIRECTORY)) return ERR_PTR(-EISDIR); /* Don't bother on an O_EXCL create */ @@ -4610,8 +4647,12 @@ static const char *open_last_lookups(struct nameidata *nd, inode_lock_shared(dir->d_inode); dentry = lookup_open(nd, file, op, got_write, &delegated_inode); if (!IS_ERR(dentry)) { - if (file->f_mode & FMODE_CREATED) - fsnotify_create(dir->d_inode, dentry); + if (file->f_mode & FMODE_CREATED) { + if (open_flag & O_DIRECTORY) + fsnotify_mkdir(dir->d_inode, dentry); + else + fsnotify_create(dir->d_inode, dentry); + } if (file->f_mode & FMODE_OPENED) fsnotify_open(file); } @@ -4672,12 +4713,15 @@ static int do_open(struct nameidata *nd, if (open_flag & O_CREAT) { if ((open_flag & O_EXCL) && !(file->f_mode & FMODE_CREATED)) return -EEXIST; - if (d_is_dir(nd->path.dentry)) - return -EISDIR; - error = may_create_in_sticky(idmap, nd, - d_backing_inode(nd->path.dentry)); - if (unlikely(error)) - return error; + if (!(open_flag & O_DIRECTORY)) { + if (d_is_dir(nd->path.dentry)) + return -EISDIR; + + error = may_create_in_sticky(idmap, nd, + d_backing_inode(nd->path.dentry)); + if (unlikely(error)) + return error; + } } if ((nd->flags & LOOKUP_DIRECTORY) && !d_can_lookup(nd->path.dentry)) return -ENOTDIR; @@ -5039,7 +5083,7 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode, path->dentry = dir; mode = vfs_prepare_mode(idmap, dir_inode, mode, S_IALLUGO, S_IFREG); - create_error = may_o_create(idmap, path, dentry, mode); + create_error = may_o_create(idmap, path, dentry, mode, false); if (create_error) flags &= ~O_CREAT; @@ -5207,6 +5251,37 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d return filename_mknodat(AT_FDCWD, name, mode, dev); } +static struct dentry *__vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, + struct delegated_inode *di) +{ + int error; + unsigned max_links = dir->i_sb->s_max_links; + struct dentry *de; + + error = -EMLINK; + if (max_links && dir->i_nlink >= max_links) + goto err; + + error = try_break_deleg(dir, di); + if (error) + goto err; + + de = dir->i_op->mkdir(idmap, dir, dentry, mode); + if (IS_ERR(de)) { + error = PTR_ERR(de); + goto err; + } + if (de) { + dput(dentry); + dentry = de; + } + return dentry; + +err: + return ERR_PTR(error); +} + /** * vfs_mkdir - create directory returning correct dentry if possible * @idmap: idmap of the mount the inode was found from @@ -5231,17 +5306,16 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d */ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, - struct delegated_inode *delegated_inode) + struct delegated_inode *di) { int error; - unsigned max_links = dir->i_sb->s_max_links; struct dentry *de; error = may_create_dentry(idmap, dir, dentry); if (error) goto err; - error = -EPERM; + error = -EOPNOTSUPP; if (!dir->i_op->mkdir) goto err; @@ -5250,22 +5324,12 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (error) goto err; - error = -EMLINK; - if (max_links && dir->i_nlink >= max_links) - goto err; - - error = try_break_deleg(dir, delegated_inode); - if (error) + de = __vfs_mkdir(idmap, dir, dentry, mode, di); + if (IS_ERR(de)) { + error = PTR_ERR(de); goto err; - - de = dir->i_op->mkdir(idmap, dir, dentry, mode); - error = PTR_ERR(de); - if (IS_ERR(de)) - goto err; - if (de) { - dput(dentry); - dentry = de; } + dentry = de; fsnotify_mkdir(dir, dentry); return dentry; diff --git a/fs/open.c b/fs/open.c index 681d405bc61e..68b694ae1843 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1209,29 +1209,30 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op) if (WILL_CREATE(flags)) { if (how->mode & ~S_IALLUGO) return -EINVAL; - op->mode = how->mode | S_IFREG; + if (flags & O_DIRECTORY) + op->mode = how->mode | S_IFDIR; + else + op->mode = how->mode | S_IFREG; } else { if (how->mode != 0) return -EINVAL; op->mode = 0; } - /* - * Block bugs where O_DIRECTORY | O_CREAT created regular files. - * Note, that blocking O_DIRECTORY | O_CREAT here also protects - * O_TMPFILE below which requires O_DIRECTORY being raised. - */ - if ((flags & (O_DIRECTORY | O_CREAT)) == (O_DIRECTORY | O_CREAT)) - return -EINVAL; - /* Now handle the creative implementation of O_TMPFILE. */ if (flags & __O_TMPFILE) { /* * In order to ensure programs get explicit errors when trying * to use O_TMPFILE on old kernels we enforce that O_DIRECTORY - * is raised alongside __O_TMPFILE. + * is raised alongside __O_TMPFILE, but without O_CREAT. The + * reason for disallowing O_CREAT|O_TMPFILE is that + * O_DIRECTORY|O_CREAT used to work and created a regular file + * if nothing existed at the open path. Hence, allowing the + * combination would have caused O_CREAT|O_TMPFILE to create a + * regular (non-temporary) file on old kernels, while the caller + * would believe they created an actual O_TMPFILE. */ - if (!(flags & O_DIRECTORY)) + if (!(flags & O_DIRECTORY) || (flags & O_CREAT)) return -EINVAL; if (!(acc_mode & MAY_WRITE)) return -EINVAL; @@ -1268,6 +1269,8 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op) op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN; if (flags & O_CREAT) { + if ((flags & O_DIRECTORY) && (acc_mode & MAY_WRITE)) + return -EISDIR; op->intent |= LOOKUP_CREATE; if (flags & O_EXCL) { op->intent |= LOOKUP_EXCL; diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 613475285643..40ab8bbe668b 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -95,6 +95,8 @@ #define O_NDELAY O_NONBLOCK #endif +#define O_MKDIR_MASK (O_CREAT | O_DIRECTORY) + #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ #define F_SETFD 2 /* set/clear close_on_exec */ -- 2.54.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [RFC PATCH v4 2/2] selftest: add tests for open*(O_CREAT|O_DIRECTORY) 2026-05-18 16:52 [RFC PATCH v4 0/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) Jori Koolstra 2026-05-18 16:52 ` [RFC PATCH v4 1/2] " Jori Koolstra @ 2026-05-18 16:52 ` Jori Koolstra 2026-05-19 6:59 ` [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) syzbot ci 2 siblings, 0 replies; 9+ messages in thread From: Jori Koolstra @ 2026-05-18 16:52 UTC (permalink / raw) To: Alexander Viro, Christian Brauner, Jan Kara, Aleksa Sarai Cc: Jori Koolstra, linux-kernel, linux-fsdevel, cmirabil, Jori Koolstra Add some tests for the new valid O_CREAT|O_DIRECTORY flag combination for open*(2) to test compliance and to showcase its behaviour. Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl> --- .../testing/selftests/filesystems/.gitignore | 1 + tools/testing/selftests/filesystems/Makefile | 4 +- tools/testing/selftests/filesystems/fclog.c | 1 + .../filesystems/open_o_creat_o_dir.c | 200 ++++++++++++++++++ 4 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/filesystems/open_o_creat_o_dir.c diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore index 64ac0dfa46b7..f257b3ddb479 100644 --- a/tools/testing/selftests/filesystems/.gitignore +++ b/tools/testing/selftests/filesystems/.gitignore @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +open_o_creat_o_dir dnotify_test devpts_pts fclog diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile index 85427d7f19b9..ec7f93b700d2 100644 --- a/tools/testing/selftests/filesystems/Makefile +++ b/tools/testing/selftests/filesystems/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -CFLAGS += $(KHDR_INCLUDES) -TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test fclog +CFLAGS += $(KHDR_INCLUDES) $(TOOLS_INCLUDES) +TEST_GEN_PROGS := open_o_creat_o_dir devpts_pts file_stressor anon_inode_test kernfs_test fclog TEST_GEN_PROGS_EXTENDED := dnotify_test include ../lib.mk diff --git a/tools/testing/selftests/filesystems/fclog.c b/tools/testing/selftests/filesystems/fclog.c index 551c4a0f395a..33ed59286a2d 100644 --- a/tools/testing/selftests/filesystems/fclog.c +++ b/tools/testing/selftests/filesystems/fclog.c @@ -4,6 +4,7 @@ * Copyright (C) 2025 SUSE LLC. */ +#include <fcntl.h> #include <assert.h> #include <errno.h> #include <sched.h> diff --git a/tools/testing/selftests/filesystems/open_o_creat_o_dir.c b/tools/testing/selftests/filesystems/open_o_creat_o_dir.c new file mode 100644 index 000000000000..96edca532e4c --- /dev/null +++ b/tools/testing/selftests/filesystems/open_o_creat_o_dir.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/stat.h> +#include <sys/syscall.h> +#include <errno.h> +#include <limits.h> +#include <fcntl.h> + +#include "kselftest_harness.h" + +static inline int open_o_creat_o_dir(int dfd, const char *pathname, + mode_t mode, unsigned int flags) +{ + return syscall(__NR_openat, dfd, pathname, + flags | O_DIRECTORY | O_CREAT, mode); +} + +#define open_o_creat_o_dir_checked_flags(dfd, pathname, flags) ({ \ + struct stat __st; \ + int __fd = open_o_creat_o_dir(dfd, pathname, S_IRWXU, flags); \ + ASSERT_GE(__fd, 0); \ + EXPECT_EQ(fstat(__fd, &__st), 0); \ + EXPECT_TRUE(S_ISDIR(__st.st_mode)); \ + __fd; \ +}) + +#define open_o_creat_o_dir_checked(dfd, pathname) \ + open_o_creat_o_dir_checked_flags(dfd, pathname, 0) + +FIXTURE(open_o_creat_o_dir) { + char dirpath[PATH_MAX]; + int dfd; +}; + +FIXTURE_SETUP(open_o_creat_o_dir) +{ + snprintf(self->dirpath, sizeof(self->dirpath), + "/tmp/open_o_creat_o_dir_test.%d", getpid()); + ASSERT_EQ(mkdir(self->dirpath, S_IRWXU), 0); + + self->dfd = open(self->dirpath, O_DIRECTORY); + ASSERT_GE(self->dfd, 0); +} + +FIXTURE_TEARDOWN(open_o_creat_o_dir) +{ + close(self->dfd); + rmdir(self->dirpath); +} + +/* Does open_o_creat_o_dir return a fd at all? */ +TEST_F(open_o_creat_o_dir, returns_fd) +{ + int fd = open_o_creat_o_dir_checked(self->dfd, "newdir"); + EXPECT_EQ(close(fd), 0); + EXPECT_EQ(unlinkat(self->dfd, "newdir", AT_REMOVEDIR), 0); +} + +/* The fd must refer to the directory that was just created. */ +TEST_F(open_o_creat_o_dir, fd_is_created_dir) +{ + int fd; + struct stat st_via_fd, st_via_path; + char path[PATH_MAX]; + + fd = open_o_creat_o_dir_checked(self->dfd, "checkdir"); + + ASSERT_EQ(fstat(fd, &st_via_fd), 0); + + snprintf(path, sizeof(path), "%s/checkdir", self->dirpath); + ASSERT_EQ(stat(path, &st_via_path), 0); + + EXPECT_EQ(st_via_fd.st_ino, st_via_path.st_ino); + EXPECT_EQ(st_via_fd.st_dev, st_via_path.st_dev); + + EXPECT_EQ(close(fd), 0); + EXPECT_EQ(rmdir(path), 0); +} + +/* Missing parent component must fail with ENOENT. */ +TEST_F(open_o_creat_o_dir, enoent_missing_parent) +{ + EXPECT_EQ(open_o_creat_o_dir(self->dfd, "nonexistent/child", S_IRWXU, 0), -1); + EXPECT_EQ(errno, ENOENT); +} + +/* An invalid dfd must fail with EBADF. */ +TEST_F(open_o_creat_o_dir, ebadf) +{ + EXPECT_EQ(open_o_creat_o_dir(-42, "badfdir", S_IRWXU, 0), -1); + EXPECT_EQ(errno, EBADF); +} + +/* A dfd that points to a file (not a directory) must fail with ENOTDIR. */ +TEST_F(open_o_creat_o_dir, enotdir_dfd) +{ + int file_fd; + + file_fd = openat(self->dfd, "file", + O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GE(file_fd, 0); + + EXPECT_EQ(open_o_creat_o_dir(file_fd, "subdir", S_IRWXU, 0), -1); + EXPECT_EQ(errno, ENOTDIR); + + EXPECT_EQ(close(file_fd), 0); + EXPECT_EQ(unlinkat(self->dfd, "file", 0), 0); +} + +/* + * O_EXCL together with O_CREAT|O_DIRECTORY must fail with EEXIST when + * the target directory already exists. + */ +TEST_F(open_o_creat_o_dir, o_excl_eexist) +{ + int fd; + + fd = open_o_creat_o_dir_checked_flags(self->dfd, "excldir", O_EXCL); + EXPECT_EQ(close(fd), 0); + + EXPECT_EQ(open_o_creat_o_dir(self->dfd, "excldir", S_IRWXU, O_EXCL), -1); + EXPECT_EQ(errno, EEXIST); + + EXPECT_EQ(unlinkat(self->dfd, "excldir", AT_REMOVEDIR), 0); +} + +/* + * O_CREAT|O_DIRECTORY on a path that already exists as a regular file + * must fail with ENOTDIR. + */ +TEST_F(open_o_creat_o_dir, existing_file_enotdir) +{ + int file_fd; + struct stat st; + + file_fd = openat(self->dfd, "regfile", + O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GE(file_fd, 0); + EXPECT_EQ(close(file_fd), 0); + + EXPECT_EQ(open_o_creat_o_dir(self->dfd, "regfile", S_IRWXU, 0), -1); + EXPECT_EQ(errno, ENOTDIR); + + EXPECT_EQ(unlinkat(self->dfd, "regfile", 0), 0); +} + +/* + * O_CREAT|O_DIRECTORY combined with a writable access mode must be + * rejected: a directory cannot be opened for writing. + */ +TEST_F(open_o_creat_o_dir, rejects_writable_acc_mode) +{ + EXPECT_EQ(open_o_creat_o_dir(self->dfd, "rdwrdir", S_IRWXU, O_RDWR), -1); + EXPECT_EQ(errno, EISDIR); + /* Clean up if the kernel created the directory anyway. */ + unlinkat(self->dfd, "rdwrdir", AT_REMOVEDIR); +} + +/* + * openat(O_CREAT) with a trailing slash but without O_DIRECTORY + * must fail with EISDIR and must not create anything at the path. + */ +TEST_F(open_o_creat_o_dir, trailing_slash_no_o_dir) +{ + int fd; + struct stat st; + + fd = openat(self->dfd, "trailing/", O_CREAT | O_WRONLY, S_IRWXU); + EXPECT_EQ(fd, -1); + EXPECT_EQ(errno, EISDIR); + + EXPECT_EQ(fstatat(self->dfd, "trailing", &st, 0), -1); + EXPECT_EQ(errno, ENOENT); + + /* Best-effort cleanup in case the kernel left a file behind. */ + if (fd >= 0) + close(fd); + unlinkat(self->dfd, "trailing", 0); +} + +/* + * The returned fd must be usable as a dfd for further *at() calls. + */ +TEST_F(open_o_creat_o_dir, fd_usable_as_dfd) +{ + int parent_fd, child_fd; + char path[PATH_MAX]; + + parent_fd = open_o_creat_o_dir_checked(self->dfd, "parent"); + child_fd = open_o_creat_o_dir_checked(parent_fd, "child"); + + EXPECT_EQ(close(child_fd), 0); + EXPECT_EQ(close(parent_fd), 0); + + snprintf(path, sizeof(path), "%s/parent/child", self->dirpath); + EXPECT_EQ(rmdir(path), 0); + snprintf(path, sizeof(path), "%s/parent", self->dirpath); + EXPECT_EQ(rmdir(path), 0); +} + +TEST_HARNESS_MAIN -- 2.54.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) 2026-05-18 16:52 [RFC PATCH v4 0/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) Jori Koolstra 2026-05-18 16:52 ` [RFC PATCH v4 1/2] " Jori Koolstra 2026-05-18 16:52 ` [RFC PATCH v4 2/2] selftest: add tests for open*(O_CREAT|O_DIRECTORY) Jori Koolstra @ 2026-05-19 6:59 ` syzbot ci 2026-05-25 10:35 ` Jori Koolstra 2026-05-25 18:30 ` Jori Koolstra 2 siblings, 2 replies; 9+ messages in thread From: syzbot ci @ 2026-05-19 6:59 UTC (permalink / raw) To: brauner, cmirabil, cyphar, jack, jkoolstra, jori.koolstra, linux-fsdevel, linux-kernel, viro Cc: syzbot, syzkaller-bugs syzbot ci has tested the following series [v4] vfs: add O_CREAT|O_DIRECTORY to open*(2) https://lore.kernel.org/all/20260518165237.2084042-1-jkoolstra@xs4all.nl * [RFC PATCH v4 1/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) * [RFC PATCH v4 2/2] selftest: add tests for open*(O_CREAT|O_DIRECTORY) and found the following issues: * KASAN: slab-out-of-bounds Read in ovl_dir_release * general protection fault in path_openat Full report is available here: https://ci.syzbot.org/series/0d511b6b-6434-45cd-bbf3-51fe9d916e99 *** KASAN: slab-out-of-bounds Read in ovl_dir_release tree: torvalds URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux base: 5200f5f493f79f14bbdc349e402a40dfb32f23c8 arch: amd64 compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 config: https://ci.syzbot.org/builds/024b677f-50e7-4ef9-ae98-2652f5098bfd/config syz repro: https://ci.syzbot.org/findings/a7087360-137c-41f5-ae13-db4d551fe142/syz_repro ================================================================== BUG: KASAN: slab-out-of-bounds in ovl_dir_release+0x228/0x2a0 fs/overlayfs/readdir.c:1033 Read of size 8 at addr ffff88816cd2c818 by task syz.0.17/5813 CPU: 0 UID: 0 PID: 5813 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 Call Trace: <TASK> dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description+0x55/0x1e0 mm/kasan/report.c:378 print_report+0x58/0x70 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 ovl_dir_release+0x228/0x2a0 fs/overlayfs/readdir.c:1033 __fput+0x44f/0xa60 fs/file_table.c:510 task_work_run+0x1d9/0x270 kernel/task_work.c:233 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] __exit_to_user_mode_loop kernel/entry/common.c:67 [inline] exit_to_user_mode_loop+0xf3/0x4d0 kernel/entry/common.c:98 __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline] syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline] syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline] do_syscall_64+0x33e/0xf80 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fe1a159ce59 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffe25af07f8 EFLAGS: 00000246 ORIG_RAX: 00000000000001b4 RAX: 0000000000000000 RBX: 00007ffe25af08e0 RCX: 00007fe1a159ce59 RDX: 0000000000000000 RSI: 000000000000001e RDI: 0000000000000003 RBP: 0000000000012e15 R08: 0000000000000001 R09: 0000000000000000 R10: 0000001b32a20000 R11: 0000000000000246 R12: 00007ffe25af0920 R13: 00007fe1a1815fac R14: 0000000000012e53 R15: 00007fe1a1815fa0 </TASK> Allocated by task 5814: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x31c/0x660 mm/slub.c:5419 kmalloc_noprof include/linux/slab.h:950 [inline] kzalloc_noprof include/linux/slab.h:1188 [inline] ovl_file_alloc+0x4f/0x90 fs/overlayfs/file.c:99 ovl_create_tmpfile fs/overlayfs/dir.c:1399 [inline] ovl_tmpfile+0x3fc/0x7d0 fs/overlayfs/dir.c:1448 vfs_tmpfile+0x3ff/0x890 fs/namei.c:4794 do_tmpfile+0xd3/0x240 fs/namei.c:4859 path_openat+0x33c7/0x3b40 fs/namei.c:4893 do_file_open+0x23e/0x4a0 fs/namei.c:4931 do_sys_openat2+0x113/0x200 fs/open.c:1367 do_sys_open fs/open.c:1373 [inline] __do_sys_open fs/open.c:1381 [inline] __se_sys_open fs/open.c:1377 [inline] __x64_sys_open+0x11e/0x150 fs/open.c:1377 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff88816cd2c800 which belongs to the cache kmalloc-16 of size 16 The buggy address is located 8 bytes to the right of allocated 16-byte region [ffff88816cd2c800, ffff88816cd2c810) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88816cd2c840 pfn:0x16cd2c flags: 0x57ff00000000200(workingset|node=1|zone=2|lastcpupid=0x7ff) page_type: f5(slab) raw: 057ff00000000200 ffff888100041640 ffff888160400408 ffff888160400408 raw: ffff88816cd2c840 0000000800800042 00000000f5000000 0000000000000000 page dumped because: kasan: bad access detected page_owner tracks the page as allocated page last allocated via order 0, migratetype Unmovable, gfp_mask 0x252800(GFP_NOWAIT|__GFP_NORETRY|__GFP_COMP|__GFP_THISNODE), pid 5741, tgid 5741 (syz-executor), ts 77437404410, free_ts 77431264562 set_page_owner include/linux/page_owner.h:32 [inline] post_alloc_hook+0x231/0x280 mm/page_alloc.c:1858 prep_new_page mm/page_alloc.c:1866 [inline] get_page_from_freelist+0x24ba/0x2540 mm/page_alloc.c:3946 __alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5226 alloc_slab_page mm/slub.c:3278 [inline] allocate_slab+0x77/0x660 mm/slub.c:3467 new_slab mm/slub.c:3525 [inline] ___slab_alloc+0x154/0x6c0 mm/slub.c:4444 __slab_alloc_node mm/slub.c:4510 [inline] slab_alloc_node mm/slub.c:4886 [inline] __do_kmalloc_node mm/slub.c:5294 [inline] __kvmalloc_node_noprof+0x34d/0x8a0 mm/slub.c:6832 xt_jumpstack_alloc net/netfilter/x_tables.c:1449 [inline] do_replace_table+0x191/0x620 net/netfilter/x_tables.c:1486 xt_register_table+0x269/0x960 net/netfilter/x_tables.c:1596 ip6t_register_table+0x16b/0x330 net/ipv6/netfilter/ip6_tables.c:1754 ip6table_raw_table_init+0x54/0x80 net/ipv6/netfilter/ip6table_raw.c:48 xt_find_table_lock+0x30c/0x3f0 net/netfilter/x_tables.c:1353 xt_request_find_table_lock+0x26/0x100 net/netfilter/x_tables.c:1378 get_info net/ipv6/netfilter/ip6_tables.c:979 [inline] do_ip6t_get_ctl+0x716/0x1230 net/ipv6/netfilter/ip6_tables.c:1668 nf_getsockopt+0x26e/0x290 net/netfilter/nf_sockopt.c:116 ipv6_getsockopt+0x1fd/0x2b0 net/ipv6/ipv6_sockglue.c:1464 do_sock_getsockopt+0x51d/0x7e0 net/socket.c:2487 page last free pid 15 tgid 15 stack trace: reset_page_owner include/linux/page_owner.h:25 [inline] __free_pages_prepare mm/page_alloc.c:1402 [inline] __free_frozen_pages+0xbc7/0xd30 mm/page_alloc.c:2943 rcu_do_batch kernel/rcu/tree.c:2617 [inline] rcu_core+0x7cd/0x1070 kernel/rcu/tree.c:2869 handle_softirqs+0x22a/0x840 kernel/softirq.c:622 run_ksoftirqd+0x36/0x60 kernel/softirq.c:1076 smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 kthread+0x389/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Memory state around the buggy address: ffff88816cd2c700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88816cd2c780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff88816cd2c800: 00 00 fc fc 00 00 fc fc fc fc fc fc fc fc fc fc ^ ffff88816cd2c880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88816cd2c900: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== *** general protection fault in path_openat tree: torvalds URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux base: 5200f5f493f79f14bbdc349e402a40dfb32f23c8 arch: amd64 compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 config: https://ci.syzbot.org/builds/024b677f-50e7-4ef9-ae98-2652f5098bfd/config syz repro: https://ci.syzbot.org/findings/4b3b0fe3-064c-43e2-b887-b3a52d87a16a/syz_repro BTRFS info (device loop1): enabling ssd optimizations BTRFS info (device loop1): turning on async discard BTRFS info (device loop1): enabling free space tree BTRFS info (device loop1): use zstd compression, level 3 Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 1 UID: 0 PID: 5849 Comm: syz.1.18 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 RIP: 0010:__d_entry_type include/linux/dcache.h:429 [inline] RIP: 0010:d_can_lookup include/linux/dcache.h:444 [inline] RIP: 0010:do_open fs/namei.c:4726 [inline] RIP: 0010:path_openat+0x2e66/0x3b40 fs/namei.c:4902 Code: e8 8f 49 7f ff eb 62 48 8b 44 24 78 42 80 3c 20 00 48 8b 5c 24 68 74 08 48 89 df e8 44 87 ea ff 4c 8b 3b 4c 89 f8 48 c1 e8 03 <42> 0f b6 04 20 84 c0 0f 85 cb 09 00 00 41 bc 00 00 38 00 45 23 27 RSP: 0018:ffffc9000405f960 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffffc9000405fc28 RCX: 0000000000000000 RDX: ffff88810fe50000 RSI: 0000000000000002 RDI: 0000000000000000 RBP: ffffc9000405fbb0 R08: ffff8881b949469b R09: 1ffff110372928d3 R10: dffffc0000000000 R11: ffffed10372928d4 R12: dffffc0000000000 R13: 1ffff1102eb65c88 R14: 000000000015d0c0 R15: 0000000000000001 FS: 00007f074937f6c0(0000) GS:ffff8882a928a000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000c2113360000 CR3: 0000000103f3a000 CR4: 00000000000006f0 Call Trace: <TASK> do_file_open+0x23e/0x4a0 fs/namei.c:4931 do_sys_openat2+0x113/0x200 fs/open.c:1367 do_sys_open fs/open.c:1373 [inline] __do_sys_openat fs/open.c:1389 [inline] __se_sys_openat fs/open.c:1384 [inline] __x64_sys_openat+0x138/0x170 fs/open.c:1384 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f074859ce59 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007f074937f028 EFLAGS: 00000246 ORIG_RAX: 0000000000000101 RAX: ffffffffffffffda RBX: 00007f0748815fa0 RCX: 00007f074859ce59 RDX: 00000000001dd0c0 RSI: 0000200000000240 RDI: ffffffffffffff9c RBP: 00007f0748632d6f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f0748816038 R14: 00007f0748815fa0 R15: 00007fff34fee548 </TASK> Modules linked in: ---[ end trace 0000000000000000 ]--- RIP: 0010:__d_entry_type include/linux/dcache.h:429 [inline] RIP: 0010:d_can_lookup include/linux/dcache.h:444 [inline] RIP: 0010:do_open fs/namei.c:4726 [inline] RIP: 0010:path_openat+0x2e66/0x3b40 fs/namei.c:4902 Code: e8 8f 49 7f ff eb 62 48 8b 44 24 78 42 80 3c 20 00 48 8b 5c 24 68 74 08 48 89 df e8 44 87 ea ff 4c 8b 3b 4c 89 f8 48 c1 e8 03 <42> 0f b6 04 20 84 c0 0f 85 cb 09 00 00 41 bc 00 00 38 00 45 23 27 RSP: 0018:ffffc9000405f960 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffffc9000405fc28 RCX: 0000000000000000 RDX: ffff88810fe50000 RSI: 0000000000000002 RDI: 0000000000000000 RBP: ffffc9000405fbb0 R08: ffff8881b949469b R09: 1ffff110372928d3 R10: dffffc0000000000 R11: ffffed10372928d4 R12: dffffc0000000000 R13: 1ffff1102eb65c88 R14: 000000000015d0c0 R15: 0000000000000001 FS: 00007f074937f6c0(0000) GS:ffff8882a928a000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f1c7a2f73b0 CR3: 0000000103f3a000 CR4: 00000000000006f0 ---------------- Code disassembly (best guess): 0: e8 8f 49 7f ff call 0xff7f4994 5: eb 62 jmp 0x69 7: 48 8b 44 24 78 mov 0x78(%rsp),%rax c: 42 80 3c 20 00 cmpb $0x0,(%rax,%r12,1) 11: 48 8b 5c 24 68 mov 0x68(%rsp),%rbx 16: 74 08 je 0x20 18: 48 89 df mov %rbx,%rdi 1b: e8 44 87 ea ff call 0xffea8764 20: 4c 8b 3b mov (%rbx),%r15 23: 4c 89 f8 mov %r15,%rax 26: 48 c1 e8 03 shr $0x3,%rax * 2a: 42 0f b6 04 20 movzbl (%rax,%r12,1),%eax <-- trapping instruction 2f: 84 c0 test %al,%al 31: 0f 85 cb 09 00 00 jne 0xa02 37: 41 bc 00 00 38 00 mov $0x380000,%r12d 3d: 45 23 27 and (%r15),%r12d *** If these findings have caused you to resend the series or submit a separate fix, please add the following tag to your commit message: Tested-by: syzbot@syzkaller.appspotmail.com --- This report is generated by a bot. It may contain errors. syzbot ci engineers can be reached at syzkaller@googlegroups.com. To test a patch for this bug, please reply with `#syz test` (should be on a separate line). The patch should be attached to the email. Note: arguments like custom git repos and branches are not supported. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) 2026-05-19 6:59 ` [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) syzbot ci @ 2026-05-25 10:35 ` Jori Koolstra 2026-05-25 11:39 ` syzbot ci 2026-05-25 18:30 ` Jori Koolstra 1 sibling, 1 reply; 9+ messages in thread From: Jori Koolstra @ 2026-05-25 10:35 UTC (permalink / raw) To: syzbot ci, syzbot, syzkaller-bugs, linux-fsdevel, linux-kernel #syz test --- fs/namei.c | 180 ++++++++++++++++++++++++++++-------------- fs/open.c | 25 +++--- include/linux/fcntl.h | 2 + 3 files changed, 138 insertions(+), 69 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index c7fac83c9a85..60223278f9ec 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2777,9 +2777,14 @@ static const char *path_init(struct nameidata *nd, unsigned flags) return s; } +static inline bool trailing_slashes(struct nameidata *nd) +{ + return (bool)nd->last.name[nd->last.len]; +} + static inline const char *lookup_last(struct nameidata *nd) { - if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) + if (nd->last_type == LAST_NORM && trailing_slashes(nd)) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; return walk_component(nd, WALK_TRAILING); @@ -4166,6 +4171,16 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap, return mode; } +static int __vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode, + struct delegated_inode *di, bool excl) +{ + struct inode *dir = d_inode(dentry->d_parent); + int error = try_break_deleg(dir, di); + if (error) + return error; + return dir->i_op->create(idmap, dir, dentry, mode, excl); +} + /** * vfs_create - create new file * @idmap: idmap of the mount the inode was found from @@ -4192,16 +4207,14 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode, return error; if (!dir->i_op->create) - return -EACCES; /* shouldn't it be ENOSYS? */ + return -EOPNOTSUPP; mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG); error = security_inode_create(dir, dentry, mode); if (error) return error; - error = try_break_deleg(dir, di); - if (error) - return error; - error = dir->i_op->create(idmap, dir, dentry, mode, true); + + error = __vfs_create(idmap, dentry, mode, di, true); if (!error) fsnotify_create(dir, dentry); return error; @@ -4321,21 +4334,32 @@ static inline int open_to_namei_flags(int flag) static int may_o_create(struct mnt_idmap *idmap, const struct path *dir, struct dentry *dentry, - umode_t mode) + umode_t mode, bool create_dir) { - int error = security_path_mknod(dir, dentry, mode, 0); + struct inode *dir_inode = dir->dentry->d_inode; + int error; + + error = create_dir ? security_path_mkdir(dir, dentry, mode) + : security_path_mknod(dir, dentry, mode, 0); if (error) return error; if (!fsuidgid_has_mapping(dir->dentry->d_sb, idmap)) return -EOVERFLOW; - error = inode_permission(idmap, dir->dentry->d_inode, - MAY_WRITE | MAY_EXEC); + error = inode_permission(idmap, dir_inode, MAY_WRITE | MAY_EXEC); if (error) return error; - return security_inode_create(dir->dentry->d_inode, dentry, mode); + return create_dir ? security_inode_mkdir(dir_inode, dentry, mode) + : security_inode_create(dir_inode, dentry, mode); +} + +static inline umode_t o_create_mode(struct mnt_idmap *idmap, + const struct inode *dir, umode_t mode, bool create_dir) +{ + return create_dir ? vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, 0) + : vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG); } /* @@ -4359,6 +4383,11 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry struct inode *dir = path->dentry->d_inode; int error; + if ((open_flag & O_MKDIR_MASK) == O_MKDIR_MASK) { + error = -EOPNOTSUPP; + goto out; + } + file->__f_path.dentry = DENTRY_NOT_SET; file->__f_path.mnt = path->mnt; error = dir->i_op->atomic_open(dir, dentry, file, @@ -4381,6 +4410,7 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry error = -ENOENT; } } +out: if (error) { dput(dentry); dentry = ERR_PTR(error); @@ -4388,6 +4418,9 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry return dentry; } +static struct dentry *__vfs_mkdir(struct mnt_idmap *, struct inode *, + struct dentry *, umode_t, + struct delegated_inode *); /* * Look up and maybe create and open the last component. * @@ -4412,8 +4445,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, struct inode *dir_inode = dir->d_inode; int open_flag = op->open_flag; struct dentry *dentry; - int error, create_error = 0; + int error = 0, create_error = 0; umode_t mode = op->mode; + bool create_dir = (open_flag & O_MKDIR_MASK) == O_MKDIR_MASK; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); if (unlikely(IS_DEADDIR(dir_inode))) @@ -4462,10 +4496,10 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, if (open_flag & O_CREAT) { if (open_flag & O_EXCL) open_flag &= ~O_TRUNC; - mode = vfs_prepare_mode(idmap, dir->d_inode, mode, mode, mode); + mode = o_create_mode(idmap, dir_inode, mode, create_dir); if (likely(got_write)) create_error = may_o_create(idmap, &nd->path, - dentry, mode); + dentry, mode, create_dir); else create_error = -EROFS; } @@ -4494,29 +4528,37 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, } } + if (unlikely(create_error) && !dentry->d_inode) { + error = create_error; + goto out_dput; + } + /* Negative dentry, just create the file */ if (!dentry->d_inode && (open_flag & O_CREAT)) { - /* but break the directory lease first! */ - error = try_break_deleg(dir_inode, delegated_inode); - if (error) - goto out_dput; file->f_mode |= FMODE_CREATED; audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE); - if (!dir_inode->i_op->create) { - error = -EACCES; + if ((create_dir && !dir_inode->i_op->mkdir) + || (!create_dir && !dir_inode->i_op->create)) { + error = -EOPNOTSUPP; goto out_dput; } - error = dir_inode->i_op->create(idmap, dir_inode, dentry, - mode, open_flag & O_EXCL); + if (create_dir) { + struct dentry *res = __vfs_mkdir(idmap, dir_inode, dentry, mode, + delegated_inode); + if (IS_ERR(res)) + error = PTR_ERR(res); + else + dentry = res; + } else { + error = __vfs_create(idmap, dentry, mode, delegated_inode, + open_flag & O_EXCL); + } if (error) goto out_dput; } - if (unlikely(create_error) && !dentry->d_inode) { - error = create_error; - goto out_dput; - } + return dentry; out_dput: @@ -4524,17 +4566,12 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, return ERR_PTR(error); } -static inline bool trailing_slashes(struct nameidata *nd) -{ - return (bool)nd->last.name[nd->last.len]; -} - static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_flag) { struct dentry *dentry; if (open_flag & O_CREAT) { - if (trailing_slashes(nd)) + if (trailing_slashes(nd) && !(open_flag & O_DIRECTORY)) return ERR_PTR(-EISDIR); /* Don't bother on an O_EXCL create */ @@ -4610,8 +4647,12 @@ static const char *open_last_lookups(struct nameidata *nd, inode_lock_shared(dir->d_inode); dentry = lookup_open(nd, file, op, got_write, &delegated_inode); if (!IS_ERR(dentry)) { - if (file->f_mode & FMODE_CREATED) - fsnotify_create(dir->d_inode, dentry); + if (file->f_mode & FMODE_CREATED) { + if (open_flag & O_DIRECTORY) + fsnotify_mkdir(dir->d_inode, dentry); + else + fsnotify_create(dir->d_inode, dentry); + } if (file->f_mode & FMODE_OPENED) fsnotify_open(file); } @@ -4672,12 +4713,15 @@ static int do_open(struct nameidata *nd, if (open_flag & O_CREAT) { if ((open_flag & O_EXCL) && !(file->f_mode & FMODE_CREATED)) return -EEXIST; - if (d_is_dir(nd->path.dentry)) - return -EISDIR; - error = may_create_in_sticky(idmap, nd, - d_backing_inode(nd->path.dentry)); - if (unlikely(error)) - return error; + if (!(open_flag & O_DIRECTORY)) { + if (d_is_dir(nd->path.dentry)) + return -EISDIR; + + error = may_create_in_sticky(idmap, nd, + d_backing_inode(nd->path.dentry)); + if (unlikely(error)) + return error; + } } if ((nd->flags & LOOKUP_DIRECTORY) && !d_can_lookup(nd->path.dentry)) return -ENOTDIR; @@ -5039,7 +5083,7 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode, path->dentry = dir; mode = vfs_prepare_mode(idmap, dir_inode, mode, S_IALLUGO, S_IFREG); - create_error = may_o_create(idmap, path, dentry, mode); + create_error = may_o_create(idmap, path, dentry, mode, false); if (create_error) flags &= ~O_CREAT; @@ -5207,6 +5251,37 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d return filename_mknodat(AT_FDCWD, name, mode, dev); } +static struct dentry *__vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, + struct delegated_inode *di) +{ + int error; + unsigned max_links = dir->i_sb->s_max_links; + struct dentry *de; + + error = -EMLINK; + if (max_links && dir->i_nlink >= max_links) + goto err; + + error = try_break_deleg(dir, di); + if (error) + goto err; + + de = dir->i_op->mkdir(idmap, dir, dentry, mode); + if (IS_ERR(de)) { + error = PTR_ERR(de); + goto err; + } + if (de) { + dput(dentry); + dentry = de; + } + return dentry; + +err: + return ERR_PTR(error); +} + /** * vfs_mkdir - create directory returning correct dentry if possible * @idmap: idmap of the mount the inode was found from @@ -5231,17 +5306,16 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d */ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, - struct delegated_inode *delegated_inode) + struct delegated_inode *di) { int error; - unsigned max_links = dir->i_sb->s_max_links; struct dentry *de; error = may_create_dentry(idmap, dir, dentry); if (error) goto err; - error = -EPERM; + error = -EOPNOTSUPP; if (!dir->i_op->mkdir) goto err; @@ -5250,22 +5324,12 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (error) goto err; - error = -EMLINK; - if (max_links && dir->i_nlink >= max_links) - goto err; - - error = try_break_deleg(dir, delegated_inode); - if (error) + de = __vfs_mkdir(idmap, dir, dentry, mode, di); + if (IS_ERR(de)) { + error = PTR_ERR(de); goto err; - - de = dir->i_op->mkdir(idmap, dir, dentry, mode); - error = PTR_ERR(de); - if (IS_ERR(de)) - goto err; - if (de) { - dput(dentry); - dentry = de; } + dentry = de; fsnotify_mkdir(dir, dentry); return dentry; diff --git a/fs/open.c b/fs/open.c index 681d405bc61e..865ea6f70e8c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1209,29 +1209,30 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op) if (WILL_CREATE(flags)) { if (how->mode & ~S_IALLUGO) return -EINVAL; - op->mode = how->mode | S_IFREG; + if ((flags & (O_MKDIR_MASK)) == O_MKDIR_MASK) + op->mode = how->mode | S_IFDIR; + else + op->mode = how->mode | S_IFREG; } else { if (how->mode != 0) return -EINVAL; op->mode = 0; } - /* - * Block bugs where O_DIRECTORY | O_CREAT created regular files. - * Note, that blocking O_DIRECTORY | O_CREAT here also protects - * O_TMPFILE below which requires O_DIRECTORY being raised. - */ - if ((flags & (O_DIRECTORY | O_CREAT)) == (O_DIRECTORY | O_CREAT)) - return -EINVAL; - /* Now handle the creative implementation of O_TMPFILE. */ if (flags & __O_TMPFILE) { /* * In order to ensure programs get explicit errors when trying * to use O_TMPFILE on old kernels we enforce that O_DIRECTORY - * is raised alongside __O_TMPFILE. + * is raised alongside __O_TMPFILE, but without O_CREAT. The + * reason for disallowing O_CREAT|O_TMPFILE is that + * O_DIRECTORY|O_CREAT used to work and created a regular file + * if nothing existed at the open path. Hence, allowing the + * combination would have caused O_CREAT|O_TMPFILE to create a + * regular (non-temporary) file on old kernels, while the caller + * would believe they created an actual O_TMPFILE. */ - if (!(flags & O_DIRECTORY)) + if (!(flags & O_DIRECTORY) || (flags & O_CREAT)) return -EINVAL; if (!(acc_mode & MAY_WRITE)) return -EINVAL; @@ -1268,6 +1269,8 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op) op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN; if (flags & O_CREAT) { + if ((flags & O_DIRECTORY) && (acc_mode & MAY_WRITE)) + return -EISDIR; op->intent |= LOOKUP_CREATE; if (flags & O_EXCL) { op->intent |= LOOKUP_EXCL; diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index a332e79b3207..e31f3a57f07c 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -12,6 +12,8 @@ FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \ O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE) +#define O_MKDIR_MASK (O_CREAT | O_DIRECTORY) + /* List of all valid flags for the how->resolve argument: */ #define VALID_RESOLVE_FLAGS \ (RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \ -- 2.54.0 > Op 19-05-2026 08:59 CEST schreef syzbot ci <syzbot+ci8ee793bb5ffded2a@syzkaller.appspotmail.com>: > > > syzbot ci has tested the following series > > [v4] vfs: add O_CREAT|O_DIRECTORY to open*(2) > https://lore.kernel.org/all/20260518165237.2084042-1-jkoolstra@xs4all.nl > * [RFC PATCH v4 1/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) > * [RFC PATCH v4 2/2] selftest: add tests for open*(O_CREAT|O_DIRECTORY) > > and found the following issues: > * KASAN: slab-out-of-bounds Read in ovl_dir_release > * general protection fault in path_openat > > Full report is available here: > https://ci.syzbot.org/series/0d511b6b-6434-45cd-bbf3-51fe9d916e99 > > *** > > KASAN: slab-out-of-bounds Read in ovl_dir_release > > tree: torvalds > URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux > base: 5200f5f493f79f14bbdc349e402a40dfb32f23c8 > arch: amd64 > compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 > config: https://ci.syzbot.org/builds/024b677f-50e7-4ef9-ae98-2652f5098bfd/config > syz repro: https://ci.syzbot.org/findings/a7087360-137c-41f5-ae13-db4d551fe142/syz_repro > > ================================================================== > BUG: KASAN: slab-out-of-bounds in ovl_dir_release+0x228/0x2a0 fs/overlayfs/readdir.c:1033 > Read of size 8 at addr ffff88816cd2c818 by task syz.0.17/5813 > > CPU: 0 UID: 0 PID: 5813 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) > Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 > Call Trace: > <TASK> > dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 > print_address_description+0x55/0x1e0 mm/kasan/report.c:378 > print_report+0x58/0x70 mm/kasan/report.c:482 > kasan_report+0x117/0x150 mm/kasan/report.c:595 > ovl_dir_release+0x228/0x2a0 fs/overlayfs/readdir.c:1033 > __fput+0x44f/0xa60 fs/file_table.c:510 > task_work_run+0x1d9/0x270 kernel/task_work.c:233 > resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] > __exit_to_user_mode_loop kernel/entry/common.c:67 [inline] > exit_to_user_mode_loop+0xf3/0x4d0 kernel/entry/common.c:98 > __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline] > syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline] > syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline] > do_syscall_64+0x33e/0xf80 arch/x86/entry/syscall_64.c:100 > entry_SYSCALL_64_after_hwframe+0x77/0x7f > RIP: 0033:0x7fe1a159ce59 > Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 > RSP: 002b:00007ffe25af07f8 EFLAGS: 00000246 ORIG_RAX: 00000000000001b4 > RAX: 0000000000000000 RBX: 00007ffe25af08e0 RCX: 00007fe1a159ce59 > RDX: 0000000000000000 RSI: 000000000000001e RDI: 0000000000000003 > RBP: 0000000000012e15 R08: 0000000000000001 R09: 0000000000000000 > R10: 0000001b32a20000 R11: 0000000000000246 R12: 00007ffe25af0920 > R13: 00007fe1a1815fac R14: 0000000000012e53 R15: 00007fe1a1815fa0 > </TASK> > > Allocated by task 5814: > kasan_save_stack mm/kasan/common.c:57 [inline] > kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 > poison_kmalloc_redzone mm/kasan/common.c:398 [inline] > __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415 > kasan_kmalloc include/linux/kasan.h:263 [inline] > __kmalloc_cache_noprof+0x31c/0x660 mm/slub.c:5419 > kmalloc_noprof include/linux/slab.h:950 [inline] > kzalloc_noprof include/linux/slab.h:1188 [inline] > ovl_file_alloc+0x4f/0x90 fs/overlayfs/file.c:99 > ovl_create_tmpfile fs/overlayfs/dir.c:1399 [inline] > ovl_tmpfile+0x3fc/0x7d0 fs/overlayfs/dir.c:1448 > vfs_tmpfile+0x3ff/0x890 fs/namei.c:4794 > do_tmpfile+0xd3/0x240 fs/namei.c:4859 > path_openat+0x33c7/0x3b40 fs/namei.c:4893 > do_file_open+0x23e/0x4a0 fs/namei.c:4931 > do_sys_openat2+0x113/0x200 fs/open.c:1367 > do_sys_open fs/open.c:1373 [inline] > __do_sys_open fs/open.c:1381 [inline] > __se_sys_open fs/open.c:1377 [inline] > __x64_sys_open+0x11e/0x150 fs/open.c:1377 > do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] > do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 > entry_SYSCALL_64_after_hwframe+0x77/0x7f > > The buggy address belongs to the object at ffff88816cd2c800 > which belongs to the cache kmalloc-16 of size 16 > The buggy address is located 8 bytes to the right of > allocated 16-byte region [ffff88816cd2c800, ffff88816cd2c810) > > The buggy address belongs to the physical page: > page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88816cd2c840 pfn:0x16cd2c > flags: 0x57ff00000000200(workingset|node=1|zone=2|lastcpupid=0x7ff) > page_type: f5(slab) > raw: 057ff00000000200 ffff888100041640 ffff888160400408 ffff888160400408 > raw: ffff88816cd2c840 0000000800800042 00000000f5000000 0000000000000000 > page dumped because: kasan: bad access detected > page_owner tracks the page as allocated > page last allocated via order 0, migratetype Unmovable, gfp_mask 0x252800(GFP_NOWAIT|__GFP_NORETRY|__GFP_COMP|__GFP_THISNODE), pid 5741, tgid 5741 (syz-executor), ts 77437404410, free_ts 77431264562 > set_page_owner include/linux/page_owner.h:32 [inline] > post_alloc_hook+0x231/0x280 mm/page_alloc.c:1858 > prep_new_page mm/page_alloc.c:1866 [inline] > get_page_from_freelist+0x24ba/0x2540 mm/page_alloc.c:3946 > __alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5226 > alloc_slab_page mm/slub.c:3278 [inline] > allocate_slab+0x77/0x660 mm/slub.c:3467 > new_slab mm/slub.c:3525 [inline] > ___slab_alloc+0x154/0x6c0 mm/slub.c:4444 > __slab_alloc_node mm/slub.c:4510 [inline] > slab_alloc_node mm/slub.c:4886 [inline] > __do_kmalloc_node mm/slub.c:5294 [inline] > __kvmalloc_node_noprof+0x34d/0x8a0 mm/slub.c:6832 > xt_jumpstack_alloc net/netfilter/x_tables.c:1449 [inline] > do_replace_table+0x191/0x620 net/netfilter/x_tables.c:1486 > xt_register_table+0x269/0x960 net/netfilter/x_tables.c:1596 > ip6t_register_table+0x16b/0x330 net/ipv6/netfilter/ip6_tables.c:1754 > ip6table_raw_table_init+0x54/0x80 net/ipv6/netfilter/ip6table_raw.c:48 > xt_find_table_lock+0x30c/0x3f0 net/netfilter/x_tables.c:1353 > xt_request_find_table_lock+0x26/0x100 net/netfilter/x_tables.c:1378 > get_info net/ipv6/netfilter/ip6_tables.c:979 [inline] > do_ip6t_get_ctl+0x716/0x1230 net/ipv6/netfilter/ip6_tables.c:1668 > nf_getsockopt+0x26e/0x290 net/netfilter/nf_sockopt.c:116 > ipv6_getsockopt+0x1fd/0x2b0 net/ipv6/ipv6_sockglue.c:1464 > do_sock_getsockopt+0x51d/0x7e0 net/socket.c:2487 > page last free pid 15 tgid 15 stack trace: > reset_page_owner include/linux/page_owner.h:25 [inline] > __free_pages_prepare mm/page_alloc.c:1402 [inline] > __free_frozen_pages+0xbc7/0xd30 mm/page_alloc.c:2943 > rcu_do_batch kernel/rcu/tree.c:2617 [inline] > rcu_core+0x7cd/0x1070 kernel/rcu/tree.c:2869 > handle_softirqs+0x22a/0x840 kernel/softirq.c:622 > run_ksoftirqd+0x36/0x60 kernel/softirq.c:1076 > smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 > kthread+0x389/0x470 kernel/kthread.c:436 > ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 > > Memory state around the buggy address: > ffff88816cd2c700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > ffff88816cd2c780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >ffff88816cd2c800: 00 00 fc fc 00 00 fc fc fc fc fc fc fc fc fc fc > ^ > ffff88816cd2c880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > ffff88816cd2c900: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > ================================================================== > > > *** > > general protection fault in path_openat > > tree: torvalds > URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux > base: 5200f5f493f79f14bbdc349e402a40dfb32f23c8 > arch: amd64 > compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 > config: https://ci.syzbot.org/builds/024b677f-50e7-4ef9-ae98-2652f5098bfd/config > syz repro: https://ci.syzbot.org/findings/4b3b0fe3-064c-43e2-b887-b3a52d87a16a/syz_repro > > BTRFS info (device loop1): enabling ssd optimizations > BTRFS info (device loop1): turning on async discard > BTRFS info (device loop1): enabling free space tree > BTRFS info (device loop1): use zstd compression, level 3 > Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN PTI > KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] > CPU: 1 UID: 0 PID: 5849 Comm: syz.1.18 Not tainted syzkaller #0 PREEMPT(full) > Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 > RIP: 0010:__d_entry_type include/linux/dcache.h:429 [inline] > RIP: 0010:d_can_lookup include/linux/dcache.h:444 [inline] > RIP: 0010:do_open fs/namei.c:4726 [inline] > RIP: 0010:path_openat+0x2e66/0x3b40 fs/namei.c:4902 > Code: e8 8f 49 7f ff eb 62 48 8b 44 24 78 42 80 3c 20 00 48 8b 5c 24 68 74 08 48 89 df e8 44 87 ea ff 4c 8b 3b 4c 89 f8 48 c1 e8 03 <42> 0f b6 04 20 84 c0 0f 85 cb 09 00 00 41 bc 00 00 38 00 45 23 27 > RSP: 0018:ffffc9000405f960 EFLAGS: 00010246 > RAX: 0000000000000000 RBX: ffffc9000405fc28 RCX: 0000000000000000 > RDX: ffff88810fe50000 RSI: 0000000000000002 RDI: 0000000000000000 > RBP: ffffc9000405fbb0 R08: ffff8881b949469b R09: 1ffff110372928d3 > R10: dffffc0000000000 R11: ffffed10372928d4 R12: dffffc0000000000 > R13: 1ffff1102eb65c88 R14: 000000000015d0c0 R15: 0000000000000001 > FS: 00007f074937f6c0(0000) GS:ffff8882a928a000(0000) knlGS:0000000000000000 > CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > CR2: 00000c2113360000 CR3: 0000000103f3a000 CR4: 00000000000006f0 > Call Trace: > <TASK> > do_file_open+0x23e/0x4a0 fs/namei.c:4931 > do_sys_openat2+0x113/0x200 fs/open.c:1367 > do_sys_open fs/open.c:1373 [inline] > __do_sys_openat fs/open.c:1389 [inline] > __se_sys_openat fs/open.c:1384 [inline] > __x64_sys_openat+0x138/0x170 fs/open.c:1384 > do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] > do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 > entry_SYSCALL_64_after_hwframe+0x77/0x7f > RIP: 0033:0x7f074859ce59 > Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 > RSP: 002b:00007f074937f028 EFLAGS: 00000246 ORIG_RAX: 0000000000000101 > RAX: ffffffffffffffda RBX: 00007f0748815fa0 RCX: 00007f074859ce59 > RDX: 00000000001dd0c0 RSI: 0000200000000240 RDI: ffffffffffffff9c > RBP: 00007f0748632d6f R08: 0000000000000000 R09: 0000000000000000 > R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 > R13: 00007f0748816038 R14: 00007f0748815fa0 R15: 00007fff34fee548 > </TASK> > Modules linked in: > ---[ end trace 0000000000000000 ]--- > RIP: 0010:__d_entry_type include/linux/dcache.h:429 [inline] > RIP: 0010:d_can_lookup include/linux/dcache.h:444 [inline] > RIP: 0010:do_open fs/namei.c:4726 [inline] > RIP: 0010:path_openat+0x2e66/0x3b40 fs/namei.c:4902 > Code: e8 8f 49 7f ff eb 62 48 8b 44 24 78 42 80 3c 20 00 48 8b 5c 24 68 74 08 48 89 df e8 44 87 ea ff 4c 8b 3b 4c 89 f8 48 c1 e8 03 <42> 0f b6 04 20 84 c0 0f 85 cb 09 00 00 41 bc 00 00 38 00 45 23 27 > RSP: 0018:ffffc9000405f960 EFLAGS: 00010246 > RAX: 0000000000000000 RBX: ffffc9000405fc28 RCX: 0000000000000000 > RDX: ffff88810fe50000 RSI: 0000000000000002 RDI: 0000000000000000 > RBP: ffffc9000405fbb0 R08: ffff8881b949469b R09: 1ffff110372928d3 > R10: dffffc0000000000 R11: ffffed10372928d4 R12: dffffc0000000000 > R13: 1ffff1102eb65c88 R14: 000000000015d0c0 R15: 0000000000000001 > FS: 00007f074937f6c0(0000) GS:ffff8882a928a000(0000) knlGS:0000000000000000 > CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > CR2: 00007f1c7a2f73b0 CR3: 0000000103f3a000 CR4: 00000000000006f0 > ---------------- > Code disassembly (best guess): > 0: e8 8f 49 7f ff call 0xff7f4994 > 5: eb 62 jmp 0x69 > 7: 48 8b 44 24 78 mov 0x78(%rsp),%rax > c: 42 80 3c 20 00 cmpb $0x0,(%rax,%r12,1) > 11: 48 8b 5c 24 68 mov 0x68(%rsp),%rbx > 16: 74 08 je 0x20 > 18: 48 89 df mov %rbx,%rdi > 1b: e8 44 87 ea ff call 0xffea8764 > 20: 4c 8b 3b mov (%rbx),%r15 > 23: 4c 89 f8 mov %r15,%rax > 26: 48 c1 e8 03 shr $0x3,%rax > * 2a: 42 0f b6 04 20 movzbl (%rax,%r12,1),%eax <-- trapping instruction > 2f: 84 c0 test %al,%al > 31: 0f 85 cb 09 00 00 jne 0xa02 > 37: 41 bc 00 00 38 00 mov $0x380000,%r12d > 3d: 45 23 27 and (%r15),%r12d > > > *** > > If these findings have caused you to resend the series or submit a > separate fix, please add the following tag to your commit message: > Tested-by: syzbot@syzkaller.appspotmail.com > > --- > This report is generated by a bot. It may contain errors. > syzbot ci engineers can be reached at syzkaller@googlegroups.com. > > To test a patch for this bug, please reply with `#syz test` > (should be on a separate line). > > The patch should be attached to the email. > Note: arguments like custom git repos and branches are not supported. ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) 2026-05-25 10:35 ` Jori Koolstra @ 2026-05-25 11:39 ` syzbot ci 0 siblings, 0 replies; 9+ messages in thread From: syzbot ci @ 2026-05-25 11:39 UTC (permalink / raw) To: jkoolstra, linux-fsdevel, linux-kernel, syzbot, syzkaller-bugs Cc: syzbot, syzkaller-bugs syzbot ci has tested the suggested fix patch on top of the following series: [v4] vfs: add O_CREAT|O_DIRECTORY to open*(2) https://lore.kernel.org/all/20260518165237.2084042-1-jkoolstra@xs4all.nl Patch: https://ci.syzbot.org/jobs/3effa8d6-6f06-49b5-b228-7c7502ca630d/patch The patch testing request could not be completed: Testing failed due to an infrastructure error. Testing results: * [build 0] Build Patched: error Full report is available here: https://ci.syzbot.org/session/e7a8e9a6-6522-439a-a09c-39da7d9d738b --- This report is generated by a bot. It may contain errors. syzbot ci engineers can be reached at syzkaller@googlegroups.com. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) 2026-05-19 6:59 ` [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) syzbot ci 2026-05-25 10:35 ` Jori Koolstra @ 2026-05-25 18:30 ` Jori Koolstra 2026-05-25 19:02 ` syzbot ci 1 sibling, 1 reply; 9+ messages in thread From: Jori Koolstra @ 2026-05-25 18:30 UTC (permalink / raw) To: syzbot ci, linux-fsdevel, linux-kernel, syzbot, syzkaller-bugs #syz test --- diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index f468acb8ee7d..d1925333d327 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -771,6 +771,9 @@ v9fs_vfs_atomic_open(struct inode *dir, struct dentry *dentry, struct inode *inode; int p9_omode; + if ((flags & O_MKDIR_MASK) == O_MKDIR_MASK) + return -EINVAL; + if (d_in_lookup(dentry)) { struct dentry *res = v9fs_vfs_lookup(dir, dentry, 0); if (res || d_really_is_positive(dentry)) diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 141fb54db65d..9f4b865d07d7 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -239,6 +239,9 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, struct v9fs_session_info *v9ses; struct posix_acl *pacl = NULL, *dacl = NULL; + if ((flags & O_MKDIR_MASK) == O_MKDIR_MASK) + return -EINVAL; + if (d_in_lookup(dentry)) { struct dentry *res = v9fs_vfs_lookup(dir, dentry, 0); if (res || d_really_is_positive(dentry)) diff --git a/fs/ceph/file.c b/fs/ceph/file.c index d54d71669176..9707d9ed17b6 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -813,6 +813,9 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, if (dentry->d_name.len > NAME_MAX) return -ENAMETOOLONG; + if ((flags & O_MKDIR_MASK) == O_MKDIR_MASK) + return -EINVAL; + err = ceph_wait_on_conflict_unlink(dentry); if (err) return err; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index b658b6baf72f..4c59992b9867 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -940,6 +940,9 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry, if (fuse_is_bad(dir)) return -EIO; + if ((flags & O_MKDIR_MASK) == O_MKDIR_MASK) + return -EINVAL; + if (d_in_lookup(entry)) { struct dentry *res = fuse_lookup(dir, entry, 0); if (res || d_really_is_positive(entry)) diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index e9bf4879c07f..21c6544fbee5 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1384,6 +1384,9 @@ static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry, { bool excl = !!(flags & O_EXCL); + if ((flags & O_MKDIR_MASK) == O_MKDIR_MASK) + return -EINVAL; + if (d_in_lookup(dentry)) { struct dentry *d = __gfs2_lookup(dir, dentry, file); if (file->f_mode & FMODE_OPENED) { diff --git a/fs/namei.c b/fs/namei.c index 60223278f9ec..9d9529ef30c4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4383,11 +4383,6 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry struct inode *dir = path->dentry->d_inode; int error; - if ((open_flag & O_MKDIR_MASK) == O_MKDIR_MASK) { - error = -EOPNOTSUPP; - goto out; - } - file->__f_path.dentry = DENTRY_NOT_SET; file->__f_path.mnt = path->mnt; error = dir->i_op->atomic_open(dir, dentry, file, @@ -4410,7 +4405,6 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry error = -ENOENT; } } -out: if (error) { dput(dentry); dentry = ERR_PTR(error); @@ -4642,7 +4636,7 @@ static const char *open_last_lookups(struct nameidata *nd, */ } if (open_flag & O_CREAT) - inode_lock(dir->d_inode); + inode_lock_nested(dir->d_inode, I_MUTEX_PARENT); else inode_lock_shared(dir->d_inode); dentry = lookup_open(nd, file, op, got_write, &delegated_inode); @@ -4713,6 +4707,7 @@ static int do_open(struct nameidata *nd, if (open_flag & O_CREAT) { if ((open_flag & O_EXCL) && !(file->f_mode & FMODE_CREATED)) return -EEXIST; + // there are no special rules for creating dirs in a sticky bit dir if (!(open_flag & O_DIRECTORY)) { if (d_is_dir(nd->path.dentry)) return -EISDIR; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e9ce1883288c..e44c7598b68e 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2314,6 +2314,9 @@ int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry, if (dentry->d_name.len > NFS_SERVER(dir)->namelen) return -ENAMETOOLONG; + if ((open_flags & O_MKDIR_MASK) == O_MKDIR_MASK) + return -EINVAL; + if (open_flags & O_CREAT) { error = nfs_do_create(dir, dentry, mode, open_flags); if (!error) { diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 25048a3c2364..467f6bc707da 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -52,6 +52,9 @@ int nfs_check_flags(int flags) if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT)) return -EINVAL; + if ((flags & O_MKDIR_MASK) == O_MKDIR_MASK) + return -EINVAL; + return 0; } EXPORT_SYMBOL_GPL(nfs_check_flags); diff --git a/fs/open.c b/fs/open.c index 68b694ae1843..865ea6f70e8c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1209,7 +1209,7 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op) if (WILL_CREATE(flags)) { if (how->mode & ~S_IALLUGO) return -EINVAL; - if (flags & O_DIRECTORY) + if ((flags & (O_MKDIR_MASK)) == O_MKDIR_MASK) op->mode = how->mode | S_IFDIR; else op->mode = how->mode | S_IFREG; diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c index e4295a5b55b3..ec8c54c91261 100644 --- a/fs/smb/client/dir.c +++ b/fs/smb/client/dir.c @@ -526,6 +526,9 @@ int cifs_atomic_open(struct inode *dir, struct dentry *direntry, if (unlikely(cifs_forced_shutdown(cifs_sb))) return smb_EIO(smb_eio_trace_forced_shutdown); + if ((oflags & O_MKDIR_MASK) == O_MKDIR_MASK) + return -EINVAL; + /* * Posix open is only called (at lookup time) for file create now. For * opens (rather than creates), because we do not know if it is a file diff --git a/fs/vboxsf/dir.c b/fs/vboxsf/dir.c index 42bedc4ec7af..aef5ca6730be 100644 --- a/fs/vboxsf/dir.c +++ b/fs/vboxsf/dir.c @@ -318,6 +318,9 @@ static int vboxsf_dir_atomic_open(struct inode *parent, struct dentry *dentry, u64 handle; int err; + if ((flags & O_MKDIR_MASK) == O_MKDIR_MASK) + return -EINVAL; + if (d_in_lookup(dentry)) { struct dentry *res = vboxsf_dir_lookup(parent, dentry, 0); if (res || d_really_is_positive(dentry)) diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index a332e79b3207..e31f3a57f07c 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -12,6 +12,8 @@ FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \ O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE) +#define O_MKDIR_MASK (O_CREAT | O_DIRECTORY) + /* List of all valid flags for the how->resolve argument: */ #define VALID_RESOLVE_FLAGS \ (RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \ diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 40ab8bbe668b..613475285643 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -95,8 +95,6 @@ #define O_NDELAY O_NONBLOCK #endif -#define O_MKDIR_MASK (O_CREAT | O_DIRECTORY) - #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ #define F_SETFD 2 /* set/clear close_on_exec */ > Op 19-05-2026 08:59 CEST schreef syzbot ci <syzbot+ci8ee793bb5ffded2a@syzkaller.appspotmail.com>: > > > syzbot ci has tested the following series > > [v4] vfs: add O_CREAT|O_DIRECTORY to open*(2) > https://lore.kernel.org/all/20260518165237.2084042-1-jkoolstra@xs4all.nl > * [RFC PATCH v4 1/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) > * [RFC PATCH v4 2/2] selftest: add tests for open*(O_CREAT|O_DIRECTORY) > > and found the following issues: > * KASAN: slab-out-of-bounds Read in ovl_dir_release > * general protection fault in path_openat > > Full report is available here: > https://ci.syzbot.org/series/0d511b6b-6434-45cd-bbf3-51fe9d916e99 > > *** > > KASAN: slab-out-of-bounds Read in ovl_dir_release > > tree: torvalds > URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux > base: 5200f5f493f79f14bbdc349e402a40dfb32f23c8 > arch: amd64 > compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 > config: https://ci.syzbot.org/builds/024b677f-50e7-4ef9-ae98-2652f5098bfd/config > syz repro: https://ci.syzbot.org/findings/a7087360-137c-41f5-ae13-db4d551fe142/syz_repro > > ================================================================== > BUG: KASAN: slab-out-of-bounds in ovl_dir_release+0x228/0x2a0 fs/overlayfs/readdir.c:1033 > Read of size 8 at addr ffff88816cd2c818 by task syz.0.17/5813 > > CPU: 0 UID: 0 PID: 5813 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) > Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 > Call Trace: > <TASK> > dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 > print_address_description+0x55/0x1e0 mm/kasan/report.c:378 > print_report+0x58/0x70 mm/kasan/report.c:482 > kasan_report+0x117/0x150 mm/kasan/report.c:595 > ovl_dir_release+0x228/0x2a0 fs/overlayfs/readdir.c:1033 > __fput+0x44f/0xa60 fs/file_table.c:510 > task_work_run+0x1d9/0x270 kernel/task_work.c:233 > resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] > __exit_to_user_mode_loop kernel/entry/common.c:67 [inline] > exit_to_user_mode_loop+0xf3/0x4d0 kernel/entry/common.c:98 > __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline] > syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline] > syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline] > do_syscall_64+0x33e/0xf80 arch/x86/entry/syscall_64.c:100 > entry_SYSCALL_64_after_hwframe+0x77/0x7f > RIP: 0033:0x7fe1a159ce59 > Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 > RSP: 002b:00007ffe25af07f8 EFLAGS: 00000246 ORIG_RAX: 00000000000001b4 > RAX: 0000000000000000 RBX: 00007ffe25af08e0 RCX: 00007fe1a159ce59 > RDX: 0000000000000000 RSI: 000000000000001e RDI: 0000000000000003 > RBP: 0000000000012e15 R08: 0000000000000001 R09: 0000000000000000 > R10: 0000001b32a20000 R11: 0000000000000246 R12: 00007ffe25af0920 > R13: 00007fe1a1815fac R14: 0000000000012e53 R15: 00007fe1a1815fa0 > </TASK> > > Allocated by task 5814: > kasan_save_stack mm/kasan/common.c:57 [inline] > kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 > poison_kmalloc_redzone mm/kasan/common.c:398 [inline] > __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415 > kasan_kmalloc include/linux/kasan.h:263 [inline] > __kmalloc_cache_noprof+0x31c/0x660 mm/slub.c:5419 > kmalloc_noprof include/linux/slab.h:950 [inline] > kzalloc_noprof include/linux/slab.h:1188 [inline] > ovl_file_alloc+0x4f/0x90 fs/overlayfs/file.c:99 > ovl_create_tmpfile fs/overlayfs/dir.c:1399 [inline] > ovl_tmpfile+0x3fc/0x7d0 fs/overlayfs/dir.c:1448 > vfs_tmpfile+0x3ff/0x890 fs/namei.c:4794 > do_tmpfile+0xd3/0x240 fs/namei.c:4859 > path_openat+0x33c7/0x3b40 fs/namei.c:4893 > do_file_open+0x23e/0x4a0 fs/namei.c:4931 > do_sys_openat2+0x113/0x200 fs/open.c:1367 > do_sys_open fs/open.c:1373 [inline] > __do_sys_open fs/open.c:1381 [inline] > __se_sys_open fs/open.c:1377 [inline] > __x64_sys_open+0x11e/0x150 fs/open.c:1377 > do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] > do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 > entry_SYSCALL_64_after_hwframe+0x77/0x7f > > The buggy address belongs to the object at ffff88816cd2c800 > which belongs to the cache kmalloc-16 of size 16 > The buggy address is located 8 bytes to the right of > allocated 16-byte region [ffff88816cd2c800, ffff88816cd2c810) > > The buggy address belongs to the physical page: > page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88816cd2c840 pfn:0x16cd2c > flags: 0x57ff00000000200(workingset|node=1|zone=2|lastcpupid=0x7ff) > page_type: f5(slab) > raw: 057ff00000000200 ffff888100041640 ffff888160400408 ffff888160400408 > raw: ffff88816cd2c840 0000000800800042 00000000f5000000 0000000000000000 > page dumped because: kasan: bad access detected > page_owner tracks the page as allocated > page last allocated via order 0, migratetype Unmovable, gfp_mask 0x252800(GFP_NOWAIT|__GFP_NORETRY|__GFP_COMP|__GFP_THISNODE), pid 5741, tgid 5741 (syz-executor), ts 77437404410, free_ts 77431264562 > set_page_owner include/linux/page_owner.h:32 [inline] > post_alloc_hook+0x231/0x280 mm/page_alloc.c:1858 > prep_new_page mm/page_alloc.c:1866 [inline] > get_page_from_freelist+0x24ba/0x2540 mm/page_alloc.c:3946 > __alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5226 > alloc_slab_page mm/slub.c:3278 [inline] > allocate_slab+0x77/0x660 mm/slub.c:3467 > new_slab mm/slub.c:3525 [inline] > ___slab_alloc+0x154/0x6c0 mm/slub.c:4444 > __slab_alloc_node mm/slub.c:4510 [inline] > slab_alloc_node mm/slub.c:4886 [inline] > __do_kmalloc_node mm/slub.c:5294 [inline] > __kvmalloc_node_noprof+0x34d/0x8a0 mm/slub.c:6832 > xt_jumpstack_alloc net/netfilter/x_tables.c:1449 [inline] > do_replace_table+0x191/0x620 net/netfilter/x_tables.c:1486 > xt_register_table+0x269/0x960 net/netfilter/x_tables.c:1596 > ip6t_register_table+0x16b/0x330 net/ipv6/netfilter/ip6_tables.c:1754 > ip6table_raw_table_init+0x54/0x80 net/ipv6/netfilter/ip6table_raw.c:48 > xt_find_table_lock+0x30c/0x3f0 net/netfilter/x_tables.c:1353 > xt_request_find_table_lock+0x26/0x100 net/netfilter/x_tables.c:1378 > get_info net/ipv6/netfilter/ip6_tables.c:979 [inline] > do_ip6t_get_ctl+0x716/0x1230 net/ipv6/netfilter/ip6_tables.c:1668 > nf_getsockopt+0x26e/0x290 net/netfilter/nf_sockopt.c:116 > ipv6_getsockopt+0x1fd/0x2b0 net/ipv6/ipv6_sockglue.c:1464 > do_sock_getsockopt+0x51d/0x7e0 net/socket.c:2487 > page last free pid 15 tgid 15 stack trace: > reset_page_owner include/linux/page_owner.h:25 [inline] > __free_pages_prepare mm/page_alloc.c:1402 [inline] > __free_frozen_pages+0xbc7/0xd30 mm/page_alloc.c:2943 > rcu_do_batch kernel/rcu/tree.c:2617 [inline] > rcu_core+0x7cd/0x1070 kernel/rcu/tree.c:2869 > handle_softirqs+0x22a/0x840 kernel/softirq.c:622 > run_ksoftirqd+0x36/0x60 kernel/softirq.c:1076 > smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 > kthread+0x389/0x470 kernel/kthread.c:436 > ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 > > Memory state around the buggy address: > ffff88816cd2c700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > ffff88816cd2c780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >ffff88816cd2c800: 00 00 fc fc 00 00 fc fc fc fc fc fc fc fc fc fc > ^ > ffff88816cd2c880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > ffff88816cd2c900: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > ================================================================== > > > *** > > general protection fault in path_openat > > tree: torvalds > URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux > base: 5200f5f493f79f14bbdc349e402a40dfb32f23c8 > arch: amd64 > compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 > config: https://ci.syzbot.org/builds/024b677f-50e7-4ef9-ae98-2652f5098bfd/config > syz repro: https://ci.syzbot.org/findings/4b3b0fe3-064c-43e2-b887-b3a52d87a16a/syz_repro > > BTRFS info (device loop1): enabling ssd optimizations > BTRFS info (device loop1): turning on async discard > BTRFS info (device loop1): enabling free space tree > BTRFS info (device loop1): use zstd compression, level 3 > Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN PTI > KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] > CPU: 1 UID: 0 PID: 5849 Comm: syz.1.18 Not tainted syzkaller #0 PREEMPT(full) > Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 > RIP: 0010:__d_entry_type include/linux/dcache.h:429 [inline] > RIP: 0010:d_can_lookup include/linux/dcache.h:444 [inline] > RIP: 0010:do_open fs/namei.c:4726 [inline] > RIP: 0010:path_openat+0x2e66/0x3b40 fs/namei.c:4902 > Code: e8 8f 49 7f ff eb 62 48 8b 44 24 78 42 80 3c 20 00 48 8b 5c 24 68 74 08 48 89 df e8 44 87 ea ff 4c 8b 3b 4c 89 f8 48 c1 e8 03 <42> 0f b6 04 20 84 c0 0f 85 cb 09 00 00 41 bc 00 00 38 00 45 23 27 > RSP: 0018:ffffc9000405f960 EFLAGS: 00010246 > RAX: 0000000000000000 RBX: ffffc9000405fc28 RCX: 0000000000000000 > RDX: ffff88810fe50000 RSI: 0000000000000002 RDI: 0000000000000000 > RBP: ffffc9000405fbb0 R08: ffff8881b949469b R09: 1ffff110372928d3 > R10: dffffc0000000000 R11: ffffed10372928d4 R12: dffffc0000000000 > R13: 1ffff1102eb65c88 R14: 000000000015d0c0 R15: 0000000000000001 > FS: 00007f074937f6c0(0000) GS:ffff8882a928a000(0000) knlGS:0000000000000000 > CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > CR2: 00000c2113360000 CR3: 0000000103f3a000 CR4: 00000000000006f0 > Call Trace: > <TASK> > do_file_open+0x23e/0x4a0 fs/namei.c:4931 > do_sys_openat2+0x113/0x200 fs/open.c:1367 > do_sys_open fs/open.c:1373 [inline] > __do_sys_openat fs/open.c:1389 [inline] > __se_sys_openat fs/open.c:1384 [inline] > __x64_sys_openat+0x138/0x170 fs/open.c:1384 > do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] > do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 > entry_SYSCALL_64_after_hwframe+0x77/0x7f > RIP: 0033:0x7f074859ce59 > Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 > RSP: 002b:00007f074937f028 EFLAGS: 00000246 ORIG_RAX: 0000000000000101 > RAX: ffffffffffffffda RBX: 00007f0748815fa0 RCX: 00007f074859ce59 > RDX: 00000000001dd0c0 RSI: 0000200000000240 RDI: ffffffffffffff9c > RBP: 00007f0748632d6f R08: 0000000000000000 R09: 0000000000000000 > R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 > R13: 00007f0748816038 R14: 00007f0748815fa0 R15: 00007fff34fee548 > </TASK> > Modules linked in: > ---[ end trace 0000000000000000 ]--- > RIP: 0010:__d_entry_type include/linux/dcache.h:429 [inline] > RIP: 0010:d_can_lookup include/linux/dcache.h:444 [inline] > RIP: 0010:do_open fs/namei.c:4726 [inline] > RIP: 0010:path_openat+0x2e66/0x3b40 fs/namei.c:4902 > Code: e8 8f 49 7f ff eb 62 48 8b 44 24 78 42 80 3c 20 00 48 8b 5c 24 68 74 08 48 89 df e8 44 87 ea ff 4c 8b 3b 4c 89 f8 48 c1 e8 03 <42> 0f b6 04 20 84 c0 0f 85 cb 09 00 00 41 bc 00 00 38 00 45 23 27 > RSP: 0018:ffffc9000405f960 EFLAGS: 00010246 > RAX: 0000000000000000 RBX: ffffc9000405fc28 RCX: 0000000000000000 > RDX: ffff88810fe50000 RSI: 0000000000000002 RDI: 0000000000000000 > RBP: ffffc9000405fbb0 R08: ffff8881b949469b R09: 1ffff110372928d3 > R10: dffffc0000000000 R11: ffffed10372928d4 R12: dffffc0000000000 > R13: 1ffff1102eb65c88 R14: 000000000015d0c0 R15: 0000000000000001 > FS: 00007f074937f6c0(0000) GS:ffff8882a928a000(0000) knlGS:0000000000000000 > CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > CR2: 00007f1c7a2f73b0 CR3: 0000000103f3a000 CR4: 00000000000006f0 > ---------------- > Code disassembly (best guess): > 0: e8 8f 49 7f ff call 0xff7f4994 > 5: eb 62 jmp 0x69 > 7: 48 8b 44 24 78 mov 0x78(%rsp),%rax > c: 42 80 3c 20 00 cmpb $0x0,(%rax,%r12,1) > 11: 48 8b 5c 24 68 mov 0x68(%rsp),%rbx > 16: 74 08 je 0x20 > 18: 48 89 df mov %rbx,%rdi > 1b: e8 44 87 ea ff call 0xffea8764 > 20: 4c 8b 3b mov (%rbx),%r15 > 23: 4c 89 f8 mov %r15,%rax > 26: 48 c1 e8 03 shr $0x3,%rax > * 2a: 42 0f b6 04 20 movzbl (%rax,%r12,1),%eax <-- trapping instruction > 2f: 84 c0 test %al,%al > 31: 0f 85 cb 09 00 00 jne 0xa02 > 37: 41 bc 00 00 38 00 mov $0x380000,%r12d > 3d: 45 23 27 and (%r15),%r12d > > > *** > > If these findings have caused you to resend the series or submit a > separate fix, please add the following tag to your commit message: > Tested-by: syzbot@syzkaller.appspotmail.com > > --- > This report is generated by a bot. It may contain errors. > syzbot ci engineers can be reached at syzkaller@googlegroups.com. > > To test a patch for this bug, please reply with `#syz test` > (should be on a separate line). > > The patch should be attached to the email. > Note: arguments like custom git repos and branches are not supported. ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) 2026-05-25 18:30 ` Jori Koolstra @ 2026-05-25 19:02 ` syzbot ci 0 siblings, 0 replies; 9+ messages in thread From: syzbot ci @ 2026-05-25 19:02 UTC (permalink / raw) To: jkoolstra, linux-fsdevel, linux-kernel, syzbot, syzkaller-bugs Cc: syzbot, syzkaller-bugs syzbot ci has tested the suggested fix patch on top of the following series: [v4] vfs: add O_CREAT|O_DIRECTORY to open*(2) https://lore.kernel.org/all/20260518165237.2084042-1-jkoolstra@xs4all.nl Patch: https://ci.syzbot.org/jobs/68cbe984-dc74-4a04-8f7f-f01909cf81f3/patch Testing results: * [build 0] Build Patched: passed * [build 0] Boot test: Patched: passed Full report is available here: https://ci.syzbot.org/session/3fd6614e-84f5-4903-b882-4f8cd45eeebc --- This report is generated by a bot. It may contain errors. syzbot ci engineers can be reached at syzkaller@googlegroups.com. ^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC PATCH v3 0/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) @ 2026-05-17 17:02 Jori Koolstra 2026-05-18 7:28 ` [syzbot ci] " syzbot ci 0 siblings, 1 reply; 9+ messages in thread From: Jori Koolstra @ 2026-05-17 17:02 UTC (permalink / raw) To: Alexander Viro, Christian Brauner, Jan Kara, Aleksa Sarai Cc: Jori Koolstra, linux-kernel, linux-fsdevel, cmirabil This series implements new semantics for the O_CREAT|O_DIRECTORY flag combination for open*(2): perform a mkdir and open the resulting directory, and return a pinning fd (which mkdir does not). Feedback on the v2 rfc of this patch was to not introduce a new syscall (mkdirat2) but implement this functionality as O_CREAT|O_DIRECTORY in open*(2). Two comments from me upfront: - This patch just EINVAL bans O_CREAT|O_DIRECTORY for filesystems that define atomic_open(). I figure it is better to (dis)allow on a fs per fs basis. So feedback per filesystem on what is the appropriate course of action on receiving O_CREAT|O_DIRECTORY would be very useful. - If we create a regular file with mknod, before creation security_path_mknod() is called, and after creation security_path_post_mknod(). If we create a regular file using O_CREAT (and this is also pre-patch) only security_path_mknod() is called. Is this the correct behaviour? Jori Koolstra (2): vfs: add O_CREAT|O_DIRECTORY to open*(2) selftest: add tests for open*(O_CREAT|O_DIRECTORY) fs/namei.c | 186 +++++++++++------- fs/open.c | 23 +-- include/uapi/asm-generic/fcntl.h | 2 + .../testing/selftests/filesystems/.gitignore | 1 + tools/testing/selftests/filesystems/Makefile | 4 +- tools/testing/selftests/filesystems/fclog.c | 1 + .../filesystems/open_o_creat_o_directory.c | 147 ++++++++++++++ 7 files changed, 283 insertions(+), 81 deletions(-) create mode 100644 tools/testing/selftests/filesystems/open_o_creat_o_directory.c -- 2.54.0 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) 2026-05-17 17:02 [RFC PATCH v3 0/2] " Jori Koolstra @ 2026-05-18 7:28 ` syzbot ci 0 siblings, 0 replies; 9+ messages in thread From: syzbot ci @ 2026-05-18 7:28 UTC (permalink / raw) To: brauner, cmirabil, cyphar, jack, jkoolstra, linux-fsdevel, linux-kernel, viro Cc: syzbot, syzkaller-bugs syzbot ci has tested the following series [v3] vfs: add O_CREAT|O_DIRECTORY to open*(2) https://lore.kernel.org/all/20260517170244.1832119-1-jkoolstra@xs4all.nl * [RFC PATCH v3 1/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) * [RFC PATCH v3 2/2] selftest: add tests for open*(O_CREAT|O_DIRECTORY) and found the following issues: * WARNING: lock held when returning to user space in filename_create * WARNING: lock held when returning to user space in start_creating * possible deadlock in mnt_want_write Full report is available here: https://ci.syzbot.org/series/6c2681e8-f8f3-4287-8f97-bd6ea26a767f *** WARNING: lock held when returning to user space in filename_create tree: torvalds URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux base: 6916d5703ddf9a38f1f6c2cc793381a24ee914c6 arch: amd64 compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 config: https://ci.syzbot.org/builds/7a0474b8-fd0f-4804-8833-2232742e06e3/config syz repro: https://ci.syzbot.org/findings/512f2832-177b-4b65-b835-bdff3e4402bc/syz_repro hpfs: hpfs_map_4sectors(): unaligned read hpfs: hpfs_map_4sectors(): unaligned read hpfs: filesystem error: unable to find root dir ================================================ WARNING: lock held when returning to user space! syzkaller #0 Not tainted ------------------------------------------------ syz.0.17/5814 is leaving the kernel with locks still held! 1 lock held by syz.0.17/5814: #0: ffff8881bab5b878 (&type->i_mutex_dir_key#8/1){+.+.}-{4:4}, at: inode_lock_nested include/linux/fs.h:1074 [inline] #0: ffff8881bab5b878 (&type->i_mutex_dir_key#8/1){+.+.}-{4:4}, at: __start_dirop fs/namei.c:2919 [inline] #0: ffff8881bab5b878 (&type->i_mutex_dir_key#8/1){+.+.}-{4:4}, at: start_dirop fs/namei.c:2943 [inline] #0: ffff8881bab5b878 (&type->i_mutex_dir_key#8/1){+.+.}-{4:4}, at: filename_create+0x200/0x370 fs/namei.c:4984 *** WARNING: lock held when returning to user space in start_creating tree: torvalds URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux base: 6916d5703ddf9a38f1f6c2cc793381a24ee914c6 arch: amd64 compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 config: https://ci.syzbot.org/builds/7a0474b8-fd0f-4804-8833-2232742e06e3/config syz repro: https://ci.syzbot.org/findings/7bdf5f73-8cce-4785-aa9e-dd89622cf613/syz_repro overlayfs: failed to create directory ./bus/work (errno: 126); mounting read-only ================================================ WARNING: lock held when returning to user space! syzkaller #0 Not tainted ------------------------------------------------ syz.1.18/5833 is leaving the kernel with locks still held! 1 lock held by syz.1.18/5833: #0: ffff8881ba0c4518 (&type->i_mutex_dir_key#3/1){+.+.}-{4:4}, at: inode_lock_nested include/linux/fs.h:1074 [inline] #0: ffff8881ba0c4518 (&type->i_mutex_dir_key#3/1){+.+.}-{4:4}, at: __start_dirop fs/namei.c:2919 [inline] #0: ffff8881ba0c4518 (&type->i_mutex_dir_key#3/1){+.+.}-{4:4}, at: start_dirop fs/namei.c:2943 [inline] #0: ffff8881ba0c4518 (&type->i_mutex_dir_key#3/1){+.+.}-{4:4}, at: start_creating+0xbe/0x100 fs/namei.c:3412 *** possible deadlock in mnt_want_write tree: torvalds URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux base: 6916d5703ddf9a38f1f6c2cc793381a24ee914c6 arch: amd64 compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 config: https://ci.syzbot.org/builds/7a0474b8-fd0f-4804-8833-2232742e06e3/config syz repro: https://ci.syzbot.org/findings/6d4f386f-fb66-4862-b644-4ac19c79f6a3/syz_repro ====================================================== WARNING: possible circular locking dependency detected syzkaller #0 Not tainted ------------------------------------------------------ syz.0.17/5836 is trying to acquire lock: ffff888118cba410 (sb_writers#12){.+.+}-{0:0}, at: mnt_want_write+0x41/0x90 fs/namespace.c:493 but task is already holding lock: ffff88811f1627e0 (&ovl_i_mutex_dir_key[depth]/1){+.+.}-{4:4}, at: inode_lock_nested include/linux/fs.h:1074 [inline] ffff88811f1627e0 (&ovl_i_mutex_dir_key[depth]/1){+.+.}-{4:4}, at: __start_dirop fs/namei.c:2919 [inline] ffff88811f1627e0 (&ovl_i_mutex_dir_key[depth]/1){+.+.}-{4:4}, at: start_dirop fs/namei.c:2943 [inline] ffff88811f1627e0 (&ovl_i_mutex_dir_key[depth]/1){+.+.}-{4:4}, at: filename_create+0x200/0x370 fs/namei.c:4984 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&ovl_i_mutex_dir_key[depth]/1){+.+.}-{4:4}: down_write_nested+0x9d/0x210 kernel/locking/rwsem.c:1751 inode_lock_nested include/linux/fs.h:1074 [inline] __start_dirop fs/namei.c:2919 [inline] start_dirop fs/namei.c:2943 [inline] filename_unlinkat+0x2a7/0x610 fs/namei.c:5599 __do_sys_unlink fs/namei.c:5653 [inline] __se_sys_unlink+0x2e/0x140 fs/namei.c:5650 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f -> #0 (sb_writers#12){.+.+}-{0:0}: check_prev_add kernel/locking/lockdep.c:3165 [inline] check_prevs_add kernel/locking/lockdep.c:3284 [inline] validate_chain kernel/locking/lockdep.c:3908 [inline] __lock_acquire+0x15a5/0x2cf0 kernel/locking/lockdep.c:5237 lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868 percpu_down_read_internal include/linux/percpu-rwsem.h:53 [inline] percpu_down_read_freezable include/linux/percpu-rwsem.h:83 [inline] __sb_start_write include/linux/fs/super.h:19 [inline] sb_start_write+0x4d/0x1c0 include/linux/fs/super.h:125 mnt_want_write+0x41/0x90 fs/namespace.c:493 filename_create+0x154/0x370 fs/namei.c:4977 filename_mkdirat+0xd2/0x510 fs/namei.c:5337 __do_sys_mkdirat fs/namei.c:5365 [inline] __se_sys_mkdirat+0x35/0x150 fs/namei.c:5362 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&ovl_i_mutex_dir_key[depth]/1); lock(sb_writers#12); lock(&ovl_i_mutex_dir_key[depth]/1); rlock(sb_writers#12); *** DEADLOCK *** 1 lock held by syz.0.17/5836: #0: ffff88811f1627e0 (&ovl_i_mutex_dir_key[depth]/1){+.+.}-{4:4}, at: inode_lock_nested include/linux/fs.h:1074 [inline] #0: ffff88811f1627e0 (&ovl_i_mutex_dir_key[depth]/1){+.+.}-{4:4}, at: __start_dirop fs/namei.c:2919 [inline] #0: ffff88811f1627e0 (&ovl_i_mutex_dir_key[depth]/1){+.+.}-{4:4}, at: start_dirop fs/namei.c:2943 [inline] #0: ffff88811f1627e0 (&ovl_i_mutex_dir_key[depth]/1){+.+.}-{4:4}, at: filename_create+0x200/0x370 fs/namei.c:4984 stack backtrace: CPU: 0 UID: 0 PID: 5836 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 Call Trace: <TASK> dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_circular_bug+0x2e1/0x300 kernel/locking/lockdep.c:2043 check_noncircular+0x12e/0x150 kernel/locking/lockdep.c:2175 check_prev_add kernel/locking/lockdep.c:3165 [inline] check_prevs_add kernel/locking/lockdep.c:3284 [inline] validate_chain kernel/locking/lockdep.c:3908 [inline] __lock_acquire+0x15a5/0x2cf0 kernel/locking/lockdep.c:5237 lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868 percpu_down_read_internal include/linux/percpu-rwsem.h:53 [inline] percpu_down_read_freezable include/linux/percpu-rwsem.h:83 [inline] __sb_start_write include/linux/fs/super.h:19 [inline] sb_start_write+0x4d/0x1c0 include/linux/fs/super.h:125 mnt_want_write+0x41/0x90 fs/namespace.c:493 filename_create+0x154/0x370 fs/namei.c:4977 filename_mkdirat+0xd2/0x510 fs/namei.c:5337 __do_sys_mkdirat fs/namei.c:5365 [inline] __se_sys_mkdirat+0x35/0x150 fs/namei.c:5362 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f2f5e99ce59 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007f2f5f771028 EFLAGS: 00000246 ORIG_RAX: 0000000000000102 RAX: ffffffffffffffda RBX: 00007f2f5ec15fa0 RCX: 00007f2f5e99ce59 RDX: 0000000000000010 RSI: 0000200000002040 RDI: ffffffffffffff9c RBP: 00007f2f5ea32d6f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f2f5ec16038 R14: 00007f2f5ec15fa0 R15: 00007ffe96c961d8 </TASK> *** If these findings have caused you to resend the series or submit a separate fix, please add the following tag to your commit message: Tested-by: syzbot@syzkaller.appspotmail.com --- This report is generated by a bot. It may contain errors. syzbot ci engineers can be reached at syzkaller@googlegroups.com. To test a patch for this bug, please reply with `#syz test` (should be on a separate line). The patch should be attached to the email. Note: arguments like custom git repos and branches are not supported. ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-05-25 19:02 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-18 16:52 [RFC PATCH v4 0/2] vfs: add O_CREAT|O_DIRECTORY to open*(2) Jori Koolstra 2026-05-18 16:52 ` [RFC PATCH v4 1/2] " Jori Koolstra 2026-05-18 16:52 ` [RFC PATCH v4 2/2] selftest: add tests for open*(O_CREAT|O_DIRECTORY) Jori Koolstra 2026-05-19 6:59 ` [syzbot ci] Re: vfs: add O_CREAT|O_DIRECTORY to open*(2) syzbot ci 2026-05-25 10:35 ` Jori Koolstra 2026-05-25 11:39 ` syzbot ci 2026-05-25 18:30 ` Jori Koolstra 2026-05-25 19:02 ` syzbot ci -- strict thread matches above, loose matches on Subject: below -- 2026-05-17 17:02 [RFC PATCH v3 0/2] " Jori Koolstra 2026-05-18 7:28 ` [syzbot ci] " syzbot ci
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox