OSDN Git Service

nilfs2: fix infinite loop in nilfs_mdt_get_block()
authorRyusuke Konishi <konishi.ryusuke@gmail.com>
Sun, 30 Apr 2023 19:30:46 +0000 (04:30 +0900)
committerAndrew Morton <akpm@linux-foundation.org>
Sat, 6 May 2023 17:10:07 +0000 (10:10 -0700)
If the disk image that nilfs2 mounts is corrupted and a virtual block
address obtained by block lookup for a metadata file is invalid,
nilfs_bmap_lookup_at_level() may return the same internal return code as
-ENOENT, meaning the block does not exist in the metadata file.

This duplication of return codes confuses nilfs_mdt_get_block(), causing
it to read and create a metadata block indefinitely.

In particular, if this happens to the inode metadata file, ifile,
semaphore i_rwsem can be left held, causing task hangs in lock_mount.

Fix this issue by making nilfs_bmap_lookup_at_level() treat virtual block
address translation failures with -ENOENT as metadata corruption instead
of returning the error code.

Link: https://lkml.kernel.org/r/20230430193046.6769-1-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+221d75710bde87fa0e97@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=221d75710bde87fa0e97
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/nilfs2/bmap.c

index 798a2c1..7a8f166 100644 (file)
@@ -67,20 +67,28 @@ int nilfs_bmap_lookup_at_level(struct nilfs_bmap *bmap, __u64 key, int level,
 
        down_read(&bmap->b_sem);
        ret = bmap->b_ops->bop_lookup(bmap, key, level, ptrp);
-       if (ret < 0) {
-               ret = nilfs_bmap_convert_error(bmap, __func__, ret);
+       if (ret < 0)
                goto out;
-       }
+
        if (NILFS_BMAP_USE_VBN(bmap)) {
                ret = nilfs_dat_translate(nilfs_bmap_get_dat(bmap), *ptrp,
                                          &blocknr);
                if (!ret)
                        *ptrp = blocknr;
+               else if (ret == -ENOENT) {
+                       /*
+                        * If there was no valid entry in DAT for the block
+                        * address obtained by b_ops->bop_lookup, then pass
+                        * internal code -EINVAL to nilfs_bmap_convert_error
+                        * to treat it as metadata corruption.
+                        */
+                       ret = -EINVAL;
+               }
        }
 
  out:
        up_read(&bmap->b_sem);
-       return ret;
+       return nilfs_bmap_convert_error(bmap, __func__, ret);
 }
 
 int nilfs_bmap_lookup_contig(struct nilfs_bmap *bmap, __u64 key, __u64 *ptrp,