disk_dumpcheck(): media_blocks β reserved_blocks unsigned underflow defeats the crash-dump bounds check; DEV_BSIZE unit mismatch for non-512B media
| Field | Value |
|---|---|
| ID | DF-2744 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:N |
| CWE | CWE-191 Integer Underflow |
| File | sys/kern/subr_disk.c |
| Lines | 909, 935-936 (bounds consumer :1288-1296) |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-08-30 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
disk_dumpcheck() computes *size = pinfo.media_blocks -
pinfo.reserved_blocks (u64) where reserved_blocks derives from an
on-disk disklabel64 d_bbase that l64_readdisklabel() never
bounds-checks (magic/CRC only). A crafted label makes reserved β«
media_blocks and the subtraction wraps to ~1.8e19 β reproduced live on
a vn-backed slice: media_blocks=1024, reserved_blocks=67,108,864 β
wrapped size 18,446,744,073,642,443,776, exactly the value
diskdump()'s bounds check compares against, so a crash dump configured
on such a slice writes past the end of the dump partition (raw-device
OOB writes at panic time). Secondary: di.mediaoffset/mediasize are
scaled by DEV_BSIZE although blkno/size are in media blocks β wrong
dump geometry on non-512B-sector media. Root-gated end to end (write
label + configure dumpdev).
Recommended fix
if (pinfo.reserved_blocks >= pinfo.media_blocks) return (EINVAL);
before the subtraction; scale mediaoffset/mediasize by secsize instead
of DEV_BSIZE; optionally validate d_bbase against the slice size in
l64_readdisklabel.
Timeline
- 2026-08-30 Discovered during pass-2 audit of subr_disk.c (GLM 5.3); wrapped operand demonstrated live on stock same run.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2744 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| poc2744.c | β | 1.7 KB | view raw | |
| mkimages.py | β | 4.5 KB | view raw | |
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 1.7 KB | β raw | |
| build.sh | β | 37 B | view raw | |
| run.sh | β | 82 B | view raw | |
| run.log | β | 270 B | view raw | |
| env.txt | β | 392 B | view raw |
DF-2744 β disk_dumpcheck() unsigned underflow (reserved > media_blocks)
Impact
sys/kern/subr_disk.c:909 computes *size = pinfo.media_blocks -
pinfo.reserved_blocks (u64 - u64). reserved_blocks derives from
sp->ds_reserved, set for disklabel64 slices from the on-disk
d_bbase/secsize (sys/kern/subr_disklabel64.c:521-527) with d_bbase
validated only for magic/CRC in l64_readdisklabel (subr_disklabel64.c:174-196)
β a crafted label yields reserved_blocks >> media_blocks. The subtraction
wraps to ~2^64, so diskdump()'s bounds check
(subr_disk.c:1291-1292, ap->a_offset + ap->a_length - offset > size)
can never trigger: a crash dump configured on such a slice writes past the
end of the dump partition (raw-device OOB writes at panic time).
Secondary units bug in the same function: di.mediaoffset/mediasize are
scaled by DEV_BSIZE (512) although blkno/size are in media blocks
(subr_disk.c:935-936) β wrong dump geometry on non-512B-sector media.
Trigger requires root (write the crafted label; configure dumpdev) β Low.
Reproduce (guest, root)
python3 mkimages.py . # host: makes lbl64.img vnconfig -c /dev/vn1 /root/lbl64.img # MBR + DFLYBSD slice w/ label64 cc -O -o poc2744 poc2744.c && ./poc2744 /dev/vn1s1
Observed
media_blocks = 1024 reserved_blocks = 67108864 UNDERFLOW TRIGGER: *size = 1024 - 67108864 = 18446744073642443776 (the exact value disk_dumpcheck() returns and diskdump() compares against)
DF-2744 VERDICT β disk_dumpcheck() unsigned underflow
Status: reproduced (the underflow precondition and the exact wrapped value that feeds the dump bounds check). Impact: dump-time OOB writes on the dump device; requires root to configure the dump device and a crafted label64 on it β Low. Confidence: certain.
- Live (run.log): crafted label64 with d_bbase = 32 GiB inside a 1024-sector
slice; DIOCGPART on /dev/vn1s1 returned media_blocks=1024,
reserved_blocks=67,108,864 β the two operands
disk_dumpcheck()(sys/kern/subr_disk.c:909) subtracts. 1024 β 67108864 wraps to 18,446,744,073,642,443,776, which is thesizethatdiskdump()comparesap->a_offset + ap->a_length - offsetagainst (subr_disk.c:1288-1296) β the check can never reject. A crash dump configured on such a slice writes past the partition end. - The label path is unvalidated on read:
l64_readdisklabel()checks only magic/npartitions/CRC (sys/kern/subr_disklabel64.c:174-196);ds_reserved = d_bbase / secsize(subr_disklabel64.c:521-527). - Not executed to an actual OOB dump write (that would require crashing the guest with this slice configured as dumpdev); the arithmetic and its inputs are demonstrated exactly.
- Secondary (same function): di.mediaoffset/mediasize are scaled by DEV_BSIZE although blkno/size are in media blocks (subr_disk.c:935-936) β wrong dump geometry for non-512B sector media.
Suggested fix
In disk_dumpcheck(): reject reserved >= blocks
(if (pinfo.reserved_blocks >= pinfo.media_blocks) return (EINVAL);)
and compute offsets/sizes in secsize units; optionally validate d_bbase in
l64_readdisklabel against the slice size.
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
['run.log: DIOCGPART shows media_blocks=1024, reserved_blocks=67108864, wrapped *size = 18446744073642443776']
PoC changes
written from scratch; mkimages.py crafts MBR + label64 with d_bbase=32GiB
Verified recommended fix
In disk_dumpcheck(): if (pinfo.reserved_blocks >= pinfo.media_blocks) return (EINVAL); and scale mediaoffset/mediasize by secsize, not DEV_BSIZE.
Verdict
disk_dumpcheck() computes *size = pinfo.media_blocks - pinfo.reserved_blocks (u64, subr_disk.c:909); reserved_blocks comes from an on-disk disklabel64 d_bbase that l64_readdisklabel() never bounds-checks (magic/CRC only, subr_disklabel64.c:174-196) -> ds_reserved = d_bbase/secsize (subr_disklabel64.c:521-527). Reproduced live: a crafted label64 yields media_blocks=1024, reserved_blocks=67,108,864, so the subtraction returns 18,446,744,073,642,443,776 -- the exact size diskdump() compares against (subr_disk.c:1288-1296), meaning a crash dump configured on such a slice can write past the end of the dump partition. Full dump-time OOB write not executed (would require crashing the guest with this slice as dumpdev); inputs and wrapped result demonstrated exactly. Root-gated end to end -> Low. Secondary: DEV_BSIZE scaling of media-block quantities (subr_disk.c:935-936) mis-geometries dumps on non-512B media.
No comments yet.