# DF-4505 — VERDICT

**REPRODUCED — kernel panic (local DoS) from an operator-group user, and the
one-file fix is validated live on the same kernel.**

## Root cause

`sys/dev/misc/ipmi/ipmi.c`:

- `ipmi_ioctl()` IPMICTL_SEND_COMMAND (ipmi.c:401-416): every ioctl allocates
  `sizeof(struct ipmi_request) + req->msg.data_len + IPMI_MAX_RX` bytes
  (`ipmi_alloc_request`, ipmi.c:528-549; `msg.data_len` is `unsigned short`,
  max 65535 → ~66.6 KB per request) and enqueues it **asynchronously**.
- The only drain is a single kthread (`kcs_loop`, ipmi_kcs.c:449-469) that
  processes pending requests serially; each request is pushed over the KCS
  interface one byte per port-IO (ipmi_kcs.c:347-365), so service time per
  64 KB request is five orders of magnitude larger than ioctl enqueue time.
- `dev->ipmi_requests` is a plain counter — nothing in the driver bounds it.
- When the M_IPMI kmem accounting limit is crossed, DragonFly's `kmalloc`
  panics: `panic: ipmi: malloc limit exceeded`.

Attack class: CWE-400 (uncontrolled resource consumption). Access class:
`/dev/ipmi0` is created 0660 root:operator (ipmi.c:831-832) — any local user
in group `operator` (the designated semi-privileged class for this node; also
the class DF-1661 was rated against). No hardware fault, misconfig, or root
is needed: ~20,000 plain ioctls.

## How it was reproduced (3x baseline, from fresh boots)

1. Guest booted with a simulated BMC so the surface is real:
   `QEMU_EXTRA="-device ipmi-bmc-sim,id=bmc0 -device isa-ipmi-kcs,bmc=bmc0"`,
   then `kldload ipmi` → `ipmi0: KCS mode found at io 0xca2 on acpi`,
   `/dev/ipmi0` = `crw-rw---- root operator`.
2. `maxx` (uid 1001, groups: maxx + operator only) runs `./flood 60000 65535`.
3. Observed:
   - 5,000 sends → v_free_count 847,167 → 762,987 (329 MB pinned — exactly
     5000 × 66.6 KB of in-flight backlog; run1.partial.txt),
   - guest stops answering ssh; `vm.sh status` → down,
   - serial console: `panic: ipmi: malloc limit exceeded` with trace
     `_kmalloc ← ipmi_alloc_request ← ipmi_ioctl ← dev_dioctl ← devfs_fo_ioctl`
     (panic.txt, panic2.txt, panic.baseline-withsrc.txt — the third on the
     with-src snapshot kernel used for fix validation).

Honest scope: this is a **kernel panic / availability** bug, not a memory-
corruption primitive — `kmalloc` fails cleanly before any write occurs, so
there is no escalation path from this finding itself.

## Fix validation (mandatory-quality evidence)

`fix.diff` (single file, applies clean with `git apply`):

- adds `IPMI_MAX_REQUESTS 128` and, in SEND_COMMAND's final locked section,
  returns `EBUSY` when `dev->ipmi_requests >= IPMI_MAX_REQUESTS` (frees the
  just-allocated request);
- fixes the latent error path found during this audit: if
  `ipmi_enqueue_request()` ever fails (all three backends currently return 0
  unconditionally — ipmi_kcs.c:487, ipmi_smic.c:393, ipmi_ssif.c:371), the
  counter is decremented and the request freed, instead of leaking both and
  hanging `ipmi_dtor()`'s drain loop forever (ipmi.c:226-230).

Baseline (same with-src snapshot kernel): panic, guest dead
(panic.baseline-withsrc.txt).
Patched: `make nativekernel KERNCONF=X86_64_GENERIC && make installkernel`,
reboot, exact same PoC:

```
send 128 failed: Device busy
after flood (fd open)   v_free_count = 845510 pages (3302 MB)   # 128×66.6KB ≈ 8.1MB
after close+10s         v_free_count = 847545 pages (3310 MB)   # fully recovered
```

Guest stayed up; 5 additional sustained floods (5×20,000 sends) showed no
panic and no cumulative leak (run.log). Residual exposure: the cap is
per-open-handle (128 × 66.6 KB ≈ 8.5 MB per fd); a user may hold many fds.
A per-`ipmi_softc` or per-cred cap would be a stricter follow-up.

## Files (evidence)

- `flood.c`, `build.sh`, `run.sh` — exact PoC + commands
- `run.log` — patched-kernel decisive run, full output
- `run1.partial.txt` — first baseline run output (mid-flood 329 MB sample)
- `panic.txt`, `panic2.txt`, `panic.baseline-withsrc.txt` — three panic captures
- `fix.diff`, `fix_build.log`, `fix_run.log` — validated fix
- `env.txt` — guest environment

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
