* [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
* [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
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