# DF-4262 — amdgpu CS ioctl with `num_chunks == 0` → NULL-deref kernel panic

**File:** `sys/dev/drm/amd/amdgpu/amdgpu_cs.c` (pass 2)
**Severity:** Medium (unprivileged local DoS/panic) — CWE-476
**Confidence:** certain (static proof, line-cited)

## Build

```
cc -O2 -Wall -o cs_zero_chunks cs_zero_chunks.c
```
(self-contained: no drm headers needed — guest `/usr/include` has none)

## Run (as any user, on a system with amdgpu hardware)

```
./cs_zero_chunks          # opens /dev/dri/renderD128 (or card0), issues
                          # DRM_IOCTL_AMDGPU_CS with in.num_chunks = 0
```

## Expected result on amdgpu hardware

Immediate kernel panic (fatal trap 12, page fault in kernel mode, read of
`parser->entity->rq` ≈ address 0x10) inside the ioctl — the machine goes
down before the syscall returns.

## What was verified on THIS audit's QEMU guest

The guest has no AMD GPU (`vgapci0 ... chip=0x11111234` = QEMU VGA;
`amdgpu.ko` loads but binds nothing, `/dev/dri` does not exist), so the
ioctl path cannot be entered at runtime:

- trigger builds on the guest: `build.log` (cc 8.3, all four DF-4262..65
  triggers compile)
- trigger run as unprivileged `maxx`: `run.log` (open fails with ENOENT —
  hardware gate documented)
- `env.txt`: uname, pciconf, kldstat evidence
- in-tree kernel compile of the vulnerable file (baseline) and of the
  fixed file: `kmod_baseline_build.log` / `fix_build.log`
  (`make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC amdgpu_cs.o`,
  `-Werror`, both rc=0)

## Root cause (path:line)

1. `amdgpu_cs.c:114-115` — `amdgpu_cs_parser_init()` returns **early
   success** (`return 0`) when `cs->in.num_chunks == 0`, before
   `amdgpu_ctx_get()` (121), before the chunk loop (151), and before
   `amdgpu_job_alloc()` (224) — the only place that would have rejected
   `num_ibs == 0` (`amdgpu_job.c:55-56`).
2. `amdgpu_cs.c:1280` — `struct amdgpu_cs_parser parser = {};` so
   `parser.job == NULL`, `parser.entity == NULL`, `parser.chunks == NULL`.
3. `amdgpu_cs.c:1296` → `amdgpu_cs_ib_fill()`:
   - loop at `amdgpu_cs.c:991` (`i < parser->nchunks && ...`) is skipped
     because `nchunks == 0` (`&&` short-circuits before touching
     `parser->job`);
   - `amdgpu_cs.c:1048` — `ring = to_amdgpu_ring(parser->entity->rq->sched);`
     dereferences `parser->entity == NULL`. `rq` sits at offset 16 of
     `struct drm_sched_entity` (`sys/dev/drm/include/drm/gpu_scheduler.h:81`)
     → kernel load from 0x10 → fatal trap → panic.

Reachability: `AMDGPU_CS` is `DRM_AUTH|DRM_RENDER_ALLOW`
(`amdgpu_kms.c:1206`) — any local user with `/dev/dri/renderD*`.

## Fix

`fix.diff` (reject zero-chunk submissions): `return -EINVAL` at the
`num_chunks == 0` check. Nothing has been allocated at that point, and
`amdgpu_cs_parser_fini()` already tolerates the all-NULL parser.
