Blame SOURCES/0032-exfat-add-boot-region-verification.patch

Kmods SIG 9e3ffb
From 476189c0ef3b658de3f6b89fd0fdeb6dc451b564 Mon Sep 17 00:00:00 2001
Kmods SIG 9e3ffb
From: Tetsuhiro Kohada <kohada.t2@gmail.com>
Kmods SIG 9e3ffb
Date: Sun, 31 May 2020 18:30:17 +0900
Kmods SIG 9e3ffb
Subject: [Backport 476189c0ef3b] exfat: add boot region verification
Kmods SIG 9e3ffb
Kmods SIG 9e3ffb
Add Boot-Regions verification specified in exFAT specification.
Kmods SIG 9e3ffb
Note that the checksum type is strongly related to the raw structure,
Kmods SIG 9e3ffb
so the'u32 'type is used to clarify the number of bits.
Kmods SIG 9e3ffb
Kmods SIG 9e3ffb
Signed-off-by: Tetsuhiro Kohada <kohada.t2@gmail.com>
Kmods SIG 9e3ffb
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Kmods SIG 9e3ffb
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
Kmods SIG 9e3ffb
---
Kmods SIG 9e3ffb
 src/exfat_fs.h |  1 +
Kmods SIG 9e3ffb
 src/misc.c     | 14 +++++++++++++
Kmods SIG 9e3ffb
 src/super.c    | 50 +++++++++++++++++++++++++++++++++++++++++++++
Kmods SIG 9e3ffb
 3 files changed, 65 insertions(+)
Kmods SIG 9e3ffb
Kmods SIG 9e3ffb
diff --git a/src/exfat_fs.h b/src/exfat_fs.h
Kmods SIG 9e3ffb
index 911f58b93f3d257bce04b645a2ada62d0f51a3c0..8c2a70bfaa10d4743c46aad74254e363d4079c74 100644
Kmods SIG 9e3ffb
--- a/src/exfat_fs.h
Kmods SIG 9e3ffb
+++ b/src/exfat_fs.h
Kmods SIG 9e3ffb
@@ -514,6 +514,7 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
Kmods SIG 9e3ffb
 		u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
Kmods SIG 9e3ffb
 unsigned short exfat_calc_chksum_2byte(void *data, int len,
Kmods SIG 9e3ffb
 		unsigned short chksum, int type);
Kmods SIG 9e3ffb
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
Kmods SIG 9e3ffb
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
Kmods SIG 9e3ffb
 void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
Kmods SIG 9e3ffb
 		unsigned int size, unsigned char flags);
Kmods SIG 9e3ffb
diff --git a/src/misc.c b/src/misc.c
Kmods SIG 9e3ffb
index ab7f88b1f6d3065cd3fca409526d50e833bbbfb0..b82d2dd5bd7cb291ffe5ed5091c9214593c32a69 100644
Kmods SIG 9e3ffb
--- a/src/misc.c
Kmods SIG 9e3ffb
+++ b/src/misc.c
Kmods SIG 9e3ffb
@@ -151,6 +151,20 @@ unsigned short exfat_calc_chksum_2byte(void *data, int len,
Kmods SIG 9e3ffb
 	return chksum;
Kmods SIG 9e3ffb
 }
Kmods SIG 9e3ffb
 
Kmods SIG 9e3ffb
+u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type)
Kmods SIG 9e3ffb
+{
Kmods SIG 9e3ffb
+	int i;
Kmods SIG 9e3ffb
+	u8 *c = (u8 *)data;
Kmods SIG 9e3ffb
+
Kmods SIG 9e3ffb
+	for (i = 0; i < len; i++, c++) {
Kmods SIG 9e3ffb
+		if (unlikely(type == CS_BOOT_SECTOR &&
Kmods SIG 9e3ffb
+			     (i == 106 || i == 107 || i == 112)))
Kmods SIG 9e3ffb
+			continue;
Kmods SIG 9e3ffb
+		chksum = ((chksum << 31) | (chksum >> 1)) + *c;
Kmods SIG 9e3ffb
+	}
Kmods SIG 9e3ffb
+	return chksum;
Kmods SIG 9e3ffb
+}
Kmods SIG 9e3ffb
+
Kmods SIG 9e3ffb
 void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync)
Kmods SIG 9e3ffb
 {
Kmods SIG 9e3ffb
 	set_bit(EXFAT_SB_DIRTY, &EXFAT_SB(sb)->s_state);
Kmods SIG 9e3ffb
diff --git a/src/super.c b/src/super.c
Kmods SIG 9e3ffb
index 6a1330be5a9a4b58e2a39b197788f139bfbaba4f..405717e4e3eac6b3233e1e57b0bcc70f56f1c42f 100644
Kmods SIG 9e3ffb
--- a/src/super.c
Kmods SIG 9e3ffb
+++ b/src/super.c
Kmods SIG 9e3ffb
@@ -491,6 +491,50 @@ static int exfat_read_boot_sector(struct super_block *sb)
Kmods SIG 9e3ffb
 	return 0;
Kmods SIG 9e3ffb
 }
Kmods SIG 9e3ffb
 
Kmods SIG 9e3ffb
+static int exfat_verify_boot_region(struct super_block *sb)
Kmods SIG 9e3ffb
+{
Kmods SIG 9e3ffb
+	struct buffer_head *bh = NULL;
Kmods SIG 9e3ffb
+	u32 chksum = 0;
Kmods SIG 9e3ffb
+	__le32 *p_sig, *p_chksum;
Kmods SIG 9e3ffb
+	int sn, i;
Kmods SIG 9e3ffb
+
Kmods SIG 9e3ffb
+	/* read boot sector sub-regions */
Kmods SIG 9e3ffb
+	for (sn = 0; sn < 11; sn++) {
Kmods SIG 9e3ffb
+		bh = sb_bread(sb, sn);
Kmods SIG 9e3ffb
+		if (!bh)
Kmods SIG 9e3ffb
+			return -EIO;
Kmods SIG 9e3ffb
+
Kmods SIG 9e3ffb
+		if (sn != 0 && sn <= 8) {
Kmods SIG 9e3ffb
+			/* extended boot sector sub-regions */
Kmods SIG 9e3ffb
+			p_sig = (__le32 *)&bh->b_data[sb->s_blocksize - 4];
Kmods SIG 9e3ffb
+			if (le32_to_cpu(*p_sig) != EXBOOT_SIGNATURE)
Kmods SIG 9e3ffb
+				exfat_warn(sb, "Invalid exboot-signature(sector = %d): 0x%08x",
Kmods SIG 9e3ffb
+					   sn, le32_to_cpu(*p_sig));
Kmods SIG 9e3ffb
+		}
Kmods SIG 9e3ffb
+
Kmods SIG 9e3ffb
+		chksum = exfat_calc_chksum32(bh->b_data, sb->s_blocksize,
Kmods SIG 9e3ffb
+			chksum, sn ? CS_DEFAULT : CS_BOOT_SECTOR);
Kmods SIG 9e3ffb
+		brelse(bh);
Kmods SIG 9e3ffb
+	}
Kmods SIG 9e3ffb
+
Kmods SIG 9e3ffb
+	/* boot checksum sub-regions */
Kmods SIG 9e3ffb
+	bh = sb_bread(sb, sn);
Kmods SIG 9e3ffb
+	if (!bh)
Kmods SIG 9e3ffb
+		return -EIO;
Kmods SIG 9e3ffb
+
Kmods SIG 9e3ffb
+	for (i = 0; i < sb->s_blocksize; i += sizeof(u32)) {
Kmods SIG 9e3ffb
+		p_chksum = (__le32 *)&bh->b_data[i];
Kmods SIG 9e3ffb
+		if (le32_to_cpu(*p_chksum) != chksum) {
Kmods SIG 9e3ffb
+			exfat_err(sb, "Invalid boot checksum (boot checksum : 0x%08x, checksum : 0x%08x)",
Kmods SIG 9e3ffb
+				  le32_to_cpu(*p_chksum), chksum);
Kmods SIG 9e3ffb
+			brelse(bh);
Kmods SIG 9e3ffb
+			return -EINVAL;
Kmods SIG 9e3ffb
+		}
Kmods SIG 9e3ffb
+	}
Kmods SIG 9e3ffb
+	brelse(bh);
Kmods SIG 9e3ffb
+	return 0;
Kmods SIG 9e3ffb
+}
Kmods SIG 9e3ffb
+
Kmods SIG 9e3ffb
 /* mount the file system volume */
Kmods SIG 9e3ffb
 static int __exfat_fill_super(struct super_block *sb)
Kmods SIG 9e3ffb
 {
Kmods SIG 9e3ffb
@@ -503,6 +547,12 @@ static int __exfat_fill_super(struct super_block *sb)
Kmods SIG 9e3ffb
 		goto free_bh;
Kmods SIG 9e3ffb
 	}
Kmods SIG 9e3ffb
 
Kmods SIG 9e3ffb
+	ret = exfat_verify_boot_region(sb);
Kmods SIG 9e3ffb
+	if (ret) {
Kmods SIG 9e3ffb
+		exfat_err(sb, "invalid boot region");
Kmods SIG 9e3ffb
+		goto free_bh;
Kmods SIG 9e3ffb
+	}
Kmods SIG 9e3ffb
+
Kmods SIG 9e3ffb
 	ret = exfat_create_upcase_table(sb);
Kmods SIG 9e3ffb
 	if (ret) {
Kmods SIG 9e3ffb
 		exfat_err(sb, "failed to load upcase table");
Kmods SIG 9e3ffb
-- 
Kmods SIG 9e3ffb
2.31.1
Kmods SIG 9e3ffb