mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] adfs: validate zone map parameters and clamp dm_endbit in adfs_map_layout()
@ 2026-09-19 22:26 Hui Peng
  0 siblings, 0 replies; only message in thread
From: Hui Peng @ 2026-09-19 22:26 UTC (permalink / raw)
  To: brauner, viro, kees; +Cc: linux-fsdevel, linux-kernel

In fs/adfs/map.c and fs/adfs/super.c, validate nzones and zone_spare on
mount to prevent divide-by-zero in adfs_map_lookup() and adfs_statfs(),
and clamp dm_endbit / map offsets to the underlying buffer_head block
size in adfs_map_layout(), lookup_zone(), and scan_free_map().

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
diff --git a/fs/adfs/map.c b/fs/adfs/map.c
index 9d535a2ca2d1..6ca7ebf22388 100644
--- a/fs/adfs/map.c
+++ b/fs/adfs/map.c
@@ -68,17 +68,25 @@ static DEFINE_RWLOCK(adfs_map_lock);
 static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
 		       const u32 frag_id, unsigned int *offset)
 {
-	const unsigned int endbit = dm->dm_endbit;
+	const unsigned int max_endbit = dm->dm_bh->b_size * 8;
+	const unsigned int endbit = min(dm->dm_endbit, max_endbit);
 	const u32 idmask = (1 << idlen) - 1;
 	unsigned char *map = dm->dm_bh->b_data;
 	unsigned int start = dm->dm_startbit;
-	unsigned int freelink, fragend;
-	u32 frag;
+	unsigned int freelink, fragend = 0;
+	u32 frag = 0;
+
+	if (8 + idlen >= endbit || (8 >> 3) + 4 > dm->dm_bh->b_size)
+		goto error;
 
 	frag = GET_FRAG_ID(map, 8, idmask & 0x7fff);
 	freelink = frag ? 8 + frag : 0;
 
 	do {
+		if (start + idlen >= endbit ||
+		    (start >> 3) + 4 > dm->dm_bh->b_size)
+			goto error;
+
 		frag = GET_FRAG_ID(map, start, idmask);
 
 		fragend = find_next_bit_le(map, endbit, start + idlen);
@@ -114,15 +122,19 @@ static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
 static unsigned int
 scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
 {
-	const unsigned int endbit = dm->dm_endbit;
+	const unsigned int max_endbit = dm->dm_bh->b_size * 8;
+	const unsigned int endbit = min(dm->dm_endbit, max_endbit);
 	const unsigned int idlen  = asb->s_idlen;
 	const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
 	const u32 idmask = (1 << frag_idlen) - 1;
 	unsigned char *map = dm->dm_bh->b_data;
-	unsigned int start = 8, fragend;
+	unsigned int start = 8, fragend = 0;
 	u32 frag;
 	unsigned long total = 0;
 
+	if (start + idlen >= endbit || (start >> 3) + 4 > dm->dm_bh->b_size)
+		goto error;
+
 	/*
 	 * get fragment id
 	 */
@@ -137,6 +149,9 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
 
 	do {
 		start += frag;
+		if (start + idlen >= endbit ||
+		    (start >> 3) + 4 > dm->dm_bh->b_size)
+			goto error;
 
 		frag = GET_FRAG_ID(map, start, idmask);
 
@@ -310,9 +325,12 @@ static void adfs_map_layout(struct adfs_discmap *dm, unsigned int nzones,
 			    struct adfs_discrecord *dr)
 {
 	unsigned int zone, zone_size;
+	unsigned int max_endbit = 8U << dr->log2secsize;
 	u64 size;
 
-	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
+	zone_size = max_endbit - le16_to_cpu(dr->zone_spare);
+	if (32 + zone_size > max_endbit)
+		zone_size = max_endbit - 32;
 
 	dm[0].dm_bh       = NULL;
 	dm[0].dm_startblk = 0;
@@ -327,7 +345,13 @@ static void adfs_map_layout(struct adfs_discmap *dm, unsigned int nzones,
 	}
 
 	size = adfs_disc_size(dr) >> dr->log2bpmb;
-	size -= (nzones - 1) * zone_size - ADFS_DR_SIZE_BITS;
+	size += ADFS_DR_SIZE_BITS;
+	if (size > (u64)(nzones - 1) * zone_size)
+		size -= (u64)(nzones - 1) * zone_size;
+	else
+		size = 0;
+	if (size > zone_size)
+		size = zone_size;
 	dm[nzones - 1].dm_endbit = 32 + size;
 }
 
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index 888aa81a6b39..5b570a4769da 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -87,6 +87,16 @@ static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
 		if (dr->unused52[i] != 0)
 			return 1;
 
+	/* At least one zone and one allocation ID per zone are required */
+	if (!dr->nzones && !dr->nzones_high)
+		return 1;
+	if (le16_to_cpu(dr->zone_spare) < 32 ||
+	    le16_to_cpu(dr->zone_spare) >= (8U << dr->log2secsize))
+		return 1;
+	if (((1U << dr->log2secsize) * 8 - le16_to_cpu(dr->zone_spare)) <
+	    (dr->idlen + 1))
+		return 1;
+
 	return 0;
 }
 
@@ -181,7 +191,8 @@ static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 	buf->f_type    = ADFS_SUPER_MAGIC;
 	buf->f_namelen = sbi->s_namelen;
 	buf->f_bsize   = sb->s_blocksize;
-	buf->f_ffree   = (long)(buf->f_bfree * buf->f_files) / (long)buf->f_blocks;
+	buf->f_ffree   = buf->f_blocks ?
+			 (long)(buf->f_bfree * buf->f_files) / (long)buf->f_blocks : 0;
 	buf->f_fsid    = u64_to_fsid(id);
 
 	return 0;

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-19 22:26 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-19 22:26 [PATCH] adfs: validate zone map parameters and clamp dm_endbit in adfs_map_layout() Hui Peng

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

all inboxes | Powered by JetHome®