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

Missing structural validation in l64_readdisklabel: crafted partition fields accepted without bounds checks

Summary

l64_readdisklabel only validates magic,npartitions<=16,CRC(:183-187). Does NOT validate partition p_boffset/p_bsize or d_bbase/d_pbase/d_pstop against slice boundaries. Entire label blindly copied to in-core(:193). Contrast l32_readdisklabel calls l32_fixlabel which validates each partition against slice. Downstream dscheck only bounds within-partition not within-slice. Crafted disk: cross-partition I/O, DoS via oversized reserved area(EROFS), potential offset overflow panic.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0134 Β· 15 files
FileTypeDescriptionSize
craft_label.c trigger-source write crafted disklabel64 with out-of-slice partition (CRC-correct) 7.1 KB view raw
readback.c trigger-source DIOCGDINFO64 readback of in-core label 1.7 KB view raw
setlabel.c trigger-source contrast: DIOCSDINFO64 set path rejects the same label 1.8 KB view raw
mkvalid.c trigger-source control: write a fully-valid (in-slice) label 4.4 KB view raw
build.sh build-script cc craft_label/readback/setlabel 185 B view raw
run.sh run-script dd image, craft label, vnconfig, readback 737 B view raw
run.log run-log decisive: in-core label has part[1] end=100728832 > total; /dev/vn0s0b readable 721 B view raw
env.txt environment uname 150 B view raw
VERDICT.md verdict full analysis: read path lacks set path's validation 4.2 KB ↓ raw
README.md readme reproduce instructions 1.3 KB ↓ raw
fix.diff suggested-fix bound partitions vs slice in l64_readdisklabel (reject OOB; allow RAW/virgin p_boffset=0) 1.6 KB view raw
fix_build.log build-log single-fix kernel build (subr_disklabel64.c) rc=0 5.6 MB ↓ download
fix_run.log run-log patched: OOB label rejected (EINVAL) + valid label still loads (control OK) 599 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme reproduce instructions
↓ download raw

DF-0134 β€” l64_readdisklabel lacks structural validation

Verdict REPRODUCED
Impact structural-validation gap; out-of-slice label accepted (cross-boundary device access)
File sys/kern/subr_disklabel64.c:189-195 (read path); contrast set path :278-304

Build

cc -o craft_label craft_label.c
cc -o readback readback.c
cc -o setlabel setlabel.c

Run (as root β€” "crafted disk" precondition)

./run.sh

Expected (bug present)

The read path (DIOCGDINFO64) returns the out-of-slice partition unchanged:

part[1]: p_boffset=65536  p_bsize=100663296  end=100728832  *** OUT-OF-SLICE (readdisklabel accepted it!) ***
RESULT: LEAK_CONFIRMED

and /dev/vn0s0b is created and readable. The set path (DIOCSDINFO64) rejects the same label.

Expected (fixed)

readdisklabel rejects the crafted label; readback reports "no disk label" / the device gets no OOB partitions; RESULT: NO_LEAK.

Mechanism (short)

l64_readdisklabel checks only magic/npartitions/CRC and copies the label in-core with no bounds check on partitions, unlike l64_setdisklabel which validates p_boffset+p_bsize <= d_total_size etc. A CRC-correct crafted label with out-of-slice partitions is therefore accepted and the OOB partitions get real device nodes.

See VERDICT.md for the full analysis and fix.diff for the patch.

VERDICT.md verdict full analysis: read path lacks set path's validation
↓ download raw

DF-0134 β€” l64_readdisklabel lacks structural validation

Verdict: REPRODUCED (missing structural validation; out-of-slice label accepted)

Mechanism

l64_readdisklabel() (sys/kern/subr_disklabel64.c:146-201) validates only: d_magic == DISKMAGIC64 (:183), d_npartitions <= MAXPARTITIONS64 (:185), and the CRC (:187). On success it blindly copies the whole on-disk label into the in-core label (:191-193):

} else {
    dlp->d_crc = savecrc;
    (*lpp).lab64 = kmalloc(sizeof(*dlp), M_DEVBUF, M_WAITOK|M_ZERO);
    *(*lpp).lab64 = *dlp;          /* no bounds check on any partition */
    msg = NULL;
}

It performs none of the structural validation that the write path l64_setdisklabel() (:278-304) performs: d_total_size <= slicebsize, per-partition p_boffset + p_bsize <= d_total_size, p_boffset >= d_pbase, alignment, and the p_bsize==0 β‡’ p_boffset==0 rule. l32_readdisklabel calls l32_fixlabel which validates partitions against the slice; l64 has no equivalent on the read path.

disk_probe_slice() (sys/kern/subr_disk.c:205-210) tries disklabel32 first and, on "no disk label", falls back to disklabel64. So a crafted image with no disklabel32 magic + a CRC-correct disklabel64 with out-of-slice partitions is loaded verbatim, and the OOB partitions get real device nodes (when p_fstype is set) via disk_probe_slice :228-229.

Proof (decisive)

Crafted 32 MiB vnode disk (/dev/vn0) carrying a disklabel64 whose partition 'b' has p_boffset+p_bsize = 100728832 bytes against d_total_size = 33554432. After vnconfig -c, the in-core label read back via DIOCGDINFO64 shows the OOB partition unchanged:

[*] in-core disklabel64: magic=0xc4464c59 npart=3 d_total_size=33554432 ...
    part[1]: p_boffset=65536  p_bsize=100663296  end=100728832  *** OUT-OF-SLICE (readdisklabel accepted it!) ***
RESULT: LEAK_CONFIRMED β€” in-core label contains a partition extending beyond d_total_size

The OOB partition device is created and accessible β€” a read of /dev/vn0s0b succeeds (dd 4 blocks => 2048 bytes, rc=0). The vn backend clamps reads beyond the backing media (short read / 0 bytes, no panic); a real block driver would translate the OOB geometry into out-of-slice / cross- partition I/O.

Contrast: the same out-of-slice label submitted via DIOCSDINFO64 (the set path) is rejected (rc=-1) β€” confirming the read/set validation asymmetry.

Impact / realism

  • Realistic precondition: a crafted disk image made attachable/mountable (admin attaches it; or vfs.usermount=1 + a root-created image owned by the attacker β€” the accepted audit threat model for FS-image findings). On this guest vfs.usermount=0, so the demonstration is run as root (vnconfig), which is the standard "crafted disk" setup.
  • Class: structural-validation / robustness gap. The kernel loads a structurally-invalid disklabel64 (partitions beyond the slice) and exposes the OOB partitions as accessible devices. On the vn backend this does not crash (reads clamp); on real media the effect is out-of-slice / cross- partition I/O (info exposure / geometry confusion / reserved-area EROFS).
  • No memory-corruption primitive is triggered on the default kernel here (the vn backend bounds-checks against its own size). The realistic default-kernel ceiling is the invalid-label acceptance enabling cross-boundary device access; "potential offset overflow panic" is config/driver-dependent and was not reproduced on vn.

Fix

fix.diff: in l64_readdisklabel, after the CRC check, bound partitions against the slice β€” reject labels whose d_total_size > slice or any non-empty partition with p_boffset + p_bsize > d_total_size (and p_bsize==0 β‡’ p_boffset==0). This catches the out-of-slice case while NOT over-rejecting the legitimate RAW/whole-disk partition or the kernel's own virgin label, both of which use p_boffset = 0 (so we deliberately do not check p_boffset >= d_pbase, which would wrongly reject them). Supersedes (specifies) the finding's proposal.

Reproduce

cc -o craft_label craft_label.c     # build.sh (also readback.c)
cc -o readback readback.c
# run.sh (as root): dd image, craft label, vnconfig, readback

Fix verification

fixed

validated

see evidence pack
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Fri Jul 17 17:35:56 UTC 2026

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

REPRODUCED. l64_readdisklabel no structural validation -> OOB partition accepted. Read/set asymmetry.