# DF-4263 — duplicate CS chunks (BO_HANDLES / FENCE / SYNCOBJ_OUT) leak kernel refs

**File:** `sys/dev/drm/amd/amdgpu/amdgpu_cs.c` (pass 2)
**Severity:** Medium (unprivileged local resource-exhaustion DoS) — CWE-401
**Confidence:** certain (static proof; leak arithmetic exact)

## Build

```
cc -O2 -Wall -o dup_chunk_leak dup_chunk_leak.c
```

## Run (as any user on amdgpu hardware)

```
./dup_chunk_leak
```
Creates one 1 MiB GTT BO, then submits 1000 CS ioctls whose chunk array
holds **two** `AMDGPU_CHUNK_ID_BO_HANDLES` chunks, each listing the same
handle 64 times. Every CS permanently leaks 64 `amdgpu_bo` references plus
one whole `amdgpu_bo_list` (kref 1, never dropped). Repeat (or raise
NENTRY/ROUNDS) until GTT/kernel-memory exhaustion.

## Root cause (path:line)

- The chunk-dispatch loop `amdgpu_cs_parser_init()`
  (`amdgpu_cs.c:151-222`) never rejects a *duplicate* stateful chunk:
  - **BO_HANDLES** (:200-211) → `amdgpu_cs_bo_handles_chunk()` (:78-101)
    → `amdgpu_bo_list_create(..., &p->bo_list)` assigns a **new** list
    (kref 1, holds one `amdgpu_bo` ref per entry —
    `amdgpu_bo_list.c:83,103`) over `p->bo_list` without dropping the
    previous one. `amdgpu_cs_parser_fini()` puts it exactly **once**
    (`amdgpu_cs.c:807-808`). First list + all its BO refs leak forever.
  - **FENCE** (:186-198) → `amdgpu_cs_user_fence_chunk()` (:37-76) does
    `amdgpu_bo_ref()` then overwrites `p->uf_entry.tv.bo` (:52); fini
    unrefs once (:815-819) → one BO ref leaked per extra FENCE chunk.
  - **SYNCOBJ_OUT** — `amdgpu_cs_process_syncobj_out_dep()` (:1138-1163)
    overwrites `p->post_dep_syncobjs` and resets
    `num_post_dep_syncobjs = 0` (:1148-1151), leaking the previous array
    and every `drm_syncobj` reference it holds (`drm_syncobj_find` takes
    a ref — `drm_syncobj.c:83-94`); fini releases only the last array
    (:797-799).
- The ioctl-level guard that exists (`amdgpu_cs.c:587-589`) only rejects
  `bo_list_handle`+chunk, not chunk+chunk.

Reachability: `AMDGPU_CS` is `DRM_AUTH|DRM_RENDER_ALLOW`
(`amdgpu_kms.c:1206).  All chunk types need only well-formed ≥min-size
kdata (FENCE ≥8B, BO_HANDLES ≥24B, SYNCOBJ_OUT ≥4B — enforced at
:188/:202 and by floor-division at :1145-1146).

## Guest verification (this audit)

Same hardware gate as DF-4262 (no AMD GPU → no `/dev/dri`): trigger
builds (`build.log`), run exits at open (`run.log`); baseline + patched
in-tree compile of `amdgpu_cs.c` both rc=0 with `-Werror`
(`kmod_baseline_build.log`, `fix_build.log`).

## Fix

`fix.diff`: reject duplicates — `-EINVAL` when `p->uf_entry.tv.bo` is
already set (FENCE), when `p->bo_list` is already set (BO_HANDLES), and
when `p->post_dep_syncobjs` is already allocated (SYNCOBJ_OUT).
