[PATCH] bfs: put a inode if link count is 0

Lizhi Xu posted 1 patch 11 months, 2 weeks ago
fs/bfs/dir.c | 2 ++
1 file changed, 2 insertions(+)
[PATCH] bfs: put a inode if link count is 0
Posted by Lizhi Xu 11 months, 2 weeks ago
syzbot reported a warning in drop_nlink. [1]

The reproducer performs the rename operation on the file twice in succession
and changes the file to the same file name.  After the first rename operation,
the number of links in the inode is set to 0. In the second execution, the
same inode is used, resulting in a 0 value warning for i_nlink. 

To avoid this issue, put the target inode before exiting the bfs_rename.

[1]
WARNING: CPU: 0 PID: 5819 at fs/inode.c:407 drop_nlink+0xc4/0x110 fs/inode.c:407
Modules linked in:
CPU: 0 UID: 0 PID: 5819 Comm: syz-executor232 Not tainted 6.13.0-rc5-syzkaller-00161-g63676eefb7a0 #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 09/13/2024
RIP: 0010:drop_nlink+0xc4/0x110 fs/inode.c:407
Code: bb 70 07 00 00 be 08 00 00 00 e8 37 3b e7 ff f0 48 ff 83 70 07 00 00 5b 41 5c 41 5e 41 5f 5d c3 cc cc cc cc e8 8d 5b 83 ff 90 <0f> 0b 90 eb 83 44 89 e1 80 e1 07 80 c1 03 38 c1 0f 8c 5c ff ff ff
RSP: 0018:ffffc90003c6f950 EFLAGS: 00010293
RAX: ffffffff821c1843 RBX: 1ffff1100e5181f8 RCX: ffff8880351d9e00
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffffffff821c17c3 R09: 1ffffffff2030ad6
R10: dffffc0000000000 R11: fffffbfff2030ad7 R12: ffff8880728c0fc0
R13: dffffc0000000000 R14: ffff8880728c0f78 R15: dffffc0000000000
FS:  0000555562096380(0000) GS:ffff8880b8600000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000558ecae7b0d8 CR3: 0000000012a66000 CR4: 00000000003526f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 inode_dec_link_count include/linux/fs.h:2521 [inline]
 bfs_rename+0x44e/0x530 fs/bfs/dir.c:247
 vfs_rename+0xbdb/0xf00 fs/namei.c:5067
 do_renameat2+0xd94/0x13f0 fs/namei.c:5224
 __do_sys_rename fs/namei.c:5271 [inline]
 __se_sys_rename fs/namei.c:5269 [inline]
 __x64_sys_rename+0x82/0x90 fs/namei.c:5269
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83

Reported-by: syzbot+80e60df48923e1b7691d@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=80e60df48923e1b7691d
Signed-off-by: Lizhi Xu <lizhi.xu@windriver.com>
---
 fs/bfs/dir.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c
index c375e22c4c0c..3d67e7c5b1fa 100644
--- a/fs/bfs/dir.c
+++ b/fs/bfs/dir.c
@@ -251,6 +251,8 @@ static int bfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
 
 end_rename:
 	mutex_unlock(&info->bfs_lock);
+	if (new_inode && !new_inode->i_nlink)
+		iput(new_inode);
 	brelse(old_bh);
 	brelse(new_bh);
 	return error;
-- 
2.43.0
Re: [PATCH] bfs: put a inode if link count is 0
Posted by kernel test robot 11 months, 1 week ago
Hi Lizhi,

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.13-rc6 next-20250109]
[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/Lizhi-Xu/bfs-put-a-inode-if-link-count-is-0/20250109-123423
base:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git vfs.all
patch link:    https://lore.kernel.org/r/20250109034946.1386748-1-lizhi.xu%40windriver.com
patch subject: [PATCH] bfs: put a inode if link count is 0
config: x86_64-buildonly-randconfig-005-20250110 (https://download.01.org/0day-ci/archive/20250110/202501101459.tVj2wd8p-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250110/202501101459.tVj2wd8p-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/202501101459.tVj2wd8p-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from fs/bfs/dir.c:12:
   In file included from include/linux/buffer_head.h:12:
   In file included from include/linux/blk_types.h:10:
   In file included from include/linux/bvec.h:10:
   In file included from include/linux/highmem.h:8:
   In file included from include/linux/cacheflush.h:5:
   In file included from arch/x86/include/asm/cacheflush.h:5:
   In file included from include/linux/mm.h:2223:
   include/linux/vmstat.h:504:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     504 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     505 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:511:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     511 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     512 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
   include/linux/vmstat.h:524:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     524 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     525 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> fs/bfs/dir.c:225:6: warning: variable 'new_inode' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
     225 |         if (!old_bh || (le16_to_cpu(old_de->ino) != old_inode->i_ino))
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/bfs/dir.c:254:6: note: uninitialized use occurs here
     254 |         if (new_inode && !new_inode->i_nlink)
         |             ^~~~~~~~~
   fs/bfs/dir.c:225:2: note: remove the 'if' if its condition is always false
     225 |         if (!old_bh || (le16_to_cpu(old_de->ino) != old_inode->i_ino))
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     226 |                 goto end_rename;
         |                 ~~~~~~~~~~~~~~~
>> fs/bfs/dir.c:225:6: warning: variable 'new_inode' is used uninitialized whenever '||' condition is true [-Wsometimes-uninitialized]
     225 |         if (!old_bh || (le16_to_cpu(old_de->ino) != old_inode->i_ino))
         |             ^~~~~~~
   fs/bfs/dir.c:254:6: note: uninitialized use occurs here
     254 |         if (new_inode && !new_inode->i_nlink)
         |             ^~~~~~~~~
   fs/bfs/dir.c:225:6: note: remove the '||' if its condition is always false
     225 |         if (!old_bh || (le16_to_cpu(old_de->ino) != old_inode->i_ino))
         |             ^~~~~~~~~~
   fs/bfs/dir.c:206:37: note: initialize the variable 'new_inode' to silence this warning
     206 |         struct inode *old_inode, *new_inode;
         |                                            ^
         |                                             = NULL
   6 warnings generated.


vim +225 fs/bfs/dir.c

^1da177e4c3f41 Linus Torvalds    2005-04-16  201  
e18275ae55e07a Christian Brauner 2023-01-13  202  static int bfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
549c7297717c32 Christian Brauner 2021-01-21  203  		      struct dentry *old_dentry, struct inode *new_dir,
549c7297717c32 Christian Brauner 2021-01-21  204  		      struct dentry *new_dentry, unsigned int flags)
^1da177e4c3f41 Linus Torvalds    2005-04-16  205  {
^1da177e4c3f41 Linus Torvalds    2005-04-16  206  	struct inode *old_inode, *new_inode;
^1da177e4c3f41 Linus Torvalds    2005-04-16  207  	struct buffer_head *old_bh, *new_bh;
^1da177e4c3f41 Linus Torvalds    2005-04-16  208  	struct bfs_dirent *old_de, *new_de;
3f165e4cf2af04 Dmitri Vorobiev   2008-07-25  209  	struct bfs_sb_info *info;
^1da177e4c3f41 Linus Torvalds    2005-04-16  210  	int error = -ENOENT;
^1da177e4c3f41 Linus Torvalds    2005-04-16  211  
f03b8ad8d38634 Miklos Szeredi    2016-09-27  212  	if (flags & ~RENAME_NOREPLACE)
f03b8ad8d38634 Miklos Szeredi    2016-09-27  213  		return -EINVAL;
f03b8ad8d38634 Miklos Szeredi    2016-09-27  214  
^1da177e4c3f41 Linus Torvalds    2005-04-16  215  	old_bh = new_bh = NULL;
2b0143b5c986be David Howells     2015-03-17  216  	old_inode = d_inode(old_dentry);
^1da177e4c3f41 Linus Torvalds    2005-04-16  217  	if (S_ISDIR(old_inode->i_mode))
^1da177e4c3f41 Linus Torvalds    2005-04-16  218  		return -EINVAL;
^1da177e4c3f41 Linus Torvalds    2005-04-16  219  
3f165e4cf2af04 Dmitri Vorobiev   2008-07-25  220  	info = BFS_SB(old_inode->i_sb);
3f165e4cf2af04 Dmitri Vorobiev   2008-07-25  221  
3f165e4cf2af04 Dmitri Vorobiev   2008-07-25  222  	mutex_lock(&info->bfs_lock);
33ebdebece6d5d Al Viro           2018-04-30  223  	old_bh = bfs_find_entry(old_dir, &old_dentry->d_name, &old_de);
^1da177e4c3f41 Linus Torvalds    2005-04-16  224  
f433dc56344cb7 Dmitri Vorobiev   2007-11-14 @225  	if (!old_bh || (le16_to_cpu(old_de->ino) != old_inode->i_ino))
^1da177e4c3f41 Linus Torvalds    2005-04-16  226  		goto end_rename;
^1da177e4c3f41 Linus Torvalds    2005-04-16  227  
^1da177e4c3f41 Linus Torvalds    2005-04-16  228  	error = -EPERM;
2b0143b5c986be David Howells     2015-03-17  229  	new_inode = d_inode(new_dentry);
33ebdebece6d5d Al Viro           2018-04-30  230  	new_bh = bfs_find_entry(new_dir, &new_dentry->d_name, &new_de);
^1da177e4c3f41 Linus Torvalds    2005-04-16  231  
^1da177e4c3f41 Linus Torvalds    2005-04-16  232  	if (new_bh && !new_inode) {
^1da177e4c3f41 Linus Torvalds    2005-04-16  233  		brelse(new_bh);
^1da177e4c3f41 Linus Torvalds    2005-04-16  234  		new_bh = NULL;
^1da177e4c3f41 Linus Torvalds    2005-04-16  235  	}
^1da177e4c3f41 Linus Torvalds    2005-04-16  236  	if (!new_bh) {
b455ecd4bb4db1 Al Viro           2018-04-30  237  		error = bfs_add_entry(new_dir, &new_dentry->d_name,
f433dc56344cb7 Dmitri Vorobiev   2007-11-14  238  					old_inode->i_ino);
^1da177e4c3f41 Linus Torvalds    2005-04-16  239  		if (error)
^1da177e4c3f41 Linus Torvalds    2005-04-16  240  			goto end_rename;
^1da177e4c3f41 Linus Torvalds    2005-04-16  241  	}
^1da177e4c3f41 Linus Torvalds    2005-04-16  242  	old_de->ino = 0;
ce17a80c388cd1 Jeff Layton       2023-10-04  243  	inode_set_mtime_to_ts(old_dir, inode_set_ctime_current(old_dir));
^1da177e4c3f41 Linus Torvalds    2005-04-16  244  	mark_inode_dirty(old_dir);
^1da177e4c3f41 Linus Torvalds    2005-04-16  245  	if (new_inode) {
73d9b9d028176b Jeff Layton       2023-07-05  246  		inode_set_ctime_current(new_inode);
9a53c3a783c2fa Dave Hansen       2006-09-30  247  		inode_dec_link_count(new_inode);
^1da177e4c3f41 Linus Torvalds    2005-04-16  248  	}
4427f0c36e22e2 Al Viro           2009-06-08  249  	mark_buffer_dirty_inode(old_bh, old_dir);
^1da177e4c3f41 Linus Torvalds    2005-04-16  250  	error = 0;
^1da177e4c3f41 Linus Torvalds    2005-04-16  251  
^1da177e4c3f41 Linus Torvalds    2005-04-16  252  end_rename:
3f165e4cf2af04 Dmitri Vorobiev   2008-07-25  253  	mutex_unlock(&info->bfs_lock);
56423717549f8d Lizhi Xu          2025-01-09 @254  	if (new_inode && !new_inode->i_nlink)
56423717549f8d Lizhi Xu          2025-01-09  255  		iput(new_inode);
^1da177e4c3f41 Linus Torvalds    2005-04-16  256  	brelse(old_bh);
^1da177e4c3f41 Linus Torvalds    2005-04-16  257  	brelse(new_bh);
^1da177e4c3f41 Linus Torvalds    2005-04-16  258  	return error;
^1da177e4c3f41 Linus Torvalds    2005-04-16  259  }
^1da177e4c3f41 Linus Torvalds    2005-04-16  260  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH] bfs: put a inode if link count is 0
Posted by Al Viro 11 months, 2 weeks ago
On Thu, Jan 09, 2025 at 11:49:46AM +0800, Lizhi Xu wrote:
> 
> The reproducer performs the rename operation on the file twice in succession
> and changes the file to the same file name.  After the first rename operation,
> the number of links in the inode is set to 0. In the second execution, the
> same inode is used, resulting in a 0 value warning for i_nlink. 
> 
> To avoid this issue, put the target inode before exiting the bfs_rename.

	This is completely insane - you get an extra drop of in-core inode
refcount, which *will* end up with dangling pointer and memory corruption.
Besides, there is a perfectly legitimate case when you open a file and
rename something on top of it.  It MUST remain open and alive until the
last in-core reference to inode goes away, which must not happen before
close().

	Frankly, if you do not understand why that is wrong, you should not
mess with anything filesystem-related until you learn the area enough.

NAK.
Re: [PATCH] bfs: put a inode if link count is 0
Posted by Lizhi Xu 11 months, 2 weeks ago
On Thu, 9 Jan 2025 06:22:16 +0000, Al Viro wrote:
> > The reproducer performs the rename operation on the file twice in succession
> > and changes the file to the same file name.  After the first rename operation,
> > the number of links in the inode is set to 0. In the second execution, the
> > same inode is used, resulting in a 0 value warning for i_nlink.
> >
> > To avoid this issue, put the target inode before exiting the bfs_rename.
> 
>         This is completely insane - you get an extra drop of in-core inode
> refcount, which *will* end up with dangling pointer and memory corruption.
> Besides, there is a perfectly legitimate case when you open a file and
> rename something on top of it.  It MUST remain open and alive until the
> last in-core reference to inode goes away, which must not happen before
> close().
In the reproducer, changes the file to the same file, the same file name is
"file0", file0 uses mknod to create its inode and sets the i_nlink value to 1.
There is no operation to open file0 in the reproducer. Is this situation also
as you said?
Re: [PATCH] bfs: put a inode if link count is 0
Posted by Al Viro 11 months, 2 weeks ago
On Thu, Jan 09, 2025 at 02:39:39PM +0800, Lizhi Xu wrote:
> On Thu, 9 Jan 2025 06:22:16 +0000, Al Viro wrote:
> > > The reproducer performs the rename operation on the file twice in succession
> > > and changes the file to the same file name.  After the first rename operation,
> > > the number of links in the inode is set to 0. In the second execution, the
> > > same inode is used, resulting in a 0 value warning for i_nlink.
> > >
> > > To avoid this issue, put the target inode before exiting the bfs_rename.
> > 
> >         This is completely insane - you get an extra drop of in-core inode
> > refcount, which *will* end up with dangling pointer and memory corruption.
> > Besides, there is a perfectly legitimate case when you open a file and
> > rename something on top of it.  It MUST remain open and alive until the
> > last in-core reference to inode goes away, which must not happen before
> > close().
> In the reproducer, changes the file to the same file, the same file name is
> "file0", file0 uses mknod to create its inode and sets the i_nlink value to 1.
> There is no operation to open file0 in the reproducer. Is this situation also
> as you said?

I'm not sure I understand your sentence, to be honest.

Your patch is 100% wrong - you must *not*, under any circumstances, have
->rename() drop references to in-core struct inode instances.  It's *always*
wrong; the reference to new_inode in new_dentry->d_inode remains there (as
it ought to) and its contribution to new_inode refcount remains unchanged.
It has nothing to do with ->i_nlink; you are decrementing ->i_count, which
controls the lifetime of in-core struct inode instance.  As soon as that
reaches zero, struct inode instance will be freed.  And destructor of
new_dentry *will* call iput() on its ->d_inode, so you'll end up with
attempt to decrement refcount of already freed memory object.

Again, that has nothing to do with ->i_nlink.  I'm not familiar with
bfs layout, so I can't tell what's going on with the corrupted image syzbot
are messing with just by visual examination.  Is there, by any chance,
a preexisting file0 in the root of that bfs image?  Does mknod() succeed
there?  Because if it doesn't and what you have is a buggered image with
'file0' being there having zero link count, yes, renaming over it would
trigger warnings about detected corrupted filesystem - we are trying to
remove a link (on disk) to something that claims (on disk) to have 0 links
at the time of that operation; something is clearly wrong and deserves
a warning.

Again, that iput() in there is basically introducing random memory
corruption; it might make the warning go away, but it's not a fix.
Re: [PATCH] bfs: put a inode if link count is 0
Posted by Lizhi Xu 11 months, 2 weeks ago
On Thu, 9 Jan 2025 07:32:28 +0000, Al Viro wrote:
> > On Thu, 9 Jan 2025 06:22:16 +0000, Al Viro wrote:
> > > > The reproducer performs the rename operation on the file twice in succession
> > > > and changes the file to the same file name.  After the first rename operation,
> > > > the number of links in the inode is set to 0. In the second execution, the
> > > > same inode is used, resulting in a 0 value warning for i_nlink.
> > > >
> > > > To avoid this issue, put the target inode before exiting the bfs_rename.
> > >
> > >         This is completely insane - you get an extra drop of in-core inode
> > > refcount, which *will* end up with dangling pointer and memory corruption.
> > > Besides, there is a perfectly legitimate case when you open a file and
> > > rename something on top of it.  It MUST remain open and alive until the
> > > last in-core reference to inode goes away, which must not happen before
> > > close().
> > In the reproducer, changes the file to the same file, the same file name is
> > "file0", file0 uses mknod to create its inode and sets the i_nlink value to 1.
> > There is no operation to open file0 in the reproducer. Is this situation also
> > as you said?
> 
> I'm not sure I understand your sentence, to be honest.
> 
> Your patch is 100% wrong - you must *not*, under any circumstances, have
> ->rename() drop references to in-core struct inode instances.  It's *always*
> wrong; the reference to new_inode in new_dentry->d_inode remains there (as
> it ought to) and its contribution to new_inode refcount remains unchanged.
> It has nothing to do with ->i_nlink; you are decrementing ->i_count, which
> controls the lifetime of in-core struct inode instance.  As soon as that
> reaches zero, struct inode instance will be freed.  And destructor of
> new_dentry *will* call iput() on its ->d_inode, so you'll end up with
> attempt to decrement refcount of already freed memory object.
> 
> Again, that has nothing to do with ->i_nlink.  I'm not familiar with
> bfs layout, so I can't tell what's going on with the corrupted image syzbot
> are messing with just by visual examination.  Is there, by any chance,
> a preexisting file0 in the root of that bfs image?  Does mknod() succeed
> there?  Because if it doesn't and what you have is a buggered image with
> 'file0' being there having zero link count, yes, renaming over it would
> trigger warnings about detected corrupted filesystem - we are trying to
> remove a link (on disk) to something that claims (on disk) to have 0 links
> at the time of that operation; something is clearly wrong and deserves
> a warning.
> 
> Again, that iput() in there is basically introducing random memory
> corruption; it might make the warning go away, but it's not a fix.
I also have deep doubts about using iput in rename.
Thanks for your analysis.

Lizhi
[PATCH V2] bfs: prevent rename a target of link count is 0
Posted by Lizhi Xu 11 months ago
syzbot reported a warning in drop_nlink. [1]

The reproducer performs the rename operation on the file twice in succession
and changes the file to the same file name.  After the first rename operation,
the number of links in the inode is set to 0. In the second execution, the
same inode is used, resulting in a 0 value warning for i_nlink.

To avoid this issue, if the number of hard links to the target is 0 then
quit renaming.

[1]
WARNING: CPU: 0 PID: 5819 at fs/inode.c:407 drop_nlink+0xc4/0x110 fs/inode.c:407
Modules linked in:
CPU: 0 UID: 0 PID: 5819 Comm: syz-executor232 Not tainted 6.13.0-rc5-syzkaller-00161-g63676eefb7a0 #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 09/13/2024
RIP: 0010:drop_nlink+0xc4/0x110 fs/inode.c:407
Code: bb 70 07 00 00 be 08 00 00 00 e8 37 3b e7 ff f0 48 ff 83 70 07 00 00 5b 41 5c 41 5e 41 5f 5d c3 cc cc cc cc e8 8d 5b 83 ff 90 <0f> 0b 90 eb 83 44 89 e1 80 e1 07 80 c1 03 38 c1 0f 8c 5c ff ff ff
RSP: 0018:ffffc90003c6f950 EFLAGS: 00010293
RAX: ffffffff821c1843 RBX: 1ffff1100e5181f8 RCX: ffff8880351d9e00
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffffffff821c17c3 R09: 1ffffffff2030ad6
R10: dffffc0000000000 R11: fffffbfff2030ad7 R12: ffff8880728c0fc0
R13: dffffc0000000000 R14: ffff8880728c0f78 R15: dffffc0000000000
FS:  0000555562096380(0000) GS:ffff8880b8600000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000558ecae7b0d8 CR3: 0000000012a66000 CR4: 00000000003526f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 inode_dec_link_count include/linux/fs.h:2521 [inline]
 bfs_rename+0x44e/0x530 fs/bfs/dir.c:247
 vfs_rename+0xbdb/0xf00 fs/namei.c:5067
 do_renameat2+0xd94/0x13f0 fs/namei.c:5224
 __do_sys_rename fs/namei.c:5271 [inline]
 __se_sys_rename fs/namei.c:5269 [inline]
 __x64_sys_rename+0x82/0x90 fs/namei.c:5269
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83

Reported-by: syzbot+80e60df48923e1b7691d@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=80e60df48923e1b7691d
Signed-off-by: Lizhi Xu <lizhi.xu@windriver.com>
---
V1 -> V2: if the number of hard links to the target is 0 then quit renaming

 fs/bfs/dir.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c
index c375e22c4c0c..c78272a5bc60 100644
--- a/fs/bfs/dir.c
+++ b/fs/bfs/dir.c
@@ -227,6 +227,12 @@ static int bfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
 
 	error = -EPERM;
 	new_inode = d_inode(new_dentry);
+	if (new_inode) {
+		if (new_inode->i_nlink < 1) {
+			error = -EUCLEAN;
+			goto end_rename;
+		}
+	}
 	new_bh = bfs_find_entry(new_dir, &new_dentry->d_name, &new_de);
 
 	if (new_bh && !new_inode) {
-- 
2.43.0