# DF-5132 — amdgpu_kv_notify_message_to_smu() fail-open response validation: SMC timeout (0x00) and undefined codes (0x02–0xFD) reported as success

File: `sys/dev/drm/amd/amdgpu/kv_smc.c` (pass 2, beyond the known DF-2130
missing-`smc_idx_lock` finding — this defect is independent of the lock).

## Root cause

`amdgpu_kv_notify_message_to_smu()` (kv_smc.c:33-55) posts a message to the
Kaveri/Kabini/Mullins SMC microcontroller and then validates the response:

```c
        for (i = 0; i < adev->usec_timeout; i++) {          /* :40 */
                if ((RREG32(mmSMC_RESP_0) & SMC_RESP_0__SMC_RESP_MASK) != 0)
                        break;
                udelay(1);
        }
        tmp = RREG32(mmSMC_RESP_0) & SMC_RESP_0__SMC_RESP_MASK;   /* :45 */

        if (tmp != 1) {                                     /* :47 */
                if (tmp == 0xFF)                            /* :48  Failed  */
                        return -EINVAL;
                else if (tmp == 0xFE)                       /* :50  UnknownCmd */
                        return -EINVAL;
        }

        return 0;                                           /* :54 — fall-through */
```

Per the SMC protocol, `0x01 = PPSMC_Result_OK`, `0xFF = Failed`,
`0xFE = UnknownCmd` (`sys/dev/drm/amd/amdgpu/ppsmc.h:67-68`), and **`0x00`
means "no response yet"**. When the wait loop exhausts `adev->usec_timeout`
(SMC wedged/hung — precisely the condition the check exists for), `tmp` is
`0x00`; the nested `if` only rejects `0xFF`/`0xFE`, so **`tmp==0` falls through
to `return 0` = success**. Every undefined code in `0x02..0xFD` likewise
returns success.

Contrast with the CI sibling in the same driver:
`amdgpu_ci_send_msg_to_smc()` (`sys/dev/drm/amd/amdgpu/ci_smc.c:170-187`)
returns the raw response to its callers, so a timeout (0 ≠ `PPSMC_Result_OK`)
is *observed* as not-OK. KV converts it to a silent success.

## Impact

On an SMC that has stalled/timed out, every DPM operation
(`amdgpu_kv_smc_dpm_enable` kv_smc.c:106-112, BAPM :114-120, the ~40
`amdgpu_kv_copy_bytes_to_smc()` table uploads from kv_dpm.c, force-level and
get-enable-mask messages) is believed to have succeeded. The driver's PM state
machine then proceeds on a dead controller — programming tables onto a wedged
SMC, treating garbage reads as valid state (`amdgpu_kv_dpm_get_enable_mask`
kv_smc.c:57-67 reads `SMC_SYSCON_MSG_ARG_0` only because ret==0). Worst
realistic outcome is PM state desynchronization → wrong clocks/voltage → GPU
hang requiring reboot (local DoS); no kernel memory-safety consequence (the
SRAM write path fails closed — see VERDICT.md). Trigger requires SMC
misbehavior (hardware/firmware fault), not attacker input → **Info
(hardening / fail-closed correctness)**.

The radeon-driver twin `sys/dev/drm/radeon/kv_smc.c:30-52` is byte-identical
(same fall-through); noted here so the radeon side can be twin-filed.

## Reproduce (static)

    ./check_static.sh

asserts, against the tree: (1) the exact fall-through structure at
kv_smc.c:44-54 (no error return for `tmp==0`), (2) `PPSMC_Result_OK == 0x01`
so 0 cannot be a success code, (3) the ci sibling propagates the raw result.

Runtime reproduction requires Kaveri-class APU hardware with a wedged SMC; the
QEMU audit guest has no AMD GPU at all (`pciconf` → 0 devices) — see
VERDICT.md.

## Fix

`fix.diff` — reject anything ≠ `PPSMC_Result_OK`:

```diff
-	if (tmp != 1) {
-		if (tmp == 0xFF)
-			return -EINVAL;
-		else if (tmp == 0xFE)
-			return -EINVAL;
-	}
+	if (tmp != PPSMC_Result_OK) {
+		dev_err(adev->dev, "SMC failed to handle message 0x%x (response 0x%x)\n",
+			id & SMC_MESSAGE_0__SMC_MSG_MASK, tmp);
+		return -EINVAL;
+	}
```
