> Add Boot-Regions verification specified in exFAT specification. > Note that the checksum type is strongly related to the raw structure, so > the'u32 'type is used to clarify the number of bits. > > Signed-off-by: Tetsuhiro Kohada <kohada.t2@xxxxxxxxx> Reviewed-by: Sungjong Seo <sj1557.seo@xxxxxxxxxxx> > --- > Changes in v2: > - rebase with patch 'optimize dir-cache' applied > - just print a warning when invalid exboot-signature detected > - print additional information when invalid boot-checksum detected > Changes in v3: > - based on '[PATCH 2/4 v3] exfat: separate the boot sector analysis' > Changes in v4: > - fix type of p_sig/p_chksum to __le32 > > fs/exfat/exfat_fs.h | 1 + > fs/exfat/misc.c | 14 +++++++++++++ > fs/exfat/super.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 65 insertions(+) > > diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index > 9673e2d31045..eebbe5a84b2b 100644 > --- a/fs/exfat/exfat_fs.h > +++ b/fs/exfat/exfat_fs.h > @@ -514,6 +514,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, > struct timespec64 *ts, > u8 *tz, __le16 *time, __le16 *date, u8 *time_cs); unsigned > short exfat_calc_chksum_2byte(void *data, int len, > unsigned short chksum, int type); > +u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type); > void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int > sync); void exfat_chain_set(struct exfat_chain *ec, unsigned int dir, > unsigned int size, unsigned char flags); diff --git > a/fs/exfat/misc.c b/fs/exfat/misc.c index ab7f88b1f6d3..b82d2dd5bd7c > 100644 > --- a/fs/exfat/misc.c > +++ b/fs/exfat/misc.c > @@ -151,6 +151,20 @@ unsigned short exfat_calc_chksum_2byte(void *data, > int len, > return chksum; > } > > +u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type) { > + int i; > + u8 *c = (u8 *)data; > + > + for (i = 0; i < len; i++, c++) { > + if (unlikely(type == CS_BOOT_SECTOR && > + (i == 106 || i == 107 || i == 112))) > + continue; > + chksum = ((chksum << 31) | (chksum >> 1)) + *c; > + } > + return chksum; > +} > + > void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int > sync) { > set_bit(EXFAT_SB_DIRTY, &EXFAT_SB(sb)->s_state); diff --git > a/fs/exfat/super.c b/fs/exfat/super.c index 6a1330be5a9a..405717e4e3ea > 100644 > --- a/fs/exfat/super.c > +++ b/fs/exfat/super.c > @@ -491,6 +491,50 @@ static int exfat_read_boot_sector(struct super_block > *sb) > return 0; > } > > +static int exfat_verify_boot_region(struct super_block *sb) { > + struct buffer_head *bh = NULL; > + u32 chksum = 0; > + __le32 *p_sig, *p_chksum; > + int sn, i; > + > + /* read boot sector sub-regions */ > + for (sn = 0; sn < 11; sn++) { > + bh = sb_bread(sb, sn); > + if (!bh) > + return -EIO; > + > + if (sn != 0 && sn <= 8) { > + /* extended boot sector sub-regions */ > + p_sig = (__le32 *)&bh->b_data[sb->s_blocksize - 4]; > + if (le32_to_cpu(*p_sig) != EXBOOT_SIGNATURE) > + exfat_warn(sb, "Invalid exboot-signature(sector > = %d): 0x%08x", > + sn, le32_to_cpu(*p_sig)); > + } > + > + chksum = exfat_calc_chksum32(bh->b_data, sb->s_blocksize, > + chksum, sn ? CS_DEFAULT : CS_BOOT_SECTOR); > + brelse(bh); > + } > + > + /* boot checksum sub-regions */ > + bh = sb_bread(sb, sn); > + if (!bh) > + return -EIO; > + > + for (i = 0; i < sb->s_blocksize; i += sizeof(u32)) { > + p_chksum = (__le32 *)&bh->b_data[i]; > + if (le32_to_cpu(*p_chksum) != chksum) { > + exfat_err(sb, "Invalid boot checksum (boot checksum : > 0x%08x, checksum : 0x%08x)", > + le32_to_cpu(*p_chksum), chksum); > + brelse(bh); > + return -EINVAL; > + } > + } > + brelse(bh); > + return 0; > +} > + > /* mount the file system volume */ > static int __exfat_fill_super(struct super_block *sb) { @@ -503,6 > +547,12 @@ static int __exfat_fill_super(struct super_block *sb) > goto free_bh; > } > > + ret = exfat_verify_boot_region(sb); > + if (ret) { > + exfat_err(sb, "invalid boot region"); > + goto free_bh; > + } > + > ret = exfat_create_upcase_table(sb); > if (ret) { > exfat_err(sb, "failed to load upcase table"); > -- > 2.25.1