# DF-4580 VERDICT

**Status: not_reproduced (full chain) — sink mechanism reproduced on guest
(panic).** Impact claimed: local kernel DoS (panic or unkillable hang).

## Why the full chain did not run here
The finding's entry point is `radeon_cs_ioctl`
(`sys/dev/drm/radeon/radeon_cs.c:655`), gated by `rdev->accel_working`
(`radeon_cs.c:662`) — i.e. a bound, initialized radeon GPU. The audit guest
(QEMU/KVM, `DragonFly 6.5-DEVELOPMENT #0 X86_64_GENERIC`) has **no AMD/ATI
PCI device and no /dev/dri** (checked via `pciconfig -l`, `ls /dev/dri`);
every radeon ioctl returns before the parser runs. That is a hard
environment blocker for the ioctl itself, not a refutation: the code path
is fully traceable and its allocation sink was exercised directly.

## Code trace of the trigger (radeon hardware present)
1. Unprivileged-but-authenticated client (ioctl is `DRM_AUTH|DRM_RENDER_ALLOW`,
   `sys/dev/drm/radeon/radeon_kms.c:912`) issues `DRM_IOCTL_RADEON_CS` with
   `num_chunks = N` (u32, uncapped) and `chunks` pointing at N·8 bytes of
   user memory.
2. `radeon_cs_parser_init` `radeon_cs.c:287`:
   `kcalloc(N, 8, GFP_KERNEL)` → DF shim `sys/dev/drm/include/linux/slab.h:44`
   `kzalloc(N*8)` → `kmalloc(N*8, M_DRM, M_WAITOK|M_CACHEALIGN|M_ZERO)`.
3. With N ≈ 40–50 M on a 4 GB box (M_DRM limit = physmem/10 ≈ 389–405 MB):
   the 320–400 MB chunk-pointer array is allocated (wired, M_ZERO — feasible
   with >1 GB free RAM), pushing `M_DRM.ks_loosememuse` over the type limit;
   `radeon_cs.c:298`'s `kcalloc(N, 24, GFP_KERNEL)` then enters `_kmalloc`
   and hits `sys/kern/kern_slaballoc.c:877` →
   **`panic("M_DRM: malloc limit exceeded")`** — whole-system crash.
4. Alternative, N = `0xFFFFFFFF`: first kcalloc requests 32 GB
   (64-bit multiply, no overflow in the DF shim); `_kmalloc` oversized path
   (`kern_slaballoc.c:944-950`) → `kmem_slab_alloc(32 GB, M_WAITOK)` → VA
   fits (kernel_map ≈ 8 TB) → page loop `kern_slaballoc.c:1780-1806`
   `vm_wait(0)` = 1 s bounded sleep, retried forever since 32 GB > physmem;
   `vm_wait` sleeps with flags 0 (no PCATCH) → **unkillable thread**;
   repeatable per ioctl.
5. Latent cleanup crash: had the `radeon_cs.c:298` kcalloc returned NULL
   (it does on upstream Linux where `KMALLOC_MAX_SIZE` forces a clean NULL
   for N > ~175 K), `radeon_cs_parser_fini` `radeon_cs.c:447-448` derefs
   `parser->chunks[i]` with `parser->chunks == NULL`, `nchunks > 0` →
   NULL-page read panic. On DF-as-compiled this is masked by the allocation
   panic itself (GFP_KERNEL cannot yield NULL); it becomes live the moment
   the port adds `M_NULLOK` semantics — fix both together.

## Mechanism verification actually performed (this guest)
* Built kld `df4580.ko` (`build.log`) replicating `GFP_KERNEL` allocation
  semantics with its own malloc type (same `physmem/10` limit rule).
* `kldload` → allocations #0..#13 (32 MB each, 416 MB cumulative ≥ 389 MB
  limit) → **`panic: df4580: malloc limit exceeded`**, backtrace
  `_kmalloc() → df4580_modevent()` (`run.log`, `panic.txt`).
* This proves both (a) the sink behavior `radeon_cs.c:287/298/346` rides,
  and (b) that the Linux-shaped NULL return (and hence the parser_fini
  NULL-deref) is unreachable on DF until allocator flags change.
* Guest reset to `with-src` snapshot afterwards; module gone.

## Exploit chain
DoS only: one ioctl → panic (or one unkillable thread per ioctl; threads
are not bounded). No memory-corruption primitive identified in this path.

## Fix
Cap `num_chunks` and per-chunk `length_dw` at sane maxima before allocating
in `radeon_cs_parser_init`, and make `radeon_cs_parser_fini` NULL-safe (see
`fix.diff`). Suggested caps mirror upstream practice (chunks are at most a
handful; `64` is generous, `length_dw` ≤ 1 M dwords).

## Artifacts
* `df4580_limitpanic.c` / `Makefile` — sink-mechanism kld (source of record)
* `build.log` — untrimmed guest build output
* `run.log`, `panic.txt` — serial console capture of the run + panic backtrace
* `env.txt` — guest uname/physmem/compiler
* `fix.diff` — proposed fix (authored post-verification; never applied to sys/)

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