Skip to content

Commit 92c5d1b

Browse files
konisakpm00
authored andcommitted
nilfs2: reject devices with insufficient block count
The current sanity check for nilfs2 geometry information lacks checks for the number of segments stored in superblocks, so even for device images that have been destructively truncated or have an unusually high number of segments, the mount operation may succeed. This causes out-of-bounds block I/O on file system block reads or log writes to the segments, the latter in particular causing "a_ops->writepages" to repeatedly fail, resulting in sync_inodes_sb() to hang. Fix this issue by checking the number of segments stored in the superblock and avoiding mounting devices that can cause out-of-bounds accesses. To eliminate the possibility of overflow when calculating the number of blocks required for the device from the number of segments, this also adds a helper function to calculate the upper bound on the number of segments and inserts a check using it. Link: https://lkml.kernel.org/r/20230526021332.3431-1-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> Reported-by: syzbot+7d50f1e54a12ba3aeae2@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=7d50f1e54a12ba3aeae2 Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 50d9278 commit 92c5d1b

1 file changed

Lines changed: 42 additions & 1 deletion

File tree

fs/nilfs2/the_nilfs.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,18 @@ unsigned long nilfs_nrsvsegs(struct the_nilfs *nilfs, unsigned long nsegs)
405405
100));
406406
}
407407

408+
/**
409+
* nilfs_max_segment_count - calculate the maximum number of segments
410+
* @nilfs: nilfs object
411+
*/
412+
static u64 nilfs_max_segment_count(struct the_nilfs *nilfs)
413+
{
414+
u64 max_count = U64_MAX;
415+
416+
do_div(max_count, nilfs->ns_blocks_per_segment);
417+
return min_t(u64, max_count, ULONG_MAX);
418+
}
419+
408420
void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs)
409421
{
410422
nilfs->ns_nsegments = nsegs;
@@ -414,6 +426,8 @@ void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs)
414426
static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
415427
struct nilfs_super_block *sbp)
416428
{
429+
u64 nsegments, nblocks;
430+
417431
if (le32_to_cpu(sbp->s_rev_level) < NILFS_MIN_SUPP_REV) {
418432
nilfs_err(nilfs->ns_sb,
419433
"unsupported revision (superblock rev.=%d.%d, current rev.=%d.%d). Please check the version of mkfs.nilfs(2).",
@@ -457,7 +471,34 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
457471
return -EINVAL;
458472
}
459473

460-
nilfs_set_nsegments(nilfs, le64_to_cpu(sbp->s_nsegments));
474+
nsegments = le64_to_cpu(sbp->s_nsegments);
475+
if (nsegments > nilfs_max_segment_count(nilfs)) {
476+
nilfs_err(nilfs->ns_sb,
477+
"segment count %llu exceeds upper limit (%llu segments)",
478+
(unsigned long long)nsegments,
479+
(unsigned long long)nilfs_max_segment_count(nilfs));
480+
return -EINVAL;
481+
}
482+
483+
nblocks = sb_bdev_nr_blocks(nilfs->ns_sb);
484+
if (nblocks) {
485+
u64 min_block_count = nsegments * nilfs->ns_blocks_per_segment;
486+
/*
487+
* To avoid failing to mount early device images without a
488+
* second superblock, exclude that block count from the
489+
* "min_block_count" calculation.
490+
*/
491+
492+
if (nblocks < min_block_count) {
493+
nilfs_err(nilfs->ns_sb,
494+
"total number of segment blocks %llu exceeds device size (%llu blocks)",
495+
(unsigned long long)min_block_count,
496+
(unsigned long long)nblocks);
497+
return -EINVAL;
498+
}
499+
}
500+
501+
nilfs_set_nsegments(nilfs, nsegments);
461502
nilfs->ns_crc_seed = le32_to_cpu(sbp->s_crc_seed);
462503
return 0;
463504
}

0 commit comments

Comments
 (0)