# DF-4580 — radeon_cs: uncapped `num_chunks` wired allocations; DF GFP_KERNEL failure = panic / eternal hang (local DoS)

## What the finding is
`radeon_cs_ioctl` → `radeon_cs_parser_init` (`sys/dev/drm/radeon/radeon_cs.c`):
two kernel allocations sized directly from the fully attacker-controlled
`__u32 drm_radeon_cs.num_chunks`, with **no upper bound**:

* `radeon_cs.c:287` — `kcalloc(cs->num_chunks, sizeof(uint64_t), GFP_KERNEL)` (chunk pointer array, 8·n bytes)
* `radeon_cs.c:298` — `kcalloc(p->nchunks, sizeof(struct radeon_cs_chunk), GFP_KERNEL)` (24·n bytes)
* `radeon_cs.c:346` — per-chunk `kvmalloc_array(length_dw, 4, GFP_KERNEL)` (4·length_dw bytes, length_dw also an uncapped u32)

On Linux, failure of these is a clean `-ENOMEM`. On **DragonFly**, the DRM
port maps `GFP_KERNEL` to `M_WAITOK | M_CACHEALIGN` **without `M_NULLOK`**
(`sys/dev/drm/include/linux/gfp.h:40`), and DF `_kmalloc()` therefore can
never return NULL:

* type-limit exceeded → `panic("%s: malloc limit exceeded")` (`sys/kern/kern_slaballoc.c:877`; every malloc type's limit is `physmem/10`, `kern_slaballoc.c:372`)
* kernel_map full → `panic("kmem_slab_alloc(): kernel_map ran out of space!")` (`kern_slaballoc.c:1724`)
* pages unavailable → `vm_wait()` retry loop forever, unkillable thread (`kern_slaballoc.c:1780-1806`, `sys/vm/vm_page.c:2927`)

So an authenticated local DRM client on radeon hardware can, with a single
CS ioctl:

* `num_chunks` ≈ 40–50 M → chunk-pointer array (≈330–400 MB) succeeds, next
  `kcalloc` trips the `M_DRM` type limit → **deterministic kernel panic**;
* `num_chunks` = `0xFFFFFFFF` → first allocation asks for 32 GB → **permanent
  uninterruptible hang** of the calling thread (1 s `vm_wait` poll loop).

Latent aggravator (upstream-visible even where kcalloc *can* return NULL):
`radeon_cs_parser_fini` then does `for (i = 0; i < parser->nchunks; i++)
kvfree(parser->chunks[i].kdata);` (`radeon_cs.c:447-448`) — a NULL
`parser->chunks` (after the `radeon_cs.c:298-300` failure return) is
dereferenced → second, independent crash path.

## What this PoC does / does not do
The audit QEMU guest has **no AMD/ATI GPU** (`pciconfig -l` shows no
1002:xxxx device, `/dev/dri` does not exist), so `radeon_cs_ioctl` cannot
be entered on it. This pack therefore verifies the **sink mechanism** the
ioctl rides: `df4580_limitpanic.c` is a kld that performs the identical
class of allocation (`kmalloc(..., M_WAITOK|M_ZERO)` — no `M_NULLOK`, own
malloc type with the standard `physmem/10` limit, exactly like `M_DRM`)
until the limit trips.

## Build / run (inside the guest as root)
```
cd /root/df4580 && make
kldload ./df4580.ko
```

## Expected result
Serial console (see `run.log`, `panic.txt`):
```
df4580: malloc-type limit ~389 MB; looping kmalloc(32 MB, M_WAITOK|M_ZERO) [no M_NULLOK], GFP_KERNEL-equivalent
df4580: alloc #0 ... alloc #13 (type usage ~416 MB)
panic: df4580: malloc limit exceeded
_kmalloc() at _kmalloc+0xb09
df4580_modevent() at df4580_modevent+0xd8
```

The radeon trigger itself (single CS ioctl with crafted `num_chunks`) is
code-traced in `VERDICT.md`; it needs radeon hardware.
