# DF-3863 — 32-bit truncation of `nptes` in `amdgpu_vm_bo_update_mapping()` undersizes the update IB → unbounded PTE-command writes overflow the IB buffer

File: `sys/dev/drm/amd/amdgpu/amdgpu_vm.c` — `amdgpu_vm_bo_update_mapping()` (1797-1933)

## Root cause

amdgpu_vm.c:1807 declares `unsigned nptes, ncmds, ndw;` (32-bit) and
amdgpu_vm.c:1841 computes

```c
	nptes = last - start + 1;      /* uint64_t arithmetic, truncated to u32 */
```

`start`/`last` are GPU page numbers of the range being written. When a single
update covers ≥ 2^32 GPU pages (16 TiB), `nptes` wraps:
- nptes ≡ 0 (span exactly 2^32) → `ncmds = 1`, `ndw ≈ 64 + 10 + frag*…`
  → a ~100-dword IB is allocated (amdgpu_vm.c:1879);
- `amdgpu_vm_update_ptes()` (amdgpu_vm.c:1915 → 1670-1777) then walks the
  page-table leaves of the range and emits `amdgpu_vm_write_pte` /
  `set_pte_pde` packets into `job->ibs[0].ptr` with **no capacity check**
  (the only guard is the after-the-fact `WARN_ON(params.ib->length_dw > ndw)`
  at amdgpu_vm.c:1920) → kernel heap buffer overflow of the kmalloc'd IB,
  one WRITE_PTE packet per 2 MB of the span.

Which callers can hand a ≥2^32-page range to update_mapping in ONE call:
- `amdgpu_vm_clear_freed()` (amdgpu_vm.c:2333-2335) passes the *entire*
  freed mapping span (`mapping->start .. mapping->last`);
- `amdgpu_vm_bo_split_mapping()` with `nodes == NULL` (BO-less **PRT**
  mapping — created via AMDGPU_VA_OP_MAP with AMDGPU_VM_PAGE_PRT, where the
  ioctl uses `fpriv->prt_va` whose `base.bo` is NULL, amdgpu_gem.c:604-631)
  sets `max_entries = S64_MAX` (amdgpu_vm.c:2006-2007) so the full mapping
  span again lands in a single call.

## Reachability (honest gating)

Creating the giant mapping requires `amdgpu_vm_alloc_pts()` over the whole
span first (amdgpu_gem.c:638-641). With the default block_size = 9
(2 MB leaf coverage) a 16 TiB span needs 2^23.5 page tables → infeasible
OOM. With `amdgpu_vm_block_size` ≥ 16 (root module parameter; GMC9 default
VM size is 256 TB — gmc_v9_0.c:940 `amdgpu_vm_adjust_size(adev, 256*1024, 9, 3, 48)`
— so the VA space itself allows it) a 16 TiB PRT mapping needs only 2^16
leaves (256 MB of PT BOs): feasible, and then UNMAPping it
(AMDGPU_VA_OP_UNMAP → vm->freed → amdgpu_gem_va_update_vm →
amdgpu_vm_clear_freed) hits the truncated `nptes` and overflows the IB.
Linux ≤5.0 upstream carries the same `unsigned nptes` (verified against
v4.20 and v5.0 sources); the 5.3+ VM rework removed the whole
pre-size-the-IB-from-total-PTE-count design.

Unprivileged trigger given the config: any render-node user issuing
GEM_VA MAP (PRT) + UNMAP of a 2^32-page span.

## Trigger

`va_nptes_overflow.c` — performs the PRT MAP/UNMAP via DRM_IOCTL_AMDGPU_GEM_VA
on /dev/dri/renderD128. Requires `amdgpu.vm_block_size=16` (and recovery of
a suitably large vm size, default 256 TB on VEGA10) on hardware.

## Fix

`fix.diff`: `uint64_t nptes, ncmds, ndw;` + cap the IB request (fail loudly
on absurd sizes instead of truncating).
