Unvalidated d_secsize in writedisklabel enables oversized I/O transfer
| Field | Value |
|---|---|
| ID | DF-0108 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:L/A:H |
| CWE | CWE-787 Out-of-bounds Write |
| File | sys/kern/subr_disklabel32.c |
| Lines | 336-341 |
| Area | kern |
| Confidence | medium |
| Discovered | 2026-06-30 |
| Reported | pending |
Summary
l32_writedisklabel uses lp->d_secsize directly as bp->b_bcount
(:340) and as a loop bound (:360), with only a KKASSERT
(no-op on production kernels) to verify it fits in the buffer. A user
who supplies a label with d_secsize exceeding the pbuf's
b_bufsize can trigger an oversized I/O transfer, potentially
corrupting kernel memory depending on the device strategy routine.
Root cause
l32_writedisklabel at :336-340:
KKASSERT((int)lp->d_secsize <= bp->b_bufsize);
bp->b_bio1.bio_offset = (off_t)LABELSECTOR32 * lp->d_secsize;
...
bp->b_bcount = lp->d_secsize;
KKASSERT is #ifdef INVARIANTS only (sys/sys/systm.h:94-118) โ
a no-op on production kernels. lp->d_secsize comes from the new label
supplied via DIOCWDINFO ioctl. If d_secsize exceeds bp->b_bufsize
(typically MAXPHYS = 128KB), the strategy routine receives
b_bcount > b_bufsize.
Additionally, the loop bound at :359-360:
((char *)bp->b_data + lp->d_secsize - sizeof(*dlp))
If d_secsize < sizeof(*dlp) (e.g. 0), the pointer arithmetic wraps,
making the loop condition enormous โ though the first iteration reads
bp->b_data which is valid, limiting immediate impact.
Threat model & preconditions
- Attacker position: Root or operator with write access to disk device node.
- Impact: Potential kernel memory corruption via oversized I/O.
Severity depends on the underlying device driver's handling of
b_bcount > b_bufsize. - Required config: Production kernel (no INVARIANTS).
- Reachability:
ioctl(fd, DIOCWDINFO32, &label)where label hasd_secsize > MAXPHYSandp_offset[RAW_PART] == 0.
Proof of concept
PoC source: findings/poc/DF-0108/ (scaffold)
Build & run
cc -o poc_secsize poc_secsize.c sudo ./poc_secsize /dev/da0s1
Expected output
Depends on driver behavior: kernel panic, memory corruption, or silent data transfer past buffer end.
Impact
Low. Requires root/operator privileges. Whether the oversized transfer
actually corrupts memory depends on the specific disk driver โ many
drivers clamp b_bcount to b_bufsize internally. This is primarily
a hardening gap.
Recommended fix
Replace the KKASSERT with a real validation:
--- a/sys/kern/subr_disklabel32.c
+++ b/sys/kern/subr_disklabel32.c
@@ -333,7 +333,10 @@
if (lp->d_partitions[RAW_PART].p_offset != 0)
return (EXDEV); /* not quite right */
- bp = getpbuf_mem(NULL);
+ bp = getpbuf_mem(NULL);
+ if (lp->d_secsize < DEV_BSIZE || lp->d_secsize > bp->b_bufsize)
+ return (EINVAL);
+
KKASSERT((int)lp->d_secsize <= bp->b_bufsize);
bp->b_bio1.bio_offset = (off_t)LABELSECTOR32 * lp->d_secsize;
Timeline
- 2026-06-30 Discovered during automated audit.