nilfs2: fix shift-out-of-bounds due to too large exponent of block size
If field s_log_block_size of superblock data is corrupted and too large, init_nilfs() and load_nilfs() still can trigger a shift-out-of-bounds warning followed by a kernel panic (if panic_on_warn is set): shift exponent 38973 is too large for 32-bit type 'int' Call Trace: <TASK> dump_stack_lvl+0xcd/0x134 ubsan_epilogue+0xb/0x50 __ubsan_handle_shift_out_of_bounds.cold.12+0x17b/0x1f5 init_nilfs.cold.11+0x18/0x1d [nilfs2] nilfs_mount+0x9b5/0x12b0 [nilfs2] ... This fixes the issue by adding and using a new helper function for getting block size with sanity check. Link: https://lkml.kernel.org/r/20221027044306.42774-3-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
610a2a3d7d
commit
ebeccaaef6
@ -193,6 +193,34 @@ static int nilfs_store_log_cursor(struct the_nilfs *nilfs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nilfs_get_blocksize - get block size from raw superblock data
|
||||
* @sb: super block instance
|
||||
* @sbp: superblock raw data buffer
|
||||
* @blocksize: place to store block size
|
||||
*
|
||||
* nilfs_get_blocksize() calculates the block size from the block size
|
||||
* exponent information written in @sbp and stores it in @blocksize,
|
||||
* or aborts with an error message if it's too large.
|
||||
*
|
||||
* Return Value: On success, 0 is returned. If the block size is too
|
||||
* large, -EINVAL is returned.
|
||||
*/
|
||||
static int nilfs_get_blocksize(struct super_block *sb,
|
||||
struct nilfs_super_block *sbp, int *blocksize)
|
||||
{
|
||||
unsigned int shift_bits = le32_to_cpu(sbp->s_log_block_size);
|
||||
|
||||
if (unlikely(shift_bits >
|
||||
ilog2(NILFS_MAX_BLOCK_SIZE) - BLOCK_SIZE_BITS)) {
|
||||
nilfs_err(sb, "too large filesystem blocksize: 2 ^ %u KiB",
|
||||
shift_bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
*blocksize = BLOCK_SIZE << shift_bits;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* load_nilfs - load and recover the nilfs
|
||||
* @nilfs: the_nilfs structure to be released
|
||||
@ -246,11 +274,15 @@ int load_nilfs(struct the_nilfs *nilfs, struct super_block *sb)
|
||||
nilfs->ns_sbwtime = le64_to_cpu(sbp[0]->s_wtime);
|
||||
|
||||
/* verify consistency between two super blocks */
|
||||
blocksize = BLOCK_SIZE << le32_to_cpu(sbp[0]->s_log_block_size);
|
||||
err = nilfs_get_blocksize(sb, sbp[0], &blocksize);
|
||||
if (err)
|
||||
goto scan_error;
|
||||
|
||||
if (blocksize != nilfs->ns_blocksize) {
|
||||
nilfs_warn(sb,
|
||||
"blocksize differs between two super blocks (%d != %d)",
|
||||
blocksize, nilfs->ns_blocksize);
|
||||
err = -EINVAL;
|
||||
goto scan_error;
|
||||
}
|
||||
|
||||
@ -609,9 +641,11 @@ int init_nilfs(struct the_nilfs *nilfs, struct super_block *sb, char *data)
|
||||
if (err)
|
||||
goto failed_sbh;
|
||||
|
||||
blocksize = BLOCK_SIZE << le32_to_cpu(sbp->s_log_block_size);
|
||||
if (blocksize < NILFS_MIN_BLOCK_SIZE ||
|
||||
blocksize > NILFS_MAX_BLOCK_SIZE) {
|
||||
err = nilfs_get_blocksize(sb, sbp, &blocksize);
|
||||
if (err)
|
||||
goto failed_sbh;
|
||||
|
||||
if (blocksize < NILFS_MIN_BLOCK_SIZE) {
|
||||
nilfs_err(sb,
|
||||
"couldn't mount because of unsupported filesystem blocksize %d",
|
||||
blocksize);
|
||||
|
Loading…
Reference in New Issue
Block a user