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)
PoC verification
Evidence pack
findings/poc/DF-2904 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 1.8 KB | β raw | |
| verdict.json | file | 1.8 KB | view 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_testableConfirmed 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.
No comments yet.