# DF-2214 — 32-bit wrap in `rv770_copy_dma` DMA copy size silently truncates TTM buffer moves

**Status: REPRODUCED (verbatim-code KLD rig)** — impact class: GPU-side memory
integrity (silent data corruption of video/GTT buffers). No host kernel memory
corruption, no privilege-escalation primitive (verified — see VERDICT.md).
**Fix: A/B-validated in the same KLD** (`fix.diff`, u64 widening identical in
shape to the verified evergreen/si fix).

## What this pack contains

| file | role |
|---|---|
| `rig_head.c` | shims + minimal fabricated boundary structures (real constants, real ring semantics, every shim cites its production origin) |
| `rig_tail.c` | matrix driver + module glue |
| `verbatim_baseline.c` | **unmodified production function**, lines 42-94 of `sys/dev/drm/radeon/rv770_dma.c`, extracted by build.sh on the guest (sha256 pinned) |
| `verbatim_fixed.c` | same bytes + `fix.diff` applied + function renamed |
| `df2214_rig.c` | the exact assembled source that was compiled kernel-resident (head + baseline-verbatim + fixed-verbatim + tail) |
| `build.sh` | provenance chain + KLD build on the guest |
| `run.sh` | kldload once, capture dmesg matrix |
| `run_extract.log`, `run2_extract.log`, `run3_extract.log` | three identical decisive runs |
| `run_dmesg.log` | full message buffer of the decisive run |
| `build.log`, `run.log` | full untrimmed build + run transcripts |
| `kldload_panic_iteration.txt` | iteration note: first build panicked the *loader* (missing kern.mk flags); module never ran; guest reset |
| `fix.diff` | git-apply-able unified diff (u64 widening of the size computation) |
| `env.txt` | guest uname, cc, hashes, kldstat |
| `VERDICT.md`, `manifest.json`, `verdict.json` | verdict narrative + machine-readable results |

## How to reproduce

On the DragonFly guest (root, `/usr/src` present):

```sh
./build.sh   # rebuilds everything from the tree file; aborts on any hash mismatch
./run.sh     # kldloads the rig; prints the 16-case matrix to dmesg
```

## Provenance (checked at build time, build aborts otherwise)

1. guest tree file `sha256 = b33101a3cce7eba62ac2d1dda4e1ec0f7225028942cdbb1cafd1c9a2670e33d9`
   == audited `sys/` tree file.
2. verbatim function = lines 42..94 == pattern extraction, pinned at
   `sha256 = c1b2d82ab166f9e04f7158a8d6fe0506d22ee1c6f05aca121ca0ac9b5a6b547c`.
3. fixed variant = `fix.diff` mechanically applied to those same bytes.

## Expected output (baseline = unmodified production code)

```
DF2214 CASE variant=BASELINE pages=0x00100000 ...      # num_gpu_pages = 2^20
DF2214   num_loops=0 copy_packets=0 ... scheduled_bytes=0 (0.0000% of request) fence_emitted=1
DF2214   VERDICT: SILENT_TRUNCATION (fence says success, 0 of 4294967296 bytes scheduled)

DF2214 CASE variant=BASELINE pages=0x000fffff ...      # 2^20-1: just below the wrap
DF2214   num_loops=16385 copy_packets=16385 sum_chunk_dw=1073740800 ...
DF2214   scheduled_bytes=4294963200 (100.0000% of request) fence_emitted=1
DF2214   VERDICT: FULL_COPY

DF2214 CASE variant=BASELINE pages=0x000fffff ring=real64K ...   # real 64 KiB ring (rv770.c:1968)
DF2214   ERR_PTR(-12): ring reservation refused, ZERO packets, NO fence - loud failure

DF2214 CASE variant=BASELINE pages=0x00100000 ring=real64K ...
DF2214   ... scheduled_bytes=0 (0.0000%) fence_emitted=1 ... SILENT_TRUNCATION
```

With `fix.diff` (variant=FIXED): every case schedules exactly 100.0000% of the
request (or fails loudly `ERR_PTR(-12)` when the ring genuinely cannot hold
it), and every sub-2^20 case is **bit-identical** to baseline (16385 packets,
same first/last chunk sizes, same fence behavior) — no regression.

## Attacker input

`num_gpu_pages` comes from the TTM layer: `radeon_move_blit`
(sys/dev/drm/radeon/radeon_ttm.c) computes
`num_pages = new_mem->num_pages * (PAGE_SIZE / RADEON_GPU_PAGE_SIZE)` and
passes it straight to the asic copy callback. A buffer-object move of >= 4 GiB
(`num_gpu_pages >= 2^20`) — plausible on large-GTT radeon systems — flips the
bug. The DMA engine then copies a fraction (or nothing) while
`radeon_fence_emit` still signals success, so `ttm_bo_move_accel_cleanup`
publishes a destination buffer that was never written: silent video-memory
integrity corruption.
