2020-05-31 09:35:07

by Tetsuhiro Kohada

[permalink] [raw]
Subject: [PATCH 3/4 v4] exfat: add boot region verification

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 <[email protected]>
---
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


2020-06-01 12:23:39

by Sungjong Seo

[permalink] [raw]
Subject: RE: [PATCH 3/4 v4] exfat: add boot region verification

> 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 <[email protected]>

Reviewed-by: Sungjong Seo <[email protected]>

> ---
> 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