Linux filesystem development
 help / color / mirror / Atom feed
* [PATCH v1 1/3] exfat: add volume limit bounds checks
@ 2026-05-05 12:28 David Timber
  2026-05-05 12:28 ` [PATCH v1 2/3] exfat: print warning upon block size calibration David Timber
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: David Timber @ 2026-05-05 12:28 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo; +Cc: linux-fsdevel, David Timber

If the user inadvertenly truncates an exFAT volume(mistakenly shrinks
the partition or simply dd'ing from a larger removable device to a
smaller one. eg: device marketed as having 8GB capatity < 8GiB), the
kernel exFAT obliviously mounts the volume and operates on it. No error
is reported to userspace unless the filesystem is accessed with O_SYNC
or O_DIRECT.

Off by one sector test:

	# truncate -s 1073741824 img
	# mkfs.exfat img
	# truncate -s 1073741312 img
	# mount -t exfat img ...

The existing filesystem implementations, prime examples being XFS and
ext*, refuse to mount the volume with such condition. Introduce the
checks similar checks in-place to exFAT.

Also, to prevent UB, add checks against exFAT volumes with maliciously
a crafted main boot sectors with the ClusterCount field equal to or
larger than (2^32 - 11) as per format spec.

Link: https://github.com/exfatprogs/exfatprogs/issues/353
Signed-off-by: David Timber <dxdt@dev.snart.me>
---
 fs/exfat/super.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 46 insertions(+), 2 deletions(-)

diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 95d87e2d7717..15e99766cb8c 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -431,6 +431,7 @@ static int exfat_read_boot_sector(struct super_block *sb)
 {
 	struct boot_sector *p_boot;
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	unsigned int nb_clusters;
 
 	/* set block size to read super block */
 	if (!sb_min_blocksize(sb, 512)) {
@@ -501,8 +502,17 @@ static int exfat_read_boot_sector(struct super_block *sb)
 	sbi->data_start_sector = le32_to_cpu(p_boot->clu_offset);
 	sbi->num_sectors = le64_to_cpu(p_boot->vol_length);
 	/* because the cluster index starts with 2 */
-	sbi->num_clusters = le32_to_cpu(p_boot->clu_count) +
-		EXFAT_RESERVED_CLUSTERS;
+	nb_clusters = le32_to_cpu(p_boot->clu_count);
+	/*
+	 * The inclusive comparison in the following check seems a bit off(quite
+	 * literally), but the exFAT format section 3.1.9 says
+	 * "lesser of the following". Aye, aye, captain.
+	 */
+	if (nb_clusters >= EXFAT_MAX_NUM_CLUSTER) {
+		exfat_err(sb, "bogus number of clusters : %u", nb_clusters);
+		return -EINVAL;
+	}
+	sbi->num_clusters = nb_clusters + EXFAT_RESERVED_CLUSTERS;
 
 	sbi->root_dir = le32_to_cpu(p_boot->root_cluster);
 	sbi->dentries_per_clu = 1 <<
@@ -591,6 +601,34 @@ static int exfat_verify_boot_region(struct super_block *sb)
 	return 0;
 }
 
+static inline int exfat_check_volume_sizes(struct super_block *sb)
+{
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+	/* last sector of exFAT volume == end of last cluster */
+	const sector_t lc_end = exfat_cluster_to_sector(sbi, sbi->num_clusters);
+	/*
+	 * Do the calculations in bytes because the blocksize may have been
+	 * calibrated in exfat_calibrate_blocksize(), and bdev_nr_sectors()
+	 * always reports in 512-byte units.
+	 */
+	const unsigned long long vol_size = EXFAT_BLK_TO_B(sbi->num_sectors, sb);
+	const unsigned long long bdev_size = (unsigned long long)bdev_nr_bytes(sb->s_bdev);
+
+	if (sbi->num_sectors < (unsigned long long)lc_end) {
+		exfat_err(sb, "number of clusters out of volume length bounds : num_sectors=%llu, last_cluster_sector=%lld",
+			  sbi->num_sectors, lc_end);
+		return -EINVAL;
+	}
+
+	if (bdev_size < vol_size) {
+		exfat_err(sb, "volume length out of bounds : dev=%llu, vol=%lld",
+			  bdev_size, vol_size);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /* mount the file system volume */
 static int __exfat_fill_super(struct super_block *sb,
 		struct exfat_chain *root_clu)
@@ -610,6 +648,12 @@ static int __exfat_fill_super(struct super_block *sb,
 		goto free_bh;
 	}
 
+	ret = exfat_check_volume_sizes(sb);
+	if (ret) {
+		exfat_warn(sb, "volume bounds check failed. Please run fsck");
+		goto free_bh;
+	}
+
 	/*
 	 * Call exfat_count_num_cluster() before searching for up-case and
 	 * bitmap directory entries to avoid infinite loop if they are missing
-- 
2.53.0.1.ga224b40d3f.dirty


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-05-10 23:17 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-05 12:28 [PATCH v1 1/3] exfat: add volume limit bounds checks David Timber
2026-05-05 12:28 ` [PATCH v1 2/3] exfat: print warning upon block size calibration David Timber
2026-05-05 12:28 ` [PATCH v1 3/3] exfat: fix memory leak (upcase table) David Timber
2026-05-07 12:04   ` Yuezhang.Mo
2026-05-10 23:10     ` David Timber
2026-05-07 12:04 ` [PATCH v1 1/3] exfat: add volume limit bounds checks Yuezhang.Mo
2026-05-10 23:17   ` David Timber

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox