mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Hui Peng <benquike@gmail.com>
To: brauner@kernel.org, viro@zeniv.linux.org.uk, kees@kernel.org
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] adfs: validate zone map parameters and clamp dm_endbit in adfs_map_layout()
Date: Sat, 19 Sep 2026 22:26:01 +0000	[thread overview]
Message-ID: <20260919222601.3793825-1-benquike@gmail.com> (raw)

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;

                 reply	other threads:[~2026-09-19 22:26 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260919222601.3793825-1-benquike@gmail.com \
    --to=benquike@gmail.com \
    --cc=brauner@kernel.org \
    --cc=kees@kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®