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

ioctls that can throw without lock_config() panic the kernel: VINUM_MOVE onto a full/hot-spare drive is a one-ioctl panic

Summary

throw_rude_remark's bug-catcher panics unless VF_LOCKED is held, but VINUM_MOVE/ATTACH/DETACH/SETSTATE never call lock_config(); moveobject -> give_sd_to_drive's 'No space'/'hot spare' throws turn recoverable config errors into panic(). LIVE-REPRODUCED: two crafted drives, one VINUM_MOVE ioctl -> 'panic: throw_rude_remark: called without config lock' with full backtrace. Note: the 'hotspare' config keyword is dead - the string was never added to the keyword table. Wrap-with-lock_config fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4136 Β· 6 files
FileTypeDescriptionSize
vinum_movepanic.c β€” 2.5 KB view raw
panic.txt β€” 1016 B view raw
serial_full.txt β€” 3.7 KB view raw
README.md β€” 2.3 KB ↓ raw
verdict.json β€” 2.9 KB view raw
env.txt β€” 388 B view raw

DF-4136 β€” ioctls that can reach throw_rude_remark() without holding

the config lock panic the kernel (VINUM_MOVE demonstrated)

Root cause

throw_rude_remark() (vinumconfig.c:105-106) starts with a bug-catcher:

    if ((vinum_conf.flags & VF_LOCKED) == 0)        /* bug catcher */
    panic ("throw_rude_remark: called without config lock");

VF_LOCKED is set only by lock_config() (vinumlock.c:227-238), which is called by VINUM_CREATE (vinumioctl.c:103) β€” but not by VINUM_SETSTATE/VINUM_SETSTATE_FORCE, VINUM_ATTACH, VINUM_DETACH, VINUM_MOVE, or VINUM_PARITYOP, even though those code paths reach functions that throw:

  • attachobject() (sdβ†’plex) β†’ give_sd_to_plex() β†’ "Can't add %s to %s: plex full" (vinumconfig.c:250-254);
  • moveobject() β†’ give_sd_to_drive() β†’ "Can't place %s on hot spare drive %s" (vinumconfig.c:318-322) or "No space for %s on %s" (vinumconfig.c:324-328, 373-381).

Any of those turns a routine, recoverable configuration error into panic().

Reproduced trigger (VINUM_MOVE)

  1. two crafted vinum drives on vn devices: * x0 β€” normal drive with subdisk s0 * x1 β€” up drive with the hotspare flag
  2. VINUM_MOVE (_IOWR('F', 98, struct _ioctl_reply)) with {type = sd_object, index = driveno(x1), otherobject = sdno(s0)}.
  3. moveobject() β†’ give_sd_to_drive() β†’ hotspare rejection β†’ throw_rude_remark() β†’ panic "throw_rude_remark: called without config lock".

Expected panic text captured in panic.txt / serial log.

Impact

Immediate kernel panic (local DoS) by anyone with /dev/vinum/control access (root:wheel 0600) β€” a mis-typed vinum move/vinum attach on a full or hotspare drive takes the whole machine down instead of returning an error.

Fix

Either wrap the affected ioctls with lock_config()/unlock_config() (best: VINUM_MOVE, VINUM_ATTACH, VINUM_DETACH, VINUM_SETSTATE(_FORCE)), or downgrade the bug-catcher panic to a log(LOG_ERR,...) + error return. The lock wrapper matches the VINUM_CREATE pattern:

--- a/sys/dev/raid/vinum/vinumioctl.c
+++ b/sys/dev/raid/vinum/vinumioctl.c
@@ -316,7 +316,11 @@
    case VINUM_MOVE:
+       error = lock_config();
+       if (error)
+       break;
        moveobject((struct vinum_ioctl_msg *) data);    /* move an object */
+       unlock_config();
        break;

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

No fix built for this finding in this run (the validated fix kernel addressed DF-4132/4133); the panic was reproduced on that build. Proposed lock_config wrapper in README is untested.

panic.txt baseline.
per-fix-DF-4136

Confirmed kernel references

Detail

Exploit chain

craft two vinum drives (x0 with sd s0 1MB; x1 up with a 1500kB filler so free space < 1MB) -> VINUM_MOVE {_IOC style _IOWR('F',98,struct _ioctl_reply)} with {index=driveno(x1), otherobject=sdno(s0), type=sd_object} -> moveobject -> give_sd_to_drive -> freelist search fails -> throw_rude_remark(ENOSPC,...) -> panic('throw_rude_remark: called without config lock') at vinumconfig.c:105.

Evidence (decisive lines)

panic.txt (panic message + full backtrace), serial_full.txt, vinum_movepanic.c, README.md

PoC changes

Went through several trigger variants: the 'hotspare' config keyword is dead (kw_hotspare exists but the string was never added to the keyword table), so the 'No space' route was used; config text had to use the kernel's space-separated grammar (name=x forms are userland-only).

Verified recommended fix

Take lock_config()/unlock_config() around the ioctls that can throw (VINUM_MOVE/ATTACH/DETACH/SETSTATE), or make the bug-catcher non-fatal.

Verdict

VINUM_MOVE reaches give_sd_to_drive() without lock_config(); when the move target drive has insufficient space the 'No space' error path calls throw_rude_remark(), whose bug-catcher panics because VF_LOCKED is not held. One ioctl from root panics the machine with the exact predicted backtrace: throw_rude_remark <- give_sd_to_drive <- moveobject <- vinumioctl <- dev_dioctl. Verified on the kernel rebuilt with the DF-4132/DF-4133 fixes (independent defect). Any routine, recoverable config error on the MOVE/ATTACH/DETACH/SETSTATE ioctl paths is a kernel panic instead of an error return.