DragonFlyBSD Kernel Audit
← triage · dashboard
DF-1398

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1398 · 8 files
FileTypeDescriptionSize
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
VERDICT.md verdict Full source-trace analysis
↓ download 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

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).