daddr_t (int32) truncation of block address for >1TB MMC/SD media
Summary
mmcsd_rw and mmcsd_delete declare block/end as daddr_t which is __int32_t. For SDXC/large eMMC beyond 2^31 sectors (~1TiB at 512B) bio->bio_offset/sz silently truncates to possibly-negative 32-bit value producing wrong MMC command arguments and depending on card behavior either spurious EIO or data corruption at wrong sector. block=bio->bio_offset/sz assignment truncates to int32. cmd.arg=block reinterprets negative as uint32 0xFFFFFFFF sent to card. Affects mmcsd_delete too. [VERIFIED pass2: reproduced - daddr_t int32 truncation makes 2TiB media issue sector 0 and 4TiB issue 0x3039 wrong-sector MMC commands; reject-beyond-32-bit fix A/B EIO]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2480 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.0 KB | β raw | |
| VERDICT.md | β | 2.0 KB | β raw | |
| build.sh | β | 99 B | view raw | |
| run.log | β | 488 B | view raw | |
| env.txt | β | 963 B | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| manifest.json | β | 800 B | view raw | |
| verdict.json | β | 2.3 KB | view raw |
DF-2480 -- mmcsd daddr_t (int32) truncation of block addresses past 1 TiB
Verification used the shared rig tree at /root/rigs on the audit guest (see manifest). The rig for this finding: dfv_mmcsd (rig.c = harness, gen.sh = verbatim extractor, dfv_*_extract.h = generated production text). The decisive outputs below were captured live on the guest during the 2026-10-24 verification session; full session logs are summarized per run.
Build
sh build.sh # extracts verbatim driver text + builds the KLD
Run (guest, root)
kldload ./dfv_rig.ko
kldload dfv_mmcsd.ko; for i in 0 1 2 3 4; do sysctl kern.dfv_mmcsd_run=$i >/dev/null; sysctl -n kern.dfv_mmcsd_status; done
Expected
run.log: 2TiB -> cmd.arg=0x00000000 (expected 0x100000000) TRUNCATED/WRONG; 4TiB -> 0x00003039; control 0x3e8 CORRECT.
Fix validation
fix.diff switches to u64 block math and rejects beyond-32-bit blocks; A/B shows no wrong-sector command.
Verdict: reproduced / impact=corruption (see verdict.json, VERDICT.md)
DF-2480 verdict -- mmcsd daddr_t (int32) truncation of block addresses past 1 TiB
status: reproduced reproduced: 1 impact: corruption confidence: certain attempts: 1 runtime_sec: 600
The verbatim mmcsd_rw/mmcsd_delete (extracted whole) assign bio->bio_offset/sz into daddr_t (= __int32_t, sys/types.h:80) block/end. Control (1000 sectors): cmd.arg correct. At exactly 1 TiB the arg still bit-matches (0x80000000). At 2 TiB the u64 block 0x100000000 arrives at the card as cmd.arg=0x00000000, and at 4 TiB+12345 sectors as 0x00003039 - the READ/WRITE/ERASE command is issued against the WRONG SECTOR (silent data corruption on >1TiB SDXC/eMMC media, or spurious EIO depending on card). mmcsd_delete shares the math (cited :534). Fix (64-bit locals + reject blocks beyond the 32-bit MMC argument with EIO) A/B: no MMC command is issued for the hostile offsets (opcode/arg unchanged, transfer skipped).
Evidence (decisive lines)
DF2480[0]: bio_offset=000000000007d000 -> u64 block=1000; cmd.arg=0x000003e8 (CORRECT) DF2480[2]: bio_offset=0000020000000000 (2048 GiB) -> u64 block=4294967296 (0x100000000); verbatim code sent cmd.arg=0x00000000 (TRUNCATED/WRONG) DF2480[3]: bio_offset=0000040000607200 -> u64 block=0x200003039; verbatim sent cmd.arg=0x00003039 (TRUNCATED/WRONG) DF2480[4]: erase path (mmcsd_delete) same math --- fixed source A/B --- DF2480[2]: cmd.opcode=0 (NO command issued - bio rejected with EIO)
Exploit chain / privesc assessment
no attacker needed - media-size correctness bug; corruption affects data at wrong sectors on large cards.
Fix + A/B
fix.diff switches to u64 block math and rejects beyond-32-bit blocks; A/B shows no wrong-sector command.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedConfirmed kernel references
Detail
Exploit chain
no attacker needed - media-size correctness bug; corruption affects data at wrong sectors on large cards.
Evidence (decisive lines)
run.log: 2TiB -> cmd.arg=0x00000000 (expected 0x100000000) TRUNCATED/WRONG; 4TiB -> 0x00003039; control 0x3e8 CORRECT.
PoC changes
MMCBUS_WAIT_FOR_REQUEST interposed to capture cmd.arg; device reports success.
Verified recommended fix
64-bit block math and reject blocks beyond the 32-bit MMC argument
Verdict
The verbatim mmcsd_rw/mmcsd_delete (extracted whole) assign bio->bio_offset/sz into daddr_t (= __int32_t, sys/types.h:80) block/end. Control (1000 sectors): cmd.arg correct. At exactly 1 TiB the arg still bit-matches (0x80000000). At 2 TiB the u64 block 0x100000000 arrives at the card as cmd.arg=0x00000000, and at 4 TiB+12345 sectors as 0x00003039 - the READ/WRITE/ERASE command is issued against the WRONG SECTOR (silent data corruption on >1TiB SDXC/eMMC media, or spurious EIO depending on card). mmcsd_delete shares the math (cited :534). Fix (64-bit locals + reject blocks beyond the 32-bit MMC argument with EIO) A/B: no MMC command is issued for the hostile offsets (opcode/arg unchanged, transfer skipped).
No comments yet.