vol->plex[] set to -1 by kw_prefer logic bug causing PLEX[-1] OOB read
Summary
config_volume kw_prefer at vinumconfig.c:1537-1549: myplexno=find_plex(name) (plex index), then myplexno=my_plex(volno,myplexno) (index in vol->plex[], or -1). if(myplexno>0) misses index 0. else stores -1 into vol->plex[]. :1608 vol->size loop PLEX[vol->plex[i]=-1] -> OOB heap read into reported volume size (info leak via VINUM_VOLCONFIG). Also routes I/O through bogus plex. Fix: capture real plexno before reassignment, use >=0.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1399 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Capture global plex index before my_plex() reassignment; use >=0 check; store correct index. | 1.4 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) | 528 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-1399 β 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
config_volume kw_prefer case (:1537-1549): myplexno=find_plex() returns global plex index, then myplexno=my_plex() overwrites it with local index-in-vol->plex[] (or -1). if(myplexno>0) misses index 0. The else branch stores myplexno (now -1 when plex not in vol) into vol->plex[]. At :1608, vol->size loop accesses PLEX[vol->plex[i]=-1] β OOB heap read into reported volume size (info leak via VINUM_VOLCONFIG). Also routes I/O through bogus plex.
Source: sys/dev/raid/vinum/vinumconfig.c:1537-1549, 1608
Why it cannot be reproduced on this guest
Root/vinum-capability only. vinum config is controlled via 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
Capture global_plexno before reassignment; use >= 0 check instead of > 0; store global_plexno (not myplexno) in vol->plex[].
Fix description: Capture global plex index before my_plex() reassignment; use >=0 check; store correct index.
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
- 5
- 3
- 7
- -
- 1
- 5
- 4
- 9
- 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
- 6
- 0
- 8
Detail
Exploit chain
none (OOB heap read / info leak primitive β PLEX[-1] reads adjacent heap into volume size. Trigger is root/vinum-only config ioctl. Not reachable from unprivileged maxx.)
Evidence (decisive lines)
Source trace: vinumconfig.c:1537 'myplexno = find_plex(...)' β :1539 'myplexno = my_plex(volno, myplexno)' (overwrites with -1 if not found) β :1540 'if (myplexno > 0)' misses 0 β :1546 'vol->plex[vol->plexes - 1] = myplexno' stores -1.
PoC changes
Authored fix.diff: capture global_plexno before my_plex() reassignment; use >=0 check instead of >0; store global_plexno (not myplexno) in vol->plex[].
Verified recommended fix
Capture global plex index before my_plex() reassignment; use >=0 check; store correct global index. supersedes finding proposal. Full diff in findings/poc/DF-1399/fix.diff.
Verdict
CONFIRMED BY SOURCE TRACE. config_volume kw_prefer (:1537-1549): myplexno=find_plex() (global index), then myplexno=my_plex() overwrites with local index-or-(-1). if(myplexno>0) misses index 0. else stores myplexno (now -1 when plex not found) into vol->plex[]. At :1608, vol->size loop accesses PLEX[vol->plex[i]=-1] β OOB heap read into reported volume size. Bug is real but root/vinum-only.
No comments yet.