# DF-4279 VERDICT

**Status: REPRODUCED (panic). Fixed by fix.diff hunk 1 and re-verified.**

## Environment
DragonFly 6.5-DEVELOPMENT #0 Thu Jul 2 06:02:54 UTC 2026 (X86_64_GENERIC),
QEMU/KVM guest, ccd.ko module, vn(4) components labeled fstype=ccd (19).
Trigger executed as root (also reachable by the operator group: /dev/ccdN
is 0640 root:operator via make_only_dev defaults, subr_disk.c:699-701).

## How it reproduces
1. `kldload ccd`; create two 8MB vn disks; disklabel32 each with
   `d: 16368 16 ccd` (fstype 19 = FS_CCD — required by ccd.c:458).
2. Positive control: raw `ioctl(CCDIOCSET)` ileave=32 → config ok
   ("total blocks 16352" = 16368-16 skip), 512-byte pread returns.
3. Trigger: same ioctl with `ccio_ileave = -1` → **accepted**; the
   first pread never returns and the kernel panics within seconds.

## Observed (two runs, identical signature — panic.txt / run.2.log)
```
ccd0-3: Concatenated disk drivers
ccd0: max component iosize is 65536 total blocks 16352   <- control ok
ccd0: max component iosize is 65536 total blocks 16352   <- ileave=-1 ACCEPTED
panic: assertion "obj != NULL" failed in vm_object_hold_shared vm_object.c:330
 vm_object_hold_shared → vm_fault → trap_pfault → trap → calltrap
 --- trap 0xc, rip = memmove+0x10a ---
```
(Note: every config line says "ccd0" because ccd.c:507 prints cs->sc_unit
before ccdinit sets it at ccd.c:593 — cosmetic, noted in the audit.)

## Root cause chain (path:line)
- sys/dev/disk/ccd/ccd.c:1326 — `ccd.ccd_interleave = ccio->ccio_ileave`
  with no `<= 0` validation in the kernel (userland ccdconfig.c:254
  rejects it, the ioctl does not).
- sys/dev/disk/ccd/ccd.c:484,514 — only `> 1` / `> 0` guards; negative
  values reach ccdinterleave/ccdbuffer.
- sys/dev/disk/ccd/ccd.c:1068-1071 — `cbc = dbtob(ileave - cboff)` is
  negative for ileave<0 ⇒ `cb_buf.b_bcount` negative.
- sys/dev/disk/ccd/ccd.c:886-888 — `rcount` negative ⇒ `bcount` grows ⇒
  unbounded dispatch loop; `addr += rcount` walks b_data backwards
  (OOB relative to the user buffer) ⇒ OOB transfer → fault in memmove
  → vm_fault on a destroyed/absent object → panic.

Interleave-table arithmetic itself stays in bounds for negative ileave
(the u64 two's-complement walk in ccd.c:981-985 is self-consistent); the
kill shot is the negative transfer size, not the table walk.

## Exploit chain
None to uid0. Primitive: privileged local user (root/operator) gets an
un-killable kernel thread issuing unbounded negative-length device
transfers whose data pointer walks backwards through user VA space,
terminating in a guaranteed kernel panic. Class = local DoS/panic.
The write variant sprays component-disk content at decreasing user
addresses of the caller's own process before the panic.

## Fix validation (same run, same guest)
- fix.diff hunk 1 rejects `ccio_ileave < 0` with EINVAL.
- Patched ccd.ko rebuilt from guest /usr/src (build.log), installed:
  - control ileave=32: config + read OK (no regression);
  - trigger ileave=-1: `CCDIOCSET: Invalid argument`, **no panic**,
    guest stays up (fix_patched.log).
- Baseline (unpatched): panic ×2. Patched: 0/2.

fix.diff hunk 2 (DF-4280) was validated in the same build.

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