[PATCH] mtd: inftl: validate MediaHeader partition geometry before allocating tables

From: henrymei

Date: Wed Sep 02 2026 - 08:56:03 EST


From: Aohan Mei <henrymei@xxxxxxxxxxx>

find_boot_record() trusts the on-flash INFTL MediaHeader partition
fields without validating their relationship to each other or to the
device geometry.

The sanity check

if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits)

is evaluated in unsigned 32-bit arithmetic. With lastUnit < firstUnit
the subtraction wraps to a huge value and the check passes. Control
then reaches:

inftl->nb_boot_blocks = ip->firstUnit; /* loop bound */
inftl->nb_blocks = ip->lastUnit + 1; /* table size */

so PUtable/VUtable are allocated with lastUnit + 1 entries while the
boot-block marking loop writes firstUnit u16 entries:

for (i = 0; i < inftl->nb_boot_blocks; i++)
inftl->PUtable[i] = BLOCK_RESERVED;

A crafted MediaHeader (e.g. firstUnit=7000, lastUnit=3) turns a
kmalloc_array(4, 2) 8-byte allocation into a ~14 KB out-of-bounds
write:

BUG: KASAN: slab-out-of-bounds in find_boot_record
Write of size 2 ... 0 bytes to the right of allocated 8-byte region

The MediaHeader unit itself is also marked through PUtable[block]
without verifying that block lies within the described extent.

Reject inconsistent partition geometry (lastUnit < firstUnit, or
lastUnit beyond the device) as each partition entry is scanned, and
reject a boot record that lies outside the extent of the selected
partition. The existing virtualUnits check then subtracts values
that can no longer underflow.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: TencentOS Corvus AI <corvus@xxxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: CodeBuddy:Kimi-K3
Signed-off-by: Aohan Mei <henrymei@xxxxxxxxxxx>
---
drivers/mtd/inftlmount.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)

diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c
index 87e246a6f488..b0f01db95280 100644
--- a/drivers/mtd/inftlmount.c
+++ b/drivers/mtd/inftlmount.c
@@ -192,6 +192,23 @@ static int find_boot_record(struct INFTLrecord *inftl)
ip->lastUnit, ip->flags,
ip->spareUnits);

+ /*
+ * Reject inconsistent partition geometry before it is
+ * used: lastUnit < firstUnit would make the
+ * (lastUnit - firstUnit + 1) check below underflow,
+ * and lastUnit must stay within the device as it later
+ * bounds the PUtable/VUtable allocations.
+ */
+ if (ip->lastUnit < ip->firstUnit ||
+ ip->lastUnit >= inftl->nb_blocks) {
+ pr_warn("INFTL: Media Header "
+ "Partition %d sanity check failed:\n"
+ " firstUnit %d lastUnit %d "
+ "(nb_blocks %d)\n",
+ i, ip->firstUnit, ip->lastUnit,
+ inftl->nb_blocks);
+ return -1;
+ }
if (ip->Reserved0 != ip->firstUnit) {
struct erase_info *instr = &inftl->instr;

@@ -233,6 +250,18 @@ static int find_boot_record(struct INFTLrecord *inftl)
return -1;
}

+ /*
+ * The boot record unit must lie within the described
+ * extent; it is later marked through PUtable[block].
+ */
+ if (block > ip->lastUnit) {
+ pr_warn("INFTL: Media Header "
+ "Partition %d sanity check failed:\n"
+ " boot record unit %d beyond "
+ "lastUnit %d\n",
+ i, block, ip->lastUnit);
+ return -1;
+ }
inftl->nb_boot_blocks = ip->firstUnit;
inftl->numvunits = ip->virtualUnits;
if (inftl->numvunits > (inftl->nb_blocks -
--
2.43.7