On 2018/4/28 12:26, Jaegeuk Kim wrote:
> On 04/28, Linus Torvalds wrote:
>> On Fri, Apr 27, 2018 at 7:32 PM Jaegeuk Kim <jaeg...@kernel.org> wrote:
>>
>>> +       blocks_per_seg = 1 << le32_to_cpu(raw_super->log_blocks_per_seg);
>>
>> This can overflow and do random things.
> 
> Actually it is always 512 by the existing check above.
> 
>>
>>> +       if (segment_count < segs_per_sec * total_sections) {
>>
>> The multiply can overflow. Which might not matter in this context, since
>> the resulting value will be smaller than the non-overflowed "real" value,
>> so it makes the check _stricter_ in the error case, but I think that it
>> might be good to verify the two values you are multiplying before doing the
>> multiply..
> 
> Ah, yes.
> 
>>
>>> +       if (segment_count > F2FS_MAX_SEGMENT ||
>>> +                               segment_count < F2FS_MIN_SEGMENTS) {
>> ...
>>> +       if ((segment_count << 9) > le32_to_cpu(raw_super->block_count)) {
>>
>> The "segment_count << 9" will overflow when segment_count ==
>> F2FS_MAX_SEGMENT.
> 
> Right.
> 
>>
>>> +       if ((le32_to_cpu(raw_super->extension_count) +
>>> +                       raw_super->hot_ext_count) > F2FS_MAX_EXTENSION) {
>>
>> The addition could overflow.
> 
> Yup.
> 
>>
>>> +       if (le32_to_cpu(raw_super->cp_payload) >
>>> +                               (blocks_per_seg - F2FS_CP_PACKS)) {
>>
>> You haven't checked 'blocks_per_seg', so this subtraction could underflow.
>>
>> But if you add checks for the whole "log_blocks_per_seg" (to handle that
>> first "shift can overflow" issue), you'll fix this one too, I think.
>>
>> You just need to make sure that log_blocks_per_seg >=1 and <= 31. But there
>> may be other limits that I didn't notice.
>>
>> I didn't really read it all that carefully, so I might have missed
>> something. The above is from just a quick scan.
> 
> Thank you. How about this?
> 
>>From a1bc37e10c645e351a6d2b65704d630bde882d8d Mon Sep 17 00:00:00 2001
> From: Jaegeuk Kim <jaeg...@kernel.org>
> Date: Fri, 27 Apr 2018 19:03:22 -0700
> Subject: [PATCH] f2fs: enhance sanity_check_raw_super() to avoid potential
>  overflows
> 
> In order to avoid the below overflow issue, we should have checked the
> boundaries in superblock before reaching out to allocation. As Linux 
> suggested,
> the right place should be sanity_check_raw_super().
> 
> Dr Silvio Cesare of InfoSect reported:
> 
> There are integer overflows with using the cp_payload superblock field in the
> f2fs filesystem potentially leading to memory corruption.
> 
> include/linux/f2fs_fs.h
> 
> struct f2fs_super_block {
> ...
>         __le32 cp_payload;
> 
> fs/f2fs/f2fs.h
> 
> typedef u32 block_t;    /*
>                          * should not change u32, since it is the on-disk 
> block
>                          * address format, __le32.
>                          */
> ...
> 
> static inline block_t __cp_payload(struct f2fs_sb_info *sbi)
> {
>         return le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
> }
> 
> fs/f2fs/checkpoint.c
> 
>         block_t start_blk, orphan_blocks, i, j;
> ...
>         start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
>         orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
> 
> +++ integer overflows
> 
> ...
>         unsigned int cp_blks = 1 + __cp_payload(sbi);
> ...
>         sbi->ckpt = kzalloc(cp_blks * blk_size, GFP_KERNEL);
> 
> +++ integer overflow leading to incorrect heap allocation.
> 
>         int cp_payload_blks = __cp_payload(sbi);
> ...
>         ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks +
>                         orphan_blocks);
> 
> +++ sign bug and integer overflow
> 
> ...
>         for (i = 1; i < 1 + cp_payload_blks; i++)
> 
> +++ integer overflow
> 
> ...
> 
>       sbi->max_orphans = (sbi->blocks_per_seg - F2FS_CP_PACKS -
>                         NR_CURSEG_TYPE - __cp_payload(sbi)) *
>                                 F2FS_ORPHANS_PER_BLOCK;
> 
> +++ integer overflow
> 
> Reported-by: Greg KH <g...@kroah.com>
> Reported-by: Silvio Cesare <silvio.ces...@gmail.com>
> Suggested-by: Linus Torvalds <torva...@linux-foundation.org>
> Signed-off-by: Jaegeuk Kim <jaeg...@kernel.org>

+Cc f2fs mailing list

Reviewed-by: Chao Yu <yuch...@huawei.com>

Thanks,

> ---
>  fs/f2fs/super.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 68 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index a2432b9747eb..c8e5fe5d71fe 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -2135,6 +2135,8 @@ static inline bool sanity_check_area_boundary(struct 
> f2fs_sb_info *sbi,
>  static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
>                               struct buffer_head *bh)
>  {
> +     block_t segment_count, segs_per_sec, secs_per_zone;
> +     block_t total_sections, blocks_per_seg;
>       struct f2fs_super_block *raw_super = (struct f2fs_super_block *)
>                                       (bh->b_data + F2FS_SUPER_OFFSET);
>       struct super_block *sb = sbi->sb;
> @@ -2191,6 +2193,72 @@ static int sanity_check_raw_super(struct f2fs_sb_info 
> *sbi,
>               return 1;
>       }
>  
> +     segment_count = le32_to_cpu(raw_super->segment_count);
> +     segs_per_sec = le32_to_cpu(raw_super->segs_per_sec);
> +     secs_per_zone = le32_to_cpu(raw_super->secs_per_zone);
> +     total_sections = le32_to_cpu(raw_super->section_count);
> +
> +     /* blocks_per_seg should be 512, given the above check */
> +     blocks_per_seg = 1 << le32_to_cpu(raw_super->log_blocks_per_seg);
> +
> +     if (segment_count > F2FS_MAX_SEGMENT ||
> +                             segment_count < F2FS_MIN_SEGMENTS) {
> +             f2fs_msg(sb, KERN_INFO,
> +                     "Invalid segment count (%u)",
> +                     segment_count);
> +             return 1;
> +     }
> +
> +     if (total_sections > segment_count ||
> +                     total_sections < F2FS_MIN_SEGMENTS ||
> +                     segs_per_sec > segment_count || !segs_per_sec) {
> +             f2fs_msg(sb, KERN_INFO,
> +                     "Invalid segment/section count (%u, %u x %u)",
> +                     segment_count, total_sections, segs_per_sec);
> +             return 1;
> +     }
> +
> +     if ((segment_count / segs_per_sec) < total_sections) {
> +             f2fs_msg(sb, KERN_INFO,
> +                     "Small segment_count (%u < %u * %u)",
> +                     segment_count, segs_per_sec, total_sections);
> +             return 1;
> +     }
> +
> +     if (segment_count > (le32_to_cpu(raw_super->block_count) >> 9)) {
> +             f2fs_msg(sb, KERN_INFO,
> +                     "Wrong segment_count / block_count (%u > %u)",
> +                     segment_count, le32_to_cpu(raw_super->block_count));
> +             return 1;
> +     }
> +
> +     if (secs_per_zone > total_sections) {
> +             f2fs_msg(sb, KERN_INFO,
> +                     "Wrong secs_per_zone (%u > %u)",
> +                     secs_per_zone, total_sections);
> +             return 1;
> +     }
> +     if (le32_to_cpu(raw_super->extension_count) > F2FS_MAX_EXTENSION ||
> +                     raw_super->hot_ext_count > F2FS_MAX_EXTENSION ||
> +                     (le32_to_cpu(raw_super->extension_count) +
> +                     raw_super->hot_ext_count) > F2FS_MAX_EXTENSION) {
> +             f2fs_msg(sb, KERN_INFO,
> +                     "Corrupted extension count (%u + %u > %u)",
> +                     le32_to_cpu(raw_super->extension_count),
> +                     raw_super->hot_ext_count,
> +                     F2FS_MAX_EXTENSION);
> +             return 1;
> +     }
> +
> +     if (le32_to_cpu(raw_super->cp_payload) >
> +                             (blocks_per_seg - F2FS_CP_PACKS)) {
> +             f2fs_msg(sb, KERN_INFO,
> +                     "Insane cp_payload (%u > %u)",
> +                     le32_to_cpu(raw_super->cp_payload),
> +                     blocks_per_seg - F2FS_CP_PACKS);
> +             return 1;
> +     }
> +
>       /* check reserved ino info */
>       if (le32_to_cpu(raw_super->node_ino) != 1 ||
>               le32_to_cpu(raw_super->meta_ino) != 2 ||
> @@ -2203,13 +2271,6 @@ static int sanity_check_raw_super(struct f2fs_sb_info 
> *sbi,
>               return 1;
>       }
>  
> -     if (le32_to_cpu(raw_super->segment_count) > F2FS_MAX_SEGMENT) {
> -             f2fs_msg(sb, KERN_INFO,
> -                     "Invalid segment count (%u)",
> -                     le32_to_cpu(raw_super->segment_count));
> -             return 1;
> -     }
> -
>       /* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */
>       if (sanity_check_area_boundary(sbi, bh))
>               return 1;
> 


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to