# DF-2068 — REPRODUCED (source-confirmed, latent) + FIX VALIDATED

## Verdict

**REPRODUCED via source-only trace.** The unchecked `capacity * struct_size`
multiply pattern is real in three `dal_vector_*` entry points; the finding's
classification as defense-in-depth / latent is also confirmed (no in-tree
caller currently supplies a value that overflows `uint32_t`).

## Mechanism (path:line)

The DRM linux-compat shim defines `kcalloc` as a raw multiply with no
overflow guard:

* `sys/dev/drm/include/linux/slab.h:44`
  ```c
  #define kcalloc(n, size, flags) kzalloc((n) * (size), flags)
  ```

Three vector.c call sites pass attacker-influenceable `uint32_t` operands
into that macro:

* `sys/dev/drm/amd/display/dc/basics/vector.c:43`
  `dal_vector_construct`: `kcalloc(capacity, struct_size, GFP_KERNEL)` then
  `vector->capacity = capacity` — if the multiply wraps, `container` is
  tiny but `capacity` is huge, so subsequent `dal_vector_deposit()`/
  `dal_vector_append()` writes out of bounds.
* `sys/dev/drm/amd/display/dc/basics/vector.c:71`
  `dal_vector_presized_costruct`: same pattern with `count`.
* `sys/dev/drm/amd/display/dc/basics/vector.c:293-294`
  `dal_vector_reserve`: hand-rolled `krealloc(..., capacity * struct_size)`
  with the same wrap.

If a caller ever supplies `capacity = 0x40000000, struct_size = 16` the
product `2^34` truncates to `0` in `uint32_t`, `kzalloc(0)` returns a
tiny slab object, and `vector->capacity = 0x40000000` so the next
`deposit()` writes 16 bytes far past the slab — a classic heap overflow.

## Reachability / impact ceiling

**Latent.** The single in-tree caller of `dal_vector_construct` is
`dc_link_ddc.c` (via aux engine), which uses a small compile-time
`sizeof(struct i2c_reg_helper)` and `count <= 32`; nothing in the tree
feeds parsed (EDID/MST) data into `capacity`. The primitive is therefore
a real defense-in-depth gap that would become a heap overflow the moment
a future caller routes untrusted data through `dal_vector_create`.

## Fix

`fix.diff` adds `if (capacity > 0xffffffffu / struct_size) { ... return false; }`
guards in all three functions, matching the existing
`if (!struct_size || !capacity)` early-return pattern. Uses the literal
`0xffffffffu` (no `<stdint.h>` in kernel headers, so `UINT32_MAX` is
unavailable; the literal is the kernel idiom).

Supersedes the finding markdown's recommendation (overflow guard at every
multiply).

## Phase-8 build validation

Combined kernel + modules build of all 5 fixes (DF-2068 / DF-2069 /
DF-2070 / DF-2071 / DF-2072) on DragonFly 6.5-DEVELOPMENT #0 baseline
with `make -j6 nativekernel KERNCONF=X86_64_GENERIC`:

* `=== NK_DONE rc=0 ===` (build completed 2026-07-25 11:31:20 UTC)
* `0` `error:` lines in the full 35,696-line build log
* Modules (including amdgpu.ko, which contains `vector.o`) compile with
  `-Wall ... -Wno-pointer-sign -Werror` by default; the patched
  `vector.c` TU compiled clean and linked into `amdgpu.ko`.

The default DragonFly kernel/module build **is** a `-Werror` build
(`bsd.sys.mk` enables `-Werror` whenever the compiler is gcc80, which is
what the guest uses); `-Wno-pointer-sign` suppresses the pre-existing
linux-compat shim noise. See `fix_build.log` for the full untrimmed
output and `env.txt` for the guest environment.

## Reproduce

```
./build.sh    # rebuilds the patched kernel (rc=0 with -Werror)
./run.sh      # source-only confirmation; no runtime PoC (latent)
```
