# DF-1091 — amdsmb_bread slave-controlled block-read count drives OOB MMIO

## Build

```
cc -O0 -o df1091_harness df1091_harness.c
cc -O0 -DFIX -o df1091_harness_fix df1091_harness.c
```

## Run

```
./df1091_harness        # 223 OOB MMIO reads + unclamped *count=255
./df1091_harness_fix    # 0 OOB; *count clamped to 32
sh verify.sh            # 9 static source-tree checks
```

## Expected (bug present)

- `verify.sh` reports `PASS=9 FAIL=0` (the missing clamp is pinned, the
  sibling `amdsmb_bwrite` validation is pinned as a control).
- `df1091_harness` reports `223 OOB MMIO reads of adjacent EC registers`
  (matching the 255 - 32 overflow in the finding) and
  `RETURNED COUNT > 32: caller (smb.c) will copyout up to 255 bytes`.
- `df1091_harness_fix` reports `0 OOB` and `returned count clamped to 32`.

## Bug shape

`amdsmb_bread` at `sys/bus/smbus/amdsmb/amdsmb.c:517-547`:

```c
if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
    amdsmb_ec_read(sc, SMB_BCNT, &len);          /* :533 slave-controlled 0..255 */
    for (i = 0; i < len; i++) {                  /* :534 bound is len, not 32  */
        amdsmb_ec_read(sc, SMB_DATA + i, &data); /* :535 OOB MMIO when i >= 32 */
        if (i < *count)
            buf[i] = data;                       /* :537 buf write is bounded  */
    }
    *count = len;                                /* :539 unclamped return      */
}
```

- `SMB_DATA` is a 32-byte register file at offsets `0x04..0x23` (`:75`).
  When `len > 32`, the MMIO read at `SMB_DATA + i` overruns into
  `SMB_BCNT`/`SMB_ALRM_*`/follow-on EC space — 223 reads when len=255.
- `*count = len` returns the slave-controlled length (up to 255) to the
  caller. In `sys/dev/smbus/smb/smb.c:318-330`, `SMB_BREAD` then does
  `s->rcount = min(s->rcount, bcount)` and `copyout(buf, s->rbuf, s->rcount)`,
  copying up to 255 bytes from a `char buf[SMB_MAXBLOCKSIZE=1024]` whose
  bytes 32..254 were never written by `amdsmb_bread` — i.e. an
  uninitialized-kernel-stack leak to userspace (per call, when invoked via
  `/dev/smb`).
- The cross-driver sibling `amdsmb_bwrite` (`:491-514`) **does** validate
  `count < 1 || count > 32` (`:497`) — but takes `count` by value. The
  DF-1076 fix for `ichsmb_bread` clamps `block_count` in the ISR.
  `amdsmb_bread` lacks both validations on the slave-supplied length.

## Impact / preconditions

Three impact chains, in order of practicality:

1. **OOB MMIO read (no userland action required)** — a malicious BMC
   reachable via IPMI SSIF can drive this through `ipmi_ssif.c`. Each
   out-of-bounds EC register read has potential side effects on the ACPI
   EC and may lengthen the `AMDSMB_LOCK` hold (DoS).
2. **Uninitialized kernel stack leak via `/dev/smb`** — root/wheel-only
   on hosts with an exposed `smb` device; ~223 bytes of stack leak per
   call.
3. **Lock-hold DoS** — `len=255` extends the `AMDSMB_LOCK` spin hold by
   ~340ms (224 EC reads × up to 500µs `DELAY`).

The audit QEMU guest has no AMD SMBus PCI device (`pciconf -l` shows
Intel PIIX4 PM only, class `0x068000`), so the path is not exercised
dynamically here; the bug is confirmed by source trace + harness.
