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

## Verdict

**REPRODUCED via source-only trace.** The signed `int16_t table_id`
parameter in `smu10_copy_table_from_smc` / `_to_smc` lets a caller-passed
`uint16_t` value of `0xFFFF` narrow to `int16_t -1`, sail through the
`table_id < MAX_SMU_TABLE` guard, and index the `entry[]` array at `[-1]`
OOB. Same pattern as DF-2021 (vega12) and DF-2034 (vega10). Latent
(no in-tree caller forwards an attacker-controlled `table_id`).

## Mechanism (path:line)

The two copy functions take `int16_t table_id` but their public API
contract is `uint16_t`:

* `sys/dev/drm/amd/powerplay/smumgr/smu10_smumgr.c:115-116`
  ```c
  static int smu10_copy_table_from_smc(struct pp_hwmgr *hwmgr,
          uint8_t *table, int16_t table_id)   /* <-- signed */
  ```
* `sys/dev/drm/amd/powerplay/smumgr/smu10_smumgr.c:143-144`
  ```c
  static int smu10_copy_table_to_smc(struct pp_hwmgr *hwmgr,
          uint8_t *table, int16_t table_id)   /* <-- signed */
  ```

The bound check uses signed comparison:

* `smu10_smumgr.c:121` and `:149`
  ```c
  PP_ASSERT_WITH_CODE(table_id < MAX_SMU_TABLE,
          "Invalid SMU Table ID!", return -EINVAL;);
  ```

`MAX_SMU_TABLE` is `2` (`smu10_smumgr.h:30`) and `entry[]` is a
2-element array (`smu10_smumgr.h:42`).

**The public API contract is `uint16_t`** in three places:

* `sys/dev/drm/amd/powerplay/inc/smumgr.h:114`
  `extern int smum_smc_table_manager(struct pp_hwmgr *, uint8_t *, uint16_t, bool);`
* `sys/dev/drm/amd/powerplay/inc/hwmgr.h:218`
  `int (*smc_table_manager)(struct pp_hwmgr *, uint8_t *, uint16_t, bool);`
* `sys/dev/drm/amd/powerplay/smumgr/smu10_smumgr.c:277`
  ```c
  static int smu10_smc_table_manager(struct pp_hwmgr *hwmgr, uint8_t *table,
                                     uint16_t table_id, bool rw)
  ```

So when `smu10_smc_table_manager` (uint16_t) calls
`smu10_copy_table_from_smc` (int16_t) with `table_id = 0xFFFF`, the
implicit narrowing converts `0xFFFF` to `int16_t -1`. Then
`-1 < MAX_SMU_TABLE(=2)` is true (signed compare), so the guard passes,
and `priv->smu_tables.entry[-1]` is read OOB. The OOB `.size` controls
the `memcpy` length; the OOB `.table`/`.mc_addr` control source and
destination. Net: arbitrary kernel heap read (`_from_smc`) or heap
write (`_to_smc`).

## Reachability / impact ceiling

**Latent.** Both SMU10 callers (`smu10_hwmgr.c:427` with
`SMU10_CLOCKTABLE=1` and `:1141` with `SMU10_WMTABLE=0`) pass
compile-time constants in `[0,1]`. No sysfs/ioctl/debugfs path forwards
a user-controlled `table_id` for the SMU10 driver. If a future code path
routed a user- or firmware-controlled 16-bit selector through
`smum_smc_table_manager(hwmgr, buf, 0xFFFF, true)`, the result would be
a heap OOB read/write — the same primitive as DF-2021/DF-2034.

## Fix

`fix.diff` widens the parameter type from `int16_t` to `uint16_t` in
both copy functions, matching the public API contract. The
`PP_ASSERT_WITH_CODE(table_id < MAX_SMU_TABLE, ...)` guard now operates
on an unsigned value, so `0xFFFF < 2` correctly fails and returns
`-EINVAL` before the OOB index.

Matches the finding markdown's recommendation (`int16_t → uint16_t`
parameter type in both copy functions).

## Phase-8 build validation

Combined kernel + modules build (DF-2068 / DF-2069 / DF-2070 / DF-2071 /
DF-2072) on DragonFly 6.5-DEVELOPMENT #0 baseline:

* `=== NK_DONE rc=0 ===` (2026-07-25 11:31:20 UTC)
* `0` `error:` lines in the full 35,696-line build log
* `smu10_smumgr.c` is part of `amdgpu.ko`; the patched TU compiled
  clean with the module's default `-Wno-pointer-sign -Werror` flags
  and linked into `amdgpu.ko` (verified: `smu10_smumgr.o` present).

The default kernel build IS a `-Werror` build; see `fix_build.log` and
`env.txt`.

## Reproduce

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