public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [syzbot] [gfs2?] WARNING in chown_common
@ 2025-10-12  5:45 syzbot
  2025-10-17  8:57 ` Forwarded: [PATCH] fs: fix stale inode access in chown_common() retry path syzbot
                   ` (10 more replies)
  0 siblings, 11 replies; 16+ messages in thread
From: syzbot @ 2025-10-12  5:45 UTC (permalink / raw)
  To: brauner, gfs2, jack, linux-fsdevel, linux-kernel, syzkaller-bugs,
	viro

Hello,

syzbot found the following issue on:

HEAD commit:    98906f9d850e Merge tag 'rtc-6.18' of git://git.kernel.org/..
git tree:       upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=10e10c58580000
kernel config:  https://syzkaller.appspot.com/x/.config?x=c2d7b4143707d3a0
dashboard link: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
compiler:       Debian clang version 20.1.8 (++20250708063551+0c9f909b7976-1~exp1~20250708183702.136), Debian LLD 20.1.8
syz repro:      https://syzkaller.appspot.com/x/repro.syz?x=11ab9b34580000
C reproducer:   https://syzkaller.appspot.com/x/repro.c?x=14e10c58580000

Downloadable assets:
disk image (non-bootable): https://storage.googleapis.com/syzbot-assets/d900f083ada3/non_bootable_disk-98906f9d.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/d82186923244/vmlinux-98906f9d.xz
kernel image: https://storage.googleapis.com/syzbot-assets/a23e980d2d8e/bzImage-98906f9d.xz
mounted in repro #1: https://storage.googleapis.com/syzbot-assets/b2d6dc77aff3/mount_2.gz
  fsck result: OK (log: https://syzkaller.appspot.com/x/fsck.log?x=13e03892580000)
mounted in repro #2: https://storage.googleapis.com/syzbot-assets/96cd0ec46a20/mount_8.gz

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com

DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) && !rwsem_test_oflags(sem, RWSEM_NONSPINNABLE)): count = 0x0, magic = 0xffff888036665058, owner = 0x0, curr 0xffff88803e332480, list empty
WARNING: CPU: 0 PID: 5699 at kernel/locking/rwsem.c:1381 __up_write kernel/locking/rwsem.c:1380 [inline]
WARNING: CPU: 0 PID: 5699 at kernel/locking/rwsem.c:1381 up_write+0x3a2/0x420 kernel/locking/rwsem.c:1643
Modules linked in:
CPU: 0 UID: 0 PID: 5699 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014
RIP: 0010:__up_write kernel/locking/rwsem.c:1380 [inline]
RIP: 0010:up_write+0x3a2/0x420 kernel/locking/rwsem.c:1643
Code: d0 48 c7 c7 20 ff 6a 8b 48 c7 c6 40 01 6b 8b 48 8b 14 24 4c 89 f1 4d 89 e0 4c 8b 4c 24 08 41 52 e8 b3 36 e6 ff 48 83 c4 08 90 <0f> 0b 90 90 e9 6d fd ff ff 48 c7 c1 94 61 9e 8f 80 e1 07 80 c1 03
RSP: 0018:ffffc9000d4b7c30 EFLAGS: 00010296
RAX: e0ff97a6af656400 RBX: ffff888036665058 RCX: ffff88803e332480
RDX: 0000000000000000 RSI: 0000000000000001 RDI: 0000000000000002
RBP: dffffc0000000000 R08: 0000000000000003 R09: 0000000000000004
R10: dffffc0000000000 R11: fffffbfff1bfa650 R12: 0000000000000000
R13: ffff8880366650b0 R14: ffff888036665058 R15: 1ffff11006ccca0c
FS:  00007f2b1bd9b6c0(0000) GS:ffff88808d301000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f63d120f000 CR3: 000000004fe06000 CR4: 0000000000352ef0
Call Trace:
 <TASK>
 inode_unlock include/linux/fs.h:990 [inline]
 chown_common+0x418/0x5c0 fs/open.c:793
 do_fchownat+0x161/0x270 fs/open.c:822
 __do_sys_lchown fs/open.c:847 [inline]
 __se_sys_lchown fs/open.c:845 [inline]
 __x64_sys_lchown+0x85/0xa0 fs/open.c:845
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0xfa/0xfa0 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f2b1c78eec9
Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 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 a8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f2b1bd9b038 EFLAGS: 00000246 ORIG_RAX: 000000000000005e
RAX: ffffffffffffffda RBX: 00007f2b1c9e6360 RCX: 00007f2b1c78eec9
RDX: 000000000000ee01 RSI: 0000000000000000 RDI: 00002000000006c0
RBP: 00007f2b1c811f91 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f2b1c9e63f8 R14: 00007f2b1c9e6360 R15: 00007ffe60c7e2c8
 </TASK>


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title

If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.

If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)

If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report

If you want to undo deduplication, reply with:
#syz undup

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix stale inode access in chown_common() retry path
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
  2025-10-17  8:57 ` Forwarded: [PATCH] fs: fix stale inode access in chown_common() retry path syzbot
@ 2025-10-17  8:57 ` syzbot
  2025-10-29  8:23 ` Forwarded: Test patch for [syzbot] [gfs2?] WARNING in chown_common syzbot
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-10-17  8:57 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix stale inode access in chown_common() retry path
Author: kartikey406@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

When chown_common() retries due to delegation, it reuses the inode
pointer fetched before the retry_deleg label. However, break_deleg_wait()
can cause the dentry to be updated, making path->dentry->d_inode point
to a different inode. The stale inode pointer leads to locking/unlocking
an invalid or freed inode, triggering a rwsem warning.

The issue manifests as:
  WARNING: CPU: 0 PID: 5699 at kernel/locking/rwsem.c:1381 up_write+0x3a2/0x420
  DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) &&
                       !rwsem_test_oflags(sem, RWSEM_NONSPINNABLE)):
  count = 0x0, owner = 0x0

Fix by refreshing both the inode pointer and its associated filesystem
user namespace on each retry iteration, ensuring operations are always
performed on the current, valid inode.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/open.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..8672072fb4c1 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -755,7 +755,7 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 {
 	struct mnt_idmap *idmap;
 	struct user_namespace *fs_userns;
-	struct inode *inode = path->dentry->d_inode;
+	struct inode *inode;
 	struct inode *delegated_inode = NULL;
 	int error;
 	struct iattr newattrs;
@@ -766,9 +766,10 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 	gid = make_kgid(current_user_ns(), group);
 
 	idmap = mnt_idmap(path->mnt);
-	fs_userns = i_user_ns(inode);
 
 retry_deleg:
+	inode = path->dentry->d_inode;
+	fs_userns = i_user_ns(inode);
 	newattrs.ia_vfsuid = INVALID_VFSUID;
 	newattrs.ia_vfsgid = INVALID_VFSGID;
 	newattrs.ia_valid =  ATTR_CTIME;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix stale inode access in chown_common() retry path
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
@ 2025-10-17  8:57 ` syzbot
  2025-10-17  8:57 ` syzbot
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-10-17  8:57 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix stale inode access in chown_common() retry path
Author: kartikey406@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

When chown_common() retries due to delegation, it reuses the inode
pointer fetched before the retry_deleg label. However, break_deleg_wait()
can cause the dentry to be updated, making path->dentry->d_inode point
to a different inode. The stale inode pointer leads to locking/unlocking
an invalid or freed inode, triggering a rwsem warning.

The issue manifests as:
  WARNING: CPU: 0 PID: 5699 at kernel/locking/rwsem.c:1381 up_write+0x3a2/0x420
  DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) &&
                       !rwsem_test_oflags(sem, RWSEM_NONSPINNABLE)):
  count = 0x0, owner = 0x0

Fix by refreshing both the inode pointer and its associated filesystem
user namespace on each retry iteration, ensuring operations are always
performed on the current, valid inode.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/open.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..8672072fb4c1 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -755,7 +755,7 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 {
 	struct mnt_idmap *idmap;
 	struct user_namespace *fs_userns;
-	struct inode *inode = path->dentry->d_inode;
+	struct inode *inode;
 	struct inode *delegated_inode = NULL;
 	int error;
 	struct iattr newattrs;
@@ -766,9 +766,10 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 	gid = make_kgid(current_user_ns(), group);
 
 	idmap = mnt_idmap(path->mnt);
-	fs_userns = i_user_ns(inode);
 
 retry_deleg:
+	inode = path->dentry->d_inode;
+	fs_userns = i_user_ns(inode);
 	newattrs.ia_vfsuid = INVALID_VFSUID;
 	newattrs.ia_vfsgid = INVALID_VFSGID;
 	newattrs.ia_valid =  ATTR_CTIME;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Forwarded: Test patch for [syzbot] [gfs2?] WARNING in chown_common
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
  2025-10-17  8:57 ` Forwarded: [PATCH] fs: fix stale inode access in chown_common() retry path syzbot
  2025-10-17  8:57 ` syzbot
@ 2025-10-29  8:23 ` syzbot
  2025-11-09  5:02 ` Forwarded: [PATCH] fs: fix inode reference leak in chown_common delegation retry path syzbot
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-10-29  8:23 UTC (permalink / raw)
  To: linux-kernel

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org.

***

Subject: Test patch for [syzbot] [gfs2?] WARNING in chown_common
Author: vnranganath.20@gmail.com

#syz test

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix inode reference leak in chown_common delegation retry path
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
                   ` (2 preceding siblings ...)
  2025-10-29  8:23 ` Forwarded: Test patch for [syzbot] [gfs2?] WARNING in chown_common syzbot
@ 2025-11-09  5:02 ` syzbot
  2025-11-09  9:08 ` Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry syzbot
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-11-09  5:02 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix inode reference leak in chown_common delegation retry path
Author: kartikey406@gmail.com

#syz test git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

The chown_common() function can trigger a use-after-free when retrying
after breaking a file delegation. The issue occurs because break_deleg_wait()
calls iput() on the delegated inode, potentially freeing it if this was the
last reference. However, chown_common() continues to use the stale inode
pointer on retry, leading to operations on a freed or reused inode.

This manifests as a rwsem warning:
  DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) &&
                       !rwsem_test_oflags(sem, RWSEM_NONSPINNABLE))
  owner = 0x0

The warning occurs because the inode's rwsem is in an invalid state after
the inode has been freed or reused by another thread.

The bug is particularly reproducible on GFS2 filesystem where delegations
are more common due to its clustered nature, and can be triggered by
concurrent fchownat() calls on the same file.

Fix this by:
1. Re-fetching the inode pointer from path->dentry->d_inode on each
   iteration at the retry_deleg label
2. Holding an explicit reference with ihold() at the start of each iteration
3. Releasing the reference with iput() on all exit paths, including before
   the retry

This ensures the inode remains valid throughout the delegation break and
retry sequence, preventing use-after-free.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/open.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..a564f05c0c91 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -755,7 +755,7 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 {
 	struct mnt_idmap *idmap;
 	struct user_namespace *fs_userns;
-	struct inode *inode = path->dentry->d_inode;
+	struct inode *inode;
 	struct inode *delegated_inode = NULL;
 	int error;
 	struct iattr newattrs;
@@ -766,19 +766,27 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 	gid = make_kgid(current_user_ns(), group);
 
 	idmap = mnt_idmap(path->mnt);
-	fs_userns = i_user_ns(inode);
 
 retry_deleg:
+	inode = path->dentry->d_inode;
+	ihold(inode);
+	fs_userns = i_user_ns(inode);
 	newattrs.ia_vfsuid = INVALID_VFSUID;
 	newattrs.ia_vfsgid = INVALID_VFSGID;
 	newattrs.ia_valid =  ATTR_CTIME;
-	if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid))
+	if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid)) {
+		iput(inode);
 		return -EINVAL;
-	if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid))
+	}
+	if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid)) {
+		iput(inode);
 		return -EINVAL;
+	}
 	error = inode_lock_killable(inode);
-	if (error)
+	if (error) {
+		iput(inode);
 		return error;
+	}
 	if (!S_ISDIR(inode->i_mode))
 		newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV |
 				     setattr_should_drop_sgid(idmap, inode);
@@ -793,9 +801,12 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 	inode_unlock(inode);
 	if (delegated_inode) {
 		error = break_deleg_wait(&delegated_inode);
-		if (!error)
+		if (!error) {
+			iput(inode);
 			goto retry_deleg;
+		}
 	}
+	iput(inode);
 	return error;
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
                   ` (3 preceding siblings ...)
  2025-11-09  5:02 ` Forwarded: [PATCH] fs: fix inode reference leak in chown_common delegation retry path syzbot
@ 2025-11-09  9:08 ` syzbot
  2025-11-09 11:05   ` kernel test robot
  2025-11-09 12:17   ` kernel test robot
  2025-11-09 11:40 ` syzbot
                   ` (5 subsequent siblings)
  10 siblings, 2 replies; 16+ messages in thread
From: syzbot @ 2025-11-09  9:08 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Author: kartikey406@gmail.com

#syz test git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master


The chown_common() function has a use-after-free bug in its delegation
retry path. When break_deleg_wait() is called, it internally calls
iput() on the delegated inode, potentially freeing it if this was the
last reference. However, chown_common() continues using the stale inode
pointer on retry, leading to operations on freed memory.

This manifests as a rwsem warning where the inode's rwsem owner field
is corrupted:
  DEBUG_RWSEMS_WARN_ON: owner = 0x0

The bug is triggered by concurrent fchownat() calls and is reproducible
on GFS2 filesystems where delegations are common.

Fix by:
1. Re-fetching inode from path->dentry->d_inode on each retry iteration
2. Holding an explicit inode reference with ihold() at iteration start
3. Releasing the reference with iput() on all exit paths

This ensures the inode remains valid throughout delegation break and retry.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/open.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..bbd73984292d 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -769,6 +769,9 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 	fs_userns = i_user_ns(inode);
 
 retry_deleg:
+	printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
+	       current->comm, inode, atomic_read(&inode->i_count),
+	       atomic_long_read(&inode->i_rwsem.owner));
 	newattrs.ia_vfsuid = INVALID_VFSUID;
 	newattrs.ia_vfsgid = INVALID_VFSGID;
 	newattrs.ia_valid =  ATTR_CTIME;
@@ -776,9 +779,13 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 		return -EINVAL;
 	if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid))
 		return -EINVAL;
+	printk("DEBUG: [%s] before inode_lock: inode=%p, i_count=%d\n",
+	       current->comm, inode, atomic_read(&inode->i_count));
 	error = inode_lock_killable(inode);
 	if (error)
 		return error;
+	printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
+	       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), current);
 	if (!S_ISDIR(inode->i_mode))
 		newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV |
 				     setattr_should_drop_sgid(idmap, inode);
@@ -790,9 +797,17 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 	if (!error)
 		error = notify_change(idmap, path->dentry, &newattrs,
 				      &delegated_inode);
+	printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
+	       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), delegated_inode);
 	inode_unlock(inode);
+	printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
+	       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner));
 	if (delegated_inode) {
+		printk("DEBUG: [%s] calling break_deleg_wait: inode=%p, i_count=%d, delegated_inode=%p\n",
+		       current->comm, inode, atomic_read(&inode->i_count), delegated_inode);
 		error = break_deleg_wait(&delegated_inode);
+		printk("DEBUG: [%s] after break_deleg_wait: inode=%p, i_count=%d, error=%d\n",
+		       current->comm, inode, atomic_read(&inode->i_count), error);
 		if (!error)
 			goto retry_deleg;
 	}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-11-09  9:08 ` Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry syzbot
@ 2025-11-09 11:05   ` kernel test robot
  2025-11-09 12:26     ` Philip Li
  2025-11-09 12:17   ` kernel test robot
  1 sibling, 1 reply; 16+ messages in thread
From: kernel test robot @ 2025-11-09 11:05 UTC (permalink / raw)
  To: syzbot, linux-kernel, syzkaller-bugs; +Cc: llvm, oe-kbuild-all

Hi syzbot,

kernel test robot noticed the following build warnings:

[auto build test WARNING on brauner-vfs/vfs.all]
[also build test WARNING on linus/master v6.18-rc4 next-20251107]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/syzbot/Forwarded-PATCH-fs-fix-inode-use-after-free-in-chown_common-delegation-retry/20251109-171000
base:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git vfs.all
patch link:    https://lore.kernel.org/r/691059ff.a70a0220.22f260.00a6.GAE%40google.com
patch subject: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
config: arm-allnoconfig (https://download.01.org/0day-ci/archive/20251109/202511091815.6q5WUuzH-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project b9ea93cd5c37fb6d606502fd01208dd48330549d)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251109/202511091815.6q5WUuzH-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511091815.6q5WUuzH-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> fs/open.c:771:9: warning: format specifies type 'void *' but the argument has type 'long' [-Wformat]
     769 |         printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
         |                                                                              ~~
         |                                                                              %ld
     770 |                current->comm, inode, atomic_read(&inode->i_count),
     771 |                atomic_long_read(&inode->i_rwsem.owner));
         |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/printk.h:512:60: note: expanded from macro 'printk'
     512 | #define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
         |                                                     ~~~    ^~~~~~~~~~~
   include/linux/printk.h:484:19: note: expanded from macro 'printk_index_wrap'
     484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
         |                         ~~~~    ^~~~~~~~~~~
   fs/open.c:785:31: warning: format specifies type 'void *' but the argument has type 'long' [-Wformat]
     784 |         printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
         |                                                                       ~~
         |                                                                       %ld
     785 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), current);
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/printk.h:512:60: note: expanded from macro 'printk'
     512 | #define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
         |                                                     ~~~    ^~~~~~~~~~~
   include/linux/printk.h:484:19: note: expanded from macro 'printk_index_wrap'
     484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
         |                         ~~~~    ^~~~~~~~~~~
   fs/open.c:798:31: warning: format specifies type 'void *' but the argument has type 'long' [-Wformat]
     797 |         printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
         |                                                                          ~~
         |                                                                          %ld
     798 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), delegated_inode);
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/printk.h:512:60: note: expanded from macro 'printk'
     512 | #define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
         |                                                     ~~~    ^~~~~~~~~~~
   include/linux/printk.h:484:19: note: expanded from macro 'printk_index_wrap'
     484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
         |                         ~~~~    ^~~~~~~~~~~
   fs/open.c:801:31: warning: format specifies type 'void *' but the argument has type 'long' [-Wformat]
     800 |         printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
         |                                                                         ~~
         |                                                                         %ld
     801 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner));
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/printk.h:512:60: note: expanded from macro 'printk'
     512 | #define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
         |                                                     ~~~    ^~~~~~~~~~~
   include/linux/printk.h:484:19: note: expanded from macro 'printk_index_wrap'
     484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
         |                         ~~~~    ^~~~~~~~~~~
   4 warnings generated.


vim +771 fs/open.c

   750	
   751	int chown_common(const struct path *path, uid_t user, gid_t group)
   752	{
   753		struct mnt_idmap *idmap;
   754		struct user_namespace *fs_userns;
   755		struct inode *inode = path->dentry->d_inode;
   756		struct inode *delegated_inode = NULL;
   757		int error;
   758		struct iattr newattrs;
   759		kuid_t uid;
   760		kgid_t gid;
   761	
   762		uid = make_kuid(current_user_ns(), user);
   763		gid = make_kgid(current_user_ns(), group);
   764	
   765		idmap = mnt_idmap(path->mnt);
   766		fs_userns = i_user_ns(inode);
   767	
   768	retry_deleg:
   769		printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
   770		       current->comm, inode, atomic_read(&inode->i_count),
 > 771		       atomic_long_read(&inode->i_rwsem.owner));
   772		newattrs.ia_vfsuid = INVALID_VFSUID;
   773		newattrs.ia_vfsgid = INVALID_VFSGID;
   774		newattrs.ia_valid =  ATTR_CTIME;
   775		if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid))
   776			return -EINVAL;
   777		if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid))
   778			return -EINVAL;
   779		printk("DEBUG: [%s] before inode_lock: inode=%p, i_count=%d\n",
   780		       current->comm, inode, atomic_read(&inode->i_count));
   781		error = inode_lock_killable(inode);
   782		if (error)
   783			return error;
   784		printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
   785		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), current);
   786		if (!S_ISDIR(inode->i_mode))
   787			newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV |
   788					     setattr_should_drop_sgid(idmap, inode);
   789		/* Continue to send actual fs values, not the mount values. */
   790		error = security_path_chown(
   791			path,
   792			from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid),
   793			from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid));
   794		if (!error)
   795			error = notify_change(idmap, path->dentry, &newattrs,
   796					      &delegated_inode);
   797		printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
   798		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), delegated_inode);
   799		inode_unlock(inode);
   800		printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
   801		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner));
   802		if (delegated_inode) {
   803			printk("DEBUG: [%s] calling break_deleg_wait: inode=%p, i_count=%d, delegated_inode=%p\n",
   804			       current->comm, inode, atomic_read(&inode->i_count), delegated_inode);
   805			error = break_deleg_wait(&delegated_inode);
   806			printk("DEBUG: [%s] after break_deleg_wait: inode=%p, i_count=%d, error=%d\n",
   807			       current->comm, inode, atomic_read(&inode->i_count), error);
   808			if (!error)
   809				goto retry_deleg;
   810		}
   811		return error;
   812	}
   813	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
                   ` (4 preceding siblings ...)
  2025-11-09  9:08 ` Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry syzbot
@ 2025-11-09 11:40 ` syzbot
  2025-11-09 11:57 ` syzbot
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-11-09 11:40 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Author: kartikey406@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git master

The chown_common() function has a use-after-free bug in its delegation
retry path. When break_deleg_wait() is called, it internally calls
iput() on the delegated inode, potentially freeing it if this was the
last reference. However, chown_common() continues using the stale inode
pointer on retry, leading to operations on freed memory.

This manifests as a rwsem warning where the inode's rwsem owner field
is corrupted:
  DEBUG_RWSEMS_WARN_ON: owner = 0x0

The bug is triggered by concurrent fchownat() calls and is reproducible
on GFS2 filesystems where delegations are common.

Fix by:
1. Re-fetching inode from path->dentry->d_inode on each retry iteration
2. Holding an explicit inode reference with ihold() at iteration start
3. Releasing the reference with iput() on all exit paths

This ensures the inode remains valid throughout delegation break and
retry.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/open.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..9d0f88ce56f9 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -790,6 +790,14 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 	if (!error)
 		error = notify_change(idmap, path->dentry, &newattrs,
 				      &delegated_inode);
+	if (atomic_long_read(&inode->i_rwsem.owner) != (long)current) {
+		printk(KERN_ERR "BUG: About to unlock rwsem we don't own!\n");
+		printk(KERN_ERR "  inode=%p\n", inode);
+		printk(KERN_ERR "  i_rwsem.owner=%lx\n", atomic_long_read(&inode->i_rwsem.owner));
+		printk(KERN_ERR "  current=%p\n", current);
+		printk(KERN_ERR "  delegated_inode=%p\n", delegated_inode);
+		dump_stack();
+	}
 	inode_unlock(inode);
 	if (delegated_inode) {
 		error = break_deleg_wait(&delegated_inode);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
                   ` (5 preceding siblings ...)
  2025-11-09 11:40 ` syzbot
@ 2025-11-09 11:57 ` syzbot
  2025-11-09 12:25 ` syzbot
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-11-09 11:57 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Author: kartikey406@gmail.com

#syz test git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master


The chown_common() function has a use-after-free bug in its delegation
retry path. When break_deleg_wait() is called, it internally calls
iput() on the delegated inode, potentially freeing it if this was the
last reference. However, chown_common() continues using the stale inode
pointer on retry, leading to operations on freed memory.

This manifests as a rwsem warning where the inode's rwsem owner field
is corrupted:
  DEBUG_RWSEMS_WARN_ON: owner = 0x0

The bug is triggered by concurrent fchownat() calls and is reproducible
on GFS2 filesystems where delegations are common.

Fix by:
1. Re-fetching inode from path->dentry->d_inode on each retry iteration
2. Holding an explicit inode reference with ihold() at iteration start
3. Releasing the reference with iput() on all exit paths

This ensures the inode remains valid throughout delegation break and
retry.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/open.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..9d0f88ce56f9 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -790,6 +790,14 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 	if (!error)
 		error = notify_change(idmap, path->dentry, &newattrs,
 				      &delegated_inode);
+	if (atomic_long_read(&inode->i_rwsem.owner) != (long)current) {
+		printk(KERN_ERR "BUG: About to unlock rwsem we don't own!\n");
+		printk(KERN_ERR "  inode=%p\n", inode);
+		printk(KERN_ERR "  i_rwsem.owner=%lx\n", atomic_long_read(&inode->i_rwsem.owner));
+		printk(KERN_ERR "  current=%p\n", current);
+		printk(KERN_ERR "  delegated_inode=%p\n", delegated_inode);
+		dump_stack();
+	}
 	inode_unlock(inode);
 	if (delegated_inode) {
 		error = break_deleg_wait(&delegated_inode);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-11-09  9:08 ` Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry syzbot
  2025-11-09 11:05   ` kernel test robot
@ 2025-11-09 12:17   ` kernel test robot
  2025-11-09 12:26     ` Philip Li
  1 sibling, 1 reply; 16+ messages in thread
From: kernel test robot @ 2025-11-09 12:17 UTC (permalink / raw)
  To: syzbot, linux-kernel, syzkaller-bugs; +Cc: oe-kbuild-all

Hi syzbot,

kernel test robot noticed the following build warnings:

[auto build test WARNING on brauner-vfs/vfs.all]
[also build test WARNING on linus/master v6.18-rc4 next-20251107]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/syzbot/Forwarded-PATCH-fs-fix-inode-use-after-free-in-chown_common-delegation-retry/20251109-171000
base:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git vfs.all
patch link:    https://lore.kernel.org/r/691059ff.a70a0220.22f260.00a6.GAE%40google.com
patch subject: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
config: i386-allnoconfig (https://download.01.org/0day-ci/archive/20251109/202511091831.tPcsumuB-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251109/202511091831.tPcsumuB-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511091831.tPcsumuB-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from include/asm-generic/bug.h:22,
                    from arch/x86/include/asm/bug.h:108,
                    from include/linux/bug.h:5,
                    from include/linux/mmdebug.h:5,
                    from include/linux/mm.h:6,
                    from fs/open.c:9:
   fs/open.c: In function 'chown_common':
>> fs/open.c:769:16: warning: format '%p' expects argument of type 'void *', but argument 5 has type 'long int' [-Wformat=]
     769 |         printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
         |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     770 |                current->comm, inode, atomic_read(&inode->i_count),
     771 |                atomic_long_read(&inode->i_rwsem.owner));
         |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |                |
         |                long int
   include/linux/printk.h:484:25: note: in definition of macro 'printk_index_wrap'
     484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
         |                         ^~~~
   fs/open.c:769:9: note: in expansion of macro 'printk'
     769 |         printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
         |         ^~~~~~
   fs/open.c:769:79: note: format string is defined here
     769 |         printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
         |                                                                              ~^
         |                                                                               |
         |                                                                               void *
         |                                                                              %ld
   fs/open.c:784:16: warning: format '%p' expects argument of type 'void *', but argument 4 has type 'long int' [-Wformat=]
     784 |         printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
         |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     785 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), current);
         |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |                                      |
         |                                      long int
   include/linux/printk.h:484:25: note: in definition of macro 'printk_index_wrap'
     484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
         |                         ^~~~
   fs/open.c:784:9: note: in expansion of macro 'printk'
     784 |         printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
         |         ^~~~~~
   fs/open.c:784:72: note: format string is defined here
     784 |         printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
         |                                                                       ~^
         |                                                                        |
         |                                                                        void *
         |                                                                       %ld
   fs/open.c:797:16: warning: format '%p' expects argument of type 'void *', but argument 4 has type 'long int' [-Wformat=]
     797 |         printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
         |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     798 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), delegated_inode);
         |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |                                      |
         |                                      long int
   include/linux/printk.h:484:25: note: in definition of macro 'printk_index_wrap'
     484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
         |                         ^~~~
   fs/open.c:797:9: note: in expansion of macro 'printk'
     797 |         printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
         |         ^~~~~~
   fs/open.c:797:75: note: format string is defined here
     797 |         printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
         |                                                                          ~^
         |                                                                           |
         |                                                                           void *
         |                                                                          %ld
   fs/open.c:800:16: warning: format '%p' expects argument of type 'void *', but argument 4 has type 'long int' [-Wformat=]
     800 |         printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
         |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     801 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner));
         |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |                                      |
         |                                      long int
   include/linux/printk.h:484:25: note: in definition of macro 'printk_index_wrap'
     484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
         |                         ^~~~
   fs/open.c:800:9: note: in expansion of macro 'printk'
     800 |         printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
         |         ^~~~~~
   fs/open.c:800:74: note: format string is defined here
     800 |         printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
         |                                                                         ~^
         |                                                                          |
         |                                                                          void *
         |                                                                         %ld


vim +769 fs/open.c

   750	
   751	int chown_common(const struct path *path, uid_t user, gid_t group)
   752	{
   753		struct mnt_idmap *idmap;
   754		struct user_namespace *fs_userns;
   755		struct inode *inode = path->dentry->d_inode;
   756		struct inode *delegated_inode = NULL;
   757		int error;
   758		struct iattr newattrs;
   759		kuid_t uid;
   760		kgid_t gid;
   761	
   762		uid = make_kuid(current_user_ns(), user);
   763		gid = make_kgid(current_user_ns(), group);
   764	
   765		idmap = mnt_idmap(path->mnt);
   766		fs_userns = i_user_ns(inode);
   767	
   768	retry_deleg:
 > 769		printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
   770		       current->comm, inode, atomic_read(&inode->i_count),
   771		       atomic_long_read(&inode->i_rwsem.owner));
   772		newattrs.ia_vfsuid = INVALID_VFSUID;
   773		newattrs.ia_vfsgid = INVALID_VFSGID;
   774		newattrs.ia_valid =  ATTR_CTIME;
   775		if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid))
   776			return -EINVAL;
   777		if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid))
   778			return -EINVAL;
   779		printk("DEBUG: [%s] before inode_lock: inode=%p, i_count=%d\n",
   780		       current->comm, inode, atomic_read(&inode->i_count));
   781		error = inode_lock_killable(inode);
   782		if (error)
   783			return error;
   784		printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
   785		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), current);
   786		if (!S_ISDIR(inode->i_mode))
   787			newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV |
   788					     setattr_should_drop_sgid(idmap, inode);
   789		/* Continue to send actual fs values, not the mount values. */
   790		error = security_path_chown(
   791			path,
   792			from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid),
   793			from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid));
   794		if (!error)
   795			error = notify_change(idmap, path->dentry, &newattrs,
   796					      &delegated_inode);
   797		printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
   798		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), delegated_inode);
   799		inode_unlock(inode);
   800		printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
   801		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner));
   802		if (delegated_inode) {
   803			printk("DEBUG: [%s] calling break_deleg_wait: inode=%p, i_count=%d, delegated_inode=%p\n",
   804			       current->comm, inode, atomic_read(&inode->i_count), delegated_inode);
   805			error = break_deleg_wait(&delegated_inode);
   806			printk("DEBUG: [%s] after break_deleg_wait: inode=%p, i_count=%d, error=%d\n",
   807			       current->comm, inode, atomic_read(&inode->i_count), error);
   808			if (!error)
   809				goto retry_deleg;
   810		}
   811		return error;
   812	}
   813	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
                   ` (6 preceding siblings ...)
  2025-11-09 11:57 ` syzbot
@ 2025-11-09 12:25 ` syzbot
  2025-11-09 13:27 ` syzbot
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-11-09 12:25 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Author: kartikey406@gmail.com

#syz test git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master


The chown_common() function has a use-after-free bug in its delegation
retry path. When break_deleg_wait() is called, it internally calls
iput() on the delegated inode, potentially freeing it if this was the
last reference. However, chown_common() continues using the stale inode
pointer on retry, leading to operations on freed memory.

This manifests as a rwsem warning where the inode's rwsem owner field
is corrupted:
  DEBUG_RWSEMS_WARN_ON: owner = 0x0

The bug is triggered by concurrent fchownat() calls and is reproducible
on GFS2 filesystems where delegations are common.

Fix by:
1. Re-fetching inode from path->dentry->d_inode on each retry iteration
2. Holding an explicit inode reference with ihold() at iteration start
3. Releasing the reference with iput() on all exit paths

This ensures the inode remains valid throughout delegation break and
retry.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/open.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..e5ff4d052f80 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -787,9 +787,21 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 		path,
 		from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid),
 		from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid));
+	printk(KERN_INFO "After security_path_chown: owner=%lx\n",
+       		atomic_long_read(&inode->i_rwsem.owner));
 	if (!error)
 		error = notify_change(idmap, path->dentry, &newattrs,
 				      &delegated_inode);
+	printk(KERN_INFO "After notify_change: owner=%lx, error=%d\n",
+       		atomic_long_read(&inode->i_rwsem.owner), error);
+	if (atomic_long_read(&inode->i_rwsem.owner) != (long)current) {
+   		printk(KERN_ERR "BUG: About to unlock rwsem we don't own!\n");
+    		printk(KERN_ERR "  inode=%p\n", inode);
+    		printk(KERN_ERR "  i_rwsem.owner=%lx\n", atomic_long_read(&inode->i_rwsem.owner));
+    		printk(KERN_ERR "  current=%p\n", current);
+    		printk(KERN_ERR "  delegated_inode=%p\n", delegated_inode);
+    		dump_stack();
+	}
 	inode_unlock(inode);
 	if (delegated_inode) {
 		error = break_deleg_wait(&delegated_inode);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-11-09 11:05   ` kernel test robot
@ 2025-11-09 12:26     ` Philip Li
  0 siblings, 0 replies; 16+ messages in thread
From: Philip Li @ 2025-11-09 12:26 UTC (permalink / raw)
  To: kernel test robot
  Cc: syzbot, linux-kernel, syzkaller-bugs, llvm, oe-kbuild-all

On Sun, Nov 09, 2025 at 07:05:11PM +0800, kernel test robot wrote:
> Hi syzbot,
> 
> kernel test robot noticed the following build warnings:

Sorry, kindly ignore this report.

> 
> [auto build test WARNING on brauner-vfs/vfs.all]
> [also build test WARNING on linus/master v6.18-rc4 next-20251107]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/syzbot/Forwarded-PATCH-fs-fix-inode-use-after-free-in-chown_common-delegation-retry/20251109-171000
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git vfs.all
> patch link:    https://lore.kernel.org/r/691059ff.a70a0220.22f260.00a6.GAE%40google.com
> patch subject: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
> config: arm-allnoconfig (https://download.01.org/0day-ci/archive/20251109/202511091815.6q5WUuzH-lkp@intel.com/config)
> compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project b9ea93cd5c37fb6d606502fd01208dd48330549d)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251109/202511091815.6q5WUuzH-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202511091815.6q5WUuzH-lkp@intel.com/
> 
> All warnings (new ones prefixed by >>):
> 
> >> fs/open.c:771:9: warning: format specifies type 'void *' but the argument has type 'long' [-Wformat]
>      769 |         printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
>          |                                                                              ~~
>          |                                                                              %ld
>      770 |                current->comm, inode, atomic_read(&inode->i_count),
>      771 |                atomic_long_read(&inode->i_rwsem.owner));
>          |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    include/linux/printk.h:512:60: note: expanded from macro 'printk'
>      512 | #define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
>          |                                                     ~~~    ^~~~~~~~~~~
>    include/linux/printk.h:484:19: note: expanded from macro 'printk_index_wrap'
>      484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
>          |                         ~~~~    ^~~~~~~~~~~
>    fs/open.c:785:31: warning: format specifies type 'void *' but the argument has type 'long' [-Wformat]
>      784 |         printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
>          |                                                                       ~~
>          |                                                                       %ld
>      785 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), current);
>          |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    include/linux/printk.h:512:60: note: expanded from macro 'printk'
>      512 | #define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
>          |                                                     ~~~    ^~~~~~~~~~~
>    include/linux/printk.h:484:19: note: expanded from macro 'printk_index_wrap'
>      484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
>          |                         ~~~~    ^~~~~~~~~~~
>    fs/open.c:798:31: warning: format specifies type 'void *' but the argument has type 'long' [-Wformat]
>      797 |         printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
>          |                                                                          ~~
>          |                                                                          %ld
>      798 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), delegated_inode);
>          |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    include/linux/printk.h:512:60: note: expanded from macro 'printk'
>      512 | #define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
>          |                                                     ~~~    ^~~~~~~~~~~
>    include/linux/printk.h:484:19: note: expanded from macro 'printk_index_wrap'
>      484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
>          |                         ~~~~    ^~~~~~~~~~~
>    fs/open.c:801:31: warning: format specifies type 'void *' but the argument has type 'long' [-Wformat]
>      800 |         printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
>          |                                                                         ~~
>          |                                                                         %ld
>      801 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner));
>          |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    include/linux/printk.h:512:60: note: expanded from macro 'printk'
>      512 | #define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
>          |                                                     ~~~    ^~~~~~~~~~~
>    include/linux/printk.h:484:19: note: expanded from macro 'printk_index_wrap'
>      484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
>          |                         ~~~~    ^~~~~~~~~~~
>    4 warnings generated.
> 
> 
> vim +771 fs/open.c
> 
>    750	
>    751	int chown_common(const struct path *path, uid_t user, gid_t group)
>    752	{
>    753		struct mnt_idmap *idmap;
>    754		struct user_namespace *fs_userns;
>    755		struct inode *inode = path->dentry->d_inode;
>    756		struct inode *delegated_inode = NULL;
>    757		int error;
>    758		struct iattr newattrs;
>    759		kuid_t uid;
>    760		kgid_t gid;
>    761	
>    762		uid = make_kuid(current_user_ns(), user);
>    763		gid = make_kgid(current_user_ns(), group);
>    764	
>    765		idmap = mnt_idmap(path->mnt);
>    766		fs_userns = i_user_ns(inode);
>    767	
>    768	retry_deleg:
>    769		printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
>    770		       current->comm, inode, atomic_read(&inode->i_count),
>  > 771		       atomic_long_read(&inode->i_rwsem.owner));
>    772		newattrs.ia_vfsuid = INVALID_VFSUID;
>    773		newattrs.ia_vfsgid = INVALID_VFSGID;
>    774		newattrs.ia_valid =  ATTR_CTIME;
>    775		if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid))
>    776			return -EINVAL;
>    777		if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid))
>    778			return -EINVAL;
>    779		printk("DEBUG: [%s] before inode_lock: inode=%p, i_count=%d\n",
>    780		       current->comm, inode, atomic_read(&inode->i_count));
>    781		error = inode_lock_killable(inode);
>    782		if (error)
>    783			return error;
>    784		printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
>    785		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), current);
>    786		if (!S_ISDIR(inode->i_mode))
>    787			newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV |
>    788					     setattr_should_drop_sgid(idmap, inode);
>    789		/* Continue to send actual fs values, not the mount values. */
>    790		error = security_path_chown(
>    791			path,
>    792			from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid),
>    793			from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid));
>    794		if (!error)
>    795			error = notify_change(idmap, path->dentry, &newattrs,
>    796					      &delegated_inode);
>    797		printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
>    798		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), delegated_inode);
>    799		inode_unlock(inode);
>    800		printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
>    801		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner));
>    802		if (delegated_inode) {
>    803			printk("DEBUG: [%s] calling break_deleg_wait: inode=%p, i_count=%d, delegated_inode=%p\n",
>    804			       current->comm, inode, atomic_read(&inode->i_count), delegated_inode);
>    805			error = break_deleg_wait(&delegated_inode);
>    806			printk("DEBUG: [%s] after break_deleg_wait: inode=%p, i_count=%d, error=%d\n",
>    807			       current->comm, inode, atomic_read(&inode->i_count), error);
>    808			if (!error)
>    809				goto retry_deleg;
>    810		}
>    811		return error;
>    812	}
>    813	
> 
> -- 
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-11-09 12:17   ` kernel test robot
@ 2025-11-09 12:26     ` Philip Li
  0 siblings, 0 replies; 16+ messages in thread
From: Philip Li @ 2025-11-09 12:26 UTC (permalink / raw)
  To: kernel test robot; +Cc: syzbot, linux-kernel, syzkaller-bugs, oe-kbuild-all

On Sun, Nov 09, 2025 at 08:17:42PM +0800, kernel test robot wrote:
> Hi syzbot,

Sorry, kindly ignore this report.

> 
> kernel test robot noticed the following build warnings:
> 
> [auto build test WARNING on brauner-vfs/vfs.all]
> [also build test WARNING on linus/master v6.18-rc4 next-20251107]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/syzbot/Forwarded-PATCH-fs-fix-inode-use-after-free-in-chown_common-delegation-retry/20251109-171000
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git vfs.all
> patch link:    https://lore.kernel.org/r/691059ff.a70a0220.22f260.00a6.GAE%40google.com
> patch subject: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
> config: i386-allnoconfig (https://download.01.org/0day-ci/archive/20251109/202511091831.tPcsumuB-lkp@intel.com/config)
> compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251109/202511091831.tPcsumuB-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202511091831.tPcsumuB-lkp@intel.com/
> 
> All warnings (new ones prefixed by >>):
> 
>    In file included from include/asm-generic/bug.h:22,
>                     from arch/x86/include/asm/bug.h:108,
>                     from include/linux/bug.h:5,
>                     from include/linux/mmdebug.h:5,
>                     from include/linux/mm.h:6,
>                     from fs/open.c:9:
>    fs/open.c: In function 'chown_common':
> >> fs/open.c:769:16: warning: format '%p' expects argument of type 'void *', but argument 5 has type 'long int' [-Wformat=]
>      769 |         printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
>          |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>      770 |                current->comm, inode, atomic_read(&inode->i_count),
>      771 |                atomic_long_read(&inode->i_rwsem.owner));
>          |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>          |                |
>          |                long int
>    include/linux/printk.h:484:25: note: in definition of macro 'printk_index_wrap'
>      484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
>          |                         ^~~~
>    fs/open.c:769:9: note: in expansion of macro 'printk'
>      769 |         printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
>          |         ^~~~~~
>    fs/open.c:769:79: note: format string is defined here
>      769 |         printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
>          |                                                                              ~^
>          |                                                                               |
>          |                                                                               void *
>          |                                                                              %ld
>    fs/open.c:784:16: warning: format '%p' expects argument of type 'void *', but argument 4 has type 'long int' [-Wformat=]
>      784 |         printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
>          |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>      785 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), current);
>          |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>          |                                      |
>          |                                      long int
>    include/linux/printk.h:484:25: note: in definition of macro 'printk_index_wrap'
>      484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
>          |                         ^~~~
>    fs/open.c:784:9: note: in expansion of macro 'printk'
>      784 |         printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
>          |         ^~~~~~
>    fs/open.c:784:72: note: format string is defined here
>      784 |         printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
>          |                                                                       ~^
>          |                                                                        |
>          |                                                                        void *
>          |                                                                       %ld
>    fs/open.c:797:16: warning: format '%p' expects argument of type 'void *', but argument 4 has type 'long int' [-Wformat=]
>      797 |         printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
>          |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>      798 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), delegated_inode);
>          |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>          |                                      |
>          |                                      long int
>    include/linux/printk.h:484:25: note: in definition of macro 'printk_index_wrap'
>      484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
>          |                         ^~~~
>    fs/open.c:797:9: note: in expansion of macro 'printk'
>      797 |         printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
>          |         ^~~~~~
>    fs/open.c:797:75: note: format string is defined here
>      797 |         printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
>          |                                                                          ~^
>          |                                                                           |
>          |                                                                           void *
>          |                                                                          %ld
>    fs/open.c:800:16: warning: format '%p' expects argument of type 'void *', but argument 4 has type 'long int' [-Wformat=]
>      800 |         printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
>          |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>      801 |                current->comm, inode, atomic_long_read(&inode->i_rwsem.owner));
>          |                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>          |                                      |
>          |                                      long int
>    include/linux/printk.h:484:25: note: in definition of macro 'printk_index_wrap'
>      484 |                 _p_func(_fmt, ##__VA_ARGS__);                           \
>          |                         ^~~~
>    fs/open.c:800:9: note: in expansion of macro 'printk'
>      800 |         printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
>          |         ^~~~~~
>    fs/open.c:800:74: note: format string is defined here
>      800 |         printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
>          |                                                                         ~^
>          |                                                                          |
>          |                                                                          void *
>          |                                                                         %ld
> 
> 
> vim +769 fs/open.c
> 
>    750	
>    751	int chown_common(const struct path *path, uid_t user, gid_t group)
>    752	{
>    753		struct mnt_idmap *idmap;
>    754		struct user_namespace *fs_userns;
>    755		struct inode *inode = path->dentry->d_inode;
>    756		struct inode *delegated_inode = NULL;
>    757		int error;
>    758		struct iattr newattrs;
>    759		kuid_t uid;
>    760		kgid_t gid;
>    761	
>    762		uid = make_kuid(current_user_ns(), user);
>    763		gid = make_kgid(current_user_ns(), group);
>    764	
>    765		idmap = mnt_idmap(path->mnt);
>    766		fs_userns = i_user_ns(inode);
>    767	
>    768	retry_deleg:
>  > 769		printk("DEBUG: [%s] retry_deleg: inode=%p, i_count=%d, i_rwsem.owner=%px\n",
>    770		       current->comm, inode, atomic_read(&inode->i_count),
>    771		       atomic_long_read(&inode->i_rwsem.owner));
>    772		newattrs.ia_vfsuid = INVALID_VFSUID;
>    773		newattrs.ia_vfsgid = INVALID_VFSGID;
>    774		newattrs.ia_valid =  ATTR_CTIME;
>    775		if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid))
>    776			return -EINVAL;
>    777		if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid))
>    778			return -EINVAL;
>    779		printk("DEBUG: [%s] before inode_lock: inode=%p, i_count=%d\n",
>    780		       current->comm, inode, atomic_read(&inode->i_count));
>    781		error = inode_lock_killable(inode);
>    782		if (error)
>    783			return error;
>    784		printk("DEBUG: [%s] after inode_lock: inode=%p, i_rwsem.owner=%px (current=%px)\n",
>    785		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), current);
>    786		if (!S_ISDIR(inode->i_mode))
>    787			newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV |
>    788					     setattr_should_drop_sgid(idmap, inode);
>    789		/* Continue to send actual fs values, not the mount values. */
>    790		error = security_path_chown(
>    791			path,
>    792			from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid),
>    793			from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid));
>    794		if (!error)
>    795			error = notify_change(idmap, path->dentry, &newattrs,
>    796					      &delegated_inode);
>    797		printk("DEBUG: [%s] before inode_unlock: inode=%p, i_rwsem.owner=%px, delegated_inode=%p\n",
>    798		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner), delegated_inode);
>    799		inode_unlock(inode);
>    800		printk("DEBUG: [%s] after inode_unlock: inode=%p, i_rwsem.owner=%px\n",
>    801		       current->comm, inode, atomic_long_read(&inode->i_rwsem.owner));
>    802		if (delegated_inode) {
>    803			printk("DEBUG: [%s] calling break_deleg_wait: inode=%p, i_count=%d, delegated_inode=%p\n",
>    804			       current->comm, inode, atomic_read(&inode->i_count), delegated_inode);
>    805			error = break_deleg_wait(&delegated_inode);
>    806			printk("DEBUG: [%s] after break_deleg_wait: inode=%p, i_count=%d, error=%d\n",
>    807			       current->comm, inode, atomic_read(&inode->i_count), error);
>    808			if (!error)
>    809				goto retry_deleg;
>    810		}
>    811		return error;
>    812	}
>    813	
> 
> -- 
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
                   ` (7 preceding siblings ...)
  2025-11-09 12:25 ` syzbot
@ 2025-11-09 13:27 ` syzbot
  2025-11-09 14:29 ` syzbot
  2025-11-09 15:16 ` syzbot
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-11-09 13:27 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Author: kartikey406@gmail.com

#syz test git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

The chown_common() function has a use-after-free bug in its delegation
retry path. When break_deleg_wait() is called, it internally calls
iput() on the delegated inode, potentially freeing it if this was the
last reference. However, chown_common() continues using the stale inode
pointer on retry, leading to operations on freed memory.

This manifests as a rwsem warning where the inode's rwsem owner field
is corrupted:
  DEBUG_RWSEMS_WARN_ON: owner = 0x0

The bug is triggered by concurrent fchownat() calls and is reproducible
on GFS2 filesystems where delegations are common.

Fix by:
1. Re-fetching inode from path->dentry->d_inode on each retry iteration
2. Holding an explicit inode reference with ihold() at iteration start
3. Releasing the reference with iput() on all exit paths

This ensures the inode remains valid throughout delegation break and
retry.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/open.c          | 12 ++++++++++++
 include/linux/fs.h |  5 +++++
 2 files changed, 17 insertions(+)

diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..e5ff4d052f80 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -787,9 +787,21 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 		path,
 		from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid),
 		from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid));
+	printk(KERN_INFO "After security_path_chown: owner=%lx\n",
+       		atomic_long_read(&inode->i_rwsem.owner));
 	if (!error)
 		error = notify_change(idmap, path->dentry, &newattrs,
 				      &delegated_inode);
+	printk(KERN_INFO "After notify_change: owner=%lx, error=%d\n",
+       		atomic_long_read(&inode->i_rwsem.owner), error);
+	if (atomic_long_read(&inode->i_rwsem.owner) != (long)current) {
+   		printk(KERN_ERR "BUG: About to unlock rwsem we don't own!\n");
+    		printk(KERN_ERR "  inode=%p\n", inode);
+    		printk(KERN_ERR "  i_rwsem.owner=%lx\n", atomic_long_read(&inode->i_rwsem.owner));
+    		printk(KERN_ERR "  current=%p\n", current);
+    		printk(KERN_ERR "  delegated_inode=%p\n", delegated_inode);
+    		dump_stack();
+	}
 	inode_unlock(inode);
 	if (delegated_inode) {
 		error = break_deleg_wait(&delegated_inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c895146c1444..84f7267aac3d 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -987,6 +987,11 @@ static inline __must_check int inode_lock_killable(struct inode *inode)
 
 static inline void inode_unlock(struct inode *inode)
 {
+	printk(KERN_INFO "[%d] inode_unlock: inode=%p, owner=%lx, current=%p (%s:%d)\n",
+	       count, inode,
+	       atomic_long_read(&inode->i_rwsem.owner),
+	       current, current->comm, current->pid);
+	dump_stack();
 	up_write(&inode->i_rwsem);
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
                   ` (8 preceding siblings ...)
  2025-11-09 13:27 ` syzbot
@ 2025-11-09 14:29 ` syzbot
  2025-11-09 15:16 ` syzbot
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-11-09 14:29 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Author: kartikey406@gmail.com

#syz test git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

The chown_common() function has a use-after-free bug in its delegation
retry path. When break_deleg_wait() is called, it internally calls
iput() on the delegated inode, potentially freeing it if this was the
last reference. However, chown_common() continues using the stale inode
pointer on retry, leading to operations on freed memory.

This manifests as a rwsem warning where the inode's rwsem owner field
is corrupted:
  DEBUG_RWSEMS_WARN_ON: owner = 0x0

The bug is triggered by concurrent fchownat() calls and is reproducible
on GFS2 filesystems where delegations are common.

Fix by:
1. Re-fetching inode from path->dentry->d_inode on each retry iteration
2. Holding an explicit inode reference with ihold() at iteration start
3. Releasing the reference with iput() on all exit paths

This ensures the inode remains valid throughout delegation break and
retry.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/open.c          | 12 ++++++++++++
 include/linux/fs.h |  5 +++++
 2 files changed, 17 insertions(+)

diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..e5ff4d052f80 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -787,9 +787,21 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 		path,
 		from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid),
 		from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid));
+	printk(KERN_INFO "After security_path_chown: owner=%lx\n",
+       		atomic_long_read(&inode->i_rwsem.owner));
 	if (!error)
 		error = notify_change(idmap, path->dentry, &newattrs,
 				      &delegated_inode);
+	printk(KERN_INFO "After notify_change: owner=%lx, error=%d\n",
+       		atomic_long_read(&inode->i_rwsem.owner), error);
+	if (atomic_long_read(&inode->i_rwsem.owner) != (long)current) {
+   		printk(KERN_ERR "BUG: About to unlock rwsem we don't own!\n");
+    		printk(KERN_ERR "  inode=%p\n", inode);
+    		printk(KERN_ERR "  i_rwsem.owner=%lx\n", atomic_long_read(&inode->i_rwsem.owner));
+    		printk(KERN_ERR "  current=%p\n", current);
+    		printk(KERN_ERR "  delegated_inode=%p\n", delegated_inode);
+    		dump_stack();
+	}
 	inode_unlock(inode);
 	if (delegated_inode) {
 		error = break_deleg_wait(&delegated_inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c895146c1444..005586e26688 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -987,6 +987,11 @@ static inline __must_check int inode_lock_killable(struct inode *inode)
 
 static inline void inode_unlock(struct inode *inode)
 {
+	printk(KERN_INFO "inode_unlock: inode=%p, owner=%lx, current=%p (%s:%d)\n",
+	       inode,
+	       atomic_long_read(&inode->i_rwsem.owner),
+	       current, current->comm, current->pid);
+	dump_stack();
 	up_write(&inode->i_rwsem);
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
  2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
                   ` (9 preceding siblings ...)
  2025-11-09 14:29 ` syzbot
@ 2025-11-09 15:16 ` syzbot
  10 siblings, 0 replies; 16+ messages in thread
From: syzbot @ 2025-11-09 15:16 UTC (permalink / raw)
  To: linux-kernel, syzkaller-bugs

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Author: kartikey406@gmail.com

#syz test git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

The chown_common() function has a use-after-free bug in its delegation
retry path. When break_deleg_wait() is called, it internally calls
iput() on the delegated inode, potentially freeing it if this was the
last reference. However, chown_common() continues using the stale inode
pointer on retry, leading to operations on freed memory.

This manifests as a rwsem warning where the inode's rwsem owner field
is corrupted:
  DEBUG_RWSEMS_WARN_ON: owner = 0x0

The bug is triggered by concurrent fchownat() calls and is reproducible
on GFS2 filesystems where delegations are common.

Fix by:
1. Re-fetching inode from path->dentry->d_inode on each retry iteration
2. Holding an explicit inode reference with ihold() at iteration start
3. Releasing the reference with iput() on all exit paths

This ensures the inode remains valid throughout delegation break and
retry.

Reported-by: syzbot+04c2672c56fbb9401640@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04c2672c56fbb9401640
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/attr.c |  5 ++++-
 fs/open.c | 12 ++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/fs/attr.c b/fs/attr.c
index 795f231d00e8..a45d29032283 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -418,6 +418,8 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
 		  struct iattr *attr, struct inode **delegated_inode)
 {
 	struct inode *inode = dentry->d_inode;
+	printk(KERN_INFO "notify_change START: inode=%p, owner=%lx\n",
+	       inode, atomic_long_read(&inode->i_rwsem.owner));
 	umode_t mode = inode->i_mode;
 	int error;
 	struct timespec64 now;
@@ -551,7 +553,8 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
 		fsnotify_change(dentry, ia_valid);
 		security_inode_post_setattr(idmap, dentry, ia_valid);
 	}
-
+	printk(KERN_INFO "notify_change END: inode=%p, owner=%lx, error=%d\n",
+	       inode, atomic_long_read(&inode->i_rwsem.owner), error);
 	return error;
 }
 EXPORT_SYMBOL(notify_change);
diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..e5ff4d052f80 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -787,9 +787,21 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
 		path,
 		from_vfsuid(idmap, fs_userns, newattrs.ia_vfsuid),
 		from_vfsgid(idmap, fs_userns, newattrs.ia_vfsgid));
+	printk(KERN_INFO "After security_path_chown: owner=%lx\n",
+       		atomic_long_read(&inode->i_rwsem.owner));
 	if (!error)
 		error = notify_change(idmap, path->dentry, &newattrs,
 				      &delegated_inode);
+	printk(KERN_INFO "After notify_change: owner=%lx, error=%d\n",
+       		atomic_long_read(&inode->i_rwsem.owner), error);
+	if (atomic_long_read(&inode->i_rwsem.owner) != (long)current) {
+   		printk(KERN_ERR "BUG: About to unlock rwsem we don't own!\n");
+    		printk(KERN_ERR "  inode=%p\n", inode);
+    		printk(KERN_ERR "  i_rwsem.owner=%lx\n", atomic_long_read(&inode->i_rwsem.owner));
+    		printk(KERN_ERR "  current=%p\n", current);
+    		printk(KERN_ERR "  delegated_inode=%p\n", delegated_inode);
+    		dump_stack();
+	}
 	inode_unlock(inode);
 	if (delegated_inode) {
 		error = break_deleg_wait(&delegated_inode);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2025-11-09 15:16 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-12  5:45 [syzbot] [gfs2?] WARNING in chown_common syzbot
2025-10-17  8:57 ` Forwarded: [PATCH] fs: fix stale inode access in chown_common() retry path syzbot
2025-10-17  8:57 ` syzbot
2025-10-29  8:23 ` Forwarded: Test patch for [syzbot] [gfs2?] WARNING in chown_common syzbot
2025-11-09  5:02 ` Forwarded: [PATCH] fs: fix inode reference leak in chown_common delegation retry path syzbot
2025-11-09  9:08 ` Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry syzbot
2025-11-09 11:05   ` kernel test robot
2025-11-09 12:26     ` Philip Li
2025-11-09 12:17   ` kernel test robot
2025-11-09 12:26     ` Philip Li
2025-11-09 11:40 ` syzbot
2025-11-09 11:57 ` syzbot
2025-11-09 12:25 ` syzbot
2025-11-09 13:27 ` syzbot
2025-11-09 14:29 ` syzbot
2025-11-09 15:16 ` syzbot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox