β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2744

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).

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2744 Β· 8 files
FileTypeDescriptionSize
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)

VERDICT.md
↓ download raw

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 the size that diskdump() compares ap->a_offset + ap->a_length - offset against (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_testable
per-fix-DF-2744

Confirmed 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.