Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry

syzbot posted 1 patch 1 month, 1 week ago
There is a newer version of this series
fs/open.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Posted by syzbot 1 month, 1 week ago
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
Re: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Posted by kernel test robot 1 month, 1 week ago
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
Re: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Posted by Philip Li 1 month, 1 week ago
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
>
Re: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Posted by kernel test robot 1 month, 1 week ago
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
Re: Forwarded: [PATCH] fs: fix inode use-after-free in chown_common delegation retry
Posted by Philip Li 1 month, 1 week ago
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
>