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

mbr_extended()'s signed int slice counter can wrap negative after 2^31 accepted EBR entries, defeating the `slice >= MAX_SLICES` guard β†’ OOB write of attacker-controlled ds_offset/ds_size before dss_slices[]

Field Value
ID DF-2904
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:H
CWE CWE-190 / CWE-787
File sys/kern/subr_diskmbr.c
Lines 423, 465, 490-499
Area kern/disk
Confidence speculative
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket memcorrupt
Reported pending
Known CVE none
CVE match novel

Summary

mbr_extended() keeps the running slice index in a signed int slice (seeded from dss_nslices at :465, incremented at :492/:498); the array bound is guarded only by if (slice >= MAX_SLICES) (:490). After 2^31 accepted non-extended EBR entries the int wraps negative, the guard is false, and sp = &ssp->dss_slices[slice] (:495) with a negative index lets mbr_setslice write attacker-chosen 64-bit ds_offset/ds_size at &dss_slices[negative] β€” an OOB kernel write with controlled values at a coarsely controlled offset before the kmalloc'd diskslices. The depth cap (level >= 16) bounds the tree at Ξ£4^i β‰ˆ 5.7G entries > 2^31, so the count is reachable in principle with a device that answers instantly, but requires β‰₯2.1 billion successful synchronous EBR reads inside one probe β€” days of the already-known DF-0133 exponential wedge must first be sustained. Latent type-broken guard layered on the known recursion; filed speculative, not PoC'd (multi-day precondition). Arithmetic documented in findings/poc/DF-2904/README.md. Fix: make the running count unsigned (or clamp dss_nslices at MAX_SLICES right after ssp->dss_nslices = slice; at :501).

Timeline

  • 2026-09-02 Discovered during pass-2 audit of subr_diskmbr.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2904 Β· 2 files
FileTypeDescriptionSize
README.md file 1.8 KB ↓ raw
verdict.json file 1.8 KB view raw
README.md file
↓ download raw

DF-2904 (speculative) β€” int slice counter overflow in mbr_extended β†’ OOB slice write

What

mbr_extended() (sys/kern/subr_diskmbr.c:423-501) counts accepted logical slices in a signed int slice seeded from ssp->dss_nslices (u_int) and incremented once per accepted non-extended EBR entry, with the array bound guarded only by if (slice >= MAX_SLICES) (subr_diskmbr.c:490). If the counter ever wraps past INT_MAX it becomes negative, slice >= MAX_SLICES is then FALSE, and sp = &ssp->dss_slices[slice] (subr_diskmbr.c:495) indexes far before the allocation β€” mbr_setslice() then writes attacker-controlled ds_offset/ds_size (from dp_start/dp_size) at &ssp->dss_slices[negative], an out-of-bounds kernel write with attacker-chosen 64-bit values at a (coarsely) attacker-chosen negative offset.

Why speculative / not PoC'd

Reaching 2^31 accepted entries requires β‰₯ 2.1 billion successful synchronous EBR reads inside ONE probe run. The EBR recursion depth cap (level >= 16, subr_diskmbr.c:427) bounds the tree at Ξ£ 4^i (i=1..16) β‰ˆ 5.7G potential entries, so the count is reachable in principle (a malicious device answers instantly), but at realistic DragonFly sync-I/O round-trip cost the probe must first sustain a multi-day wedge β€” during which the system is already effectively dead from the DF-0133 exponential-read blowup (this is the same recursion). The OOB write is therefore a latent memory-corruption consequence layered on the known algorithmic DoS, not an independently practical exploit; it is recorded because the guard is type-broken and the fix is one line.

Fix

Use an unsigned/64-bit counter or clamp the running count (e.g. track slice as u_int and additionally test slice >= MAX_SLICES after increment, or bound ssp->dss_nslices at MAX_SLICES inside the loop).

Fix verification

not_testable
per-fix-DF-2904

Confirmed kernel references

Detail

Evidence (decisive lines)

['README.md: full arithmetic (Ξ£4^i for level<=16 β‰ˆ 5.7G > 2^31) and the wrap path']

PoC changes

none β€” analysis-only finding

Verified recommended fix

Make the slice counter unsigned (or clamp dss_nslices at MAX_SLICES inside the loop) so the >= MAX_SLICES guard cannot be defeated by int wrap

Verdict

Latent OOB write, not practically reachable: mbr_extended()'s signed int slice counter (subr_diskmbr.c:423, incremented at :492/:498, bound-checked only by 'slice >= MAX_SLICES' at :490) would index dss_slices[] with a negative value after 2^31 accepted EBR entries, letting mbr_setslice write attacker-controlled ds_offset/ds_size before the array. Reaching it requires billions of successful synchronous EBR reads within one probe (depth-capped 4-ary tree, Ξ£4^i β‰ˆ 5.7G max), i.e. days of the DF-0133 wedge must first be sustained β€” the system is already catatonic long before the wrap. Not executed on the guest (precondition is a multi-day hammer); documented as speculative with a one-line fix.