Off-by-one OOB array access in all four remove_*_entry functions (index==allocated passes > check)
Summary
remove_drive_entry :1728 driveno>drives_allocated should be >=. DRIVE[allocated] derefed before check. Same pattern in remove_sd_entry :1758, remove_plex_entry :1812, remove_volume_entry :1871. VINUM_REMOVE passes user msg->index unvalidated. Index==allocated -> OOB read/write of ~200-600 bytes adjacent heap. free_drive bzero of OOB region. Root/vinum-cap. Fix: change > to >= in all four.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1398 · 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Change `>` to `>=` in all four remove_*_entry functions' bounds checks. | 1.6 KB | view raw |
| VERDICT.md | verdict | Full source-trace analysis | 2.2 KB | ↓ raw |
| build.sh | build-script | Kernel build validation | 537 B | view raw |
| run.sh | run-script | PoC runner (not runnable on guest) | 574 B | view raw |
| fix_build.log | build-log | Full kernel build output (make nativekernel rc=0) | 5.6 MB | ↓ download |
| env.txt | environment | Guest environment | 277 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 |
DF-1398 — Verification Verdict
Verdict: CONFIRMED-BY-SOURCE-TRACE (root/vinum-only)
Status: inconclusive (HW-gated / not reachable as unprivileged maxx) Impact: none (cannot reproduce on QEMU guest — no GPU/HW, or root/operator-only) Confidence: certain (source-trace confirmed bug is real)
Mechanism
Four remove_*_entry functions use > instead of >= for bounds check: remove_drive_entry (:1731 driveno>drives_allocated), remove_sd_entry (:1758 sdno>subdisks_allocated), remove_plex_entry (:1812 plexno>plexes_allocated), remove_volume_entry (:1871 volno>volumes_allocated). When index==N_allocated, DRIVE[sdno]/SD[sdno]/PLEX[plexno]/VOL[volno] is dereferenced before check short-circuits (struct pointer taken at function entry). free_drive bzeros the OOB region. VINUM_REMOVE passes user msg->index unvalidated.
Source: sys/dev/raid/vinum/vinumconfig.c:1731, 1758, 1812, 1871
Why it cannot be reproduced on this guest
Root/vinum-capability only. vinum is a volume manager controlled via /dev/vinum (root-only ioctl). Not reachable as unprivileged maxx.
Phase 6: Escalation Assessment
This is a Root-only (vinum volume manager) finding. The primitive is not reachable from the unprivileged maxx user on this guest (no hardware / module not loaded / root-only device). No escalation chain is possible because the trigger path is not exercisable.
For GPU findings: the module (radeon.ko/amdgpu.ko/i915.ko) is a loadable module not present in the GENERIC kernel and requires actual GPU hardware absent from the QEMU guest. For root/operator findings: the device node is mode 0600 or 0640 root:operator, and maxx (uid 1001) has no operator group membership.
Fix
Change > to >= in all four remove_*_entry bounds checks.
Fix description: Change > to >= in all four remove_*_entry functions' bounds checks.
The full git-apply-able diff is in fix.diff. It applies cleanly to the audit source tree
and compiles as part of the kernel build (validated via make nativekernel rc=0).
Classification
- status: inconclusive
- reproduced: 0
- impact: none
- fix_status: not_testable (HW-gated: PoC cannot run on guest; diff applies + compiles verified)
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- v
- i
- n
- u
- m
- /
- v
- i
- n
- u
- m
- c
- o
- n
- f
- i
- g
- .
- c
- :
- 1
- 7
- 3
- 1
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- v
- i
- n
- u
- m
- /
- v
- i
- n
- u
- m
- c
- o
- n
- f
- i
- g
- .
- c
- :
- 1
- 7
- 5
- 8
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- v
- i
- n
- u
- m
- /
- v
- i
- n
- u
- m
- c
- o
- n
- f
- i
- g
- .
- c
- :
- 1
- 8
- 1
- 2
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- v
- i
- n
- u
- m
- /
- v
- i
- n
- u
- m
- c
- o
- n
- f
- i
- g
- .
- c
- :
- 1
- 8
- 7
- 1
Detail
Exploit chain
none (OOB heap read/write primitive exists — ~200-600 bytes adjacent heap, bzero in free_drive — but trigger is root/vinum-only via VINUM_REMOVE ioctl. No privilege boundary to cross: root→kernel is game-over by definition. Not reachable from unprivileged maxx.)
Evidence (decisive lines)
Source trace: vinumconfig.c:1731 'if ((driveno > vinum_conf.drives_allocated)' — allows driveno==drives_allocated. :1726 'struct drive *drive = &DRIVE[driveno]' — OOB address taken before check.
PoC changes
Authored fix.diff: change > to >= in all four remove_*_entry bounds checks.
Verified recommended fix
Change > to >= in all four remove_*_entry functions' bounds checks. matches finding proposal. Full diff in findings/poc/DF-1398/fix.diff.
Verdict
CONFIRMED BY SOURCE TRACE. Four remove_*_entry functions use > instead of >=: remove_drive_entry (:1731), remove_sd_entry (:1758), remove_plex_entry (:1812), remove_volume_entry (:1871). When index==N_allocated, the struct pointer (&DRIVE[index]) is taken at function entry and drive->state is dereferenced in the condition before short-circuit. VINUM_REMOVE passes user msg->index. Bug is real but root/vinum-only: vinum is controlled via /dev/vinum (root-only ioctl).
No comments yet.