# DF-5507 PoC — /dev/smb SMB_BREAD hands amdsmb_bread an uninitialized kernel-stack count: feature-dead for 224/256 stack bytes, and for garbage in [1,32) it returns a count larger than the bytes it stored → kernel-stack disclosure via smbioctl copyout

## Root cause (amdsmb side of the DF-5458 twin)

`amdsmb_bread` (`sys/bus/smbus/amdsmb/amdsmb.c:517-547`) implements the
smbus(4) in/out-count contract: the caller passes its requested capacity
in `*count`; on success the driver stores `min(hw_len, *count)` bytes
into `buf` and sets `*count` to the **hardware byte count `len`**
(amdsmb.c:533-539):

```c
amdsmb_ec_read(sc, SMB_BCNT, &len);
for (i = 0; i < len; i++) {
        amdsmb_ec_read(sc, SMB_DATA + i, &data);
        if (i < *count)                    /* stores only up to capacity */
                buf[i] = data;
}
*count = len;                              /* reports hw len, not stored */
```

The only in-kernel caller, `/dev/smb` smbioctl
(`sys/dev/smbus/smb/smb.c:318-330`), passes a **kernel-stack byte that is
never initialized**:

```c
u_char bcount;                              /* smb.c:317 */
...
case SMB_BREAD:
        ...
        error = smbus_bread(bus, s->slave, s->cmd, &bcount, buf);  /* :323 */
        ...
        if (s->rcount > bcount) s->rcount = bcount;                /* :327 */
        error = copyout(buf, s->rbuf, s->rcount);                  /* :329 */
```

`buf` there is `char buf[SMB_MAXBLOCKSIZE=1024]` on the smbioctl kernel
stack (smb.c:211). Two consequences, both exercised through the real
production `amdsmb_bread`:

1. **EINVAL gate**: garbage stack byte 0 or 33..255 → `amdsmb_bread`
   rejects with `SMB_EINVAL` (amdsmb.c:524-525) — SMB_BREAD on amdsmb is
   dead for 224/256 possible stack bytes (deterministic failure; this is
   the exact twin of ichsmb's DF-5458, filed there against the same
   smb.c defect).
2. **Kernel-stack disclosure**: garbage byte g in [1,32) with a
   perfectly healthy slave delivering len=32 → the driver stores only g
   bytes but reports `*count = 32`; smbioctl then copyouts
   `min(rcount<=1024, 32)` bytes from `buf`, of which `32 - g` were
   never written — up to **31 uninitialized kernel-stack bytes per
   call** with a *well-behaved* slave (no DF-1091 hostility needed).
   Combined with the known slave-controlled count (DF-1091, len up to
   255) the disclosure grows to `min(1024, len) - g` ≥ 254 bytes.

Additionally `amdsmb_bwrite` transmits whatever garbage g in [1,32] it
receives as the on-wire byte count (SMB_BCNT gets g — T3w), i.e. a
malformed-length Block Write, again the DF-5458 pattern.

Reachability: `/dev/smbN` (root on stock installs) → ioctl SMB_BREAD /
SMB_BWRITE. Hardware-gated (AMD-8111) — evidence via the production-code
KLD harness (see DF-5506 pack; amdsmb.c asserted byte-identical to
/usr/src).

## Observed (run.log, T3/T3w — real production amdsmb_bread/bwrite)

```
g=  0 -> rc=256 SMB_EINVAL        g=  1 -> rc=0 count=32 stale=31
g= 33 -> rc=256 SMB_EINVAL        g=  5 -> rc=0 count=32 stale=27
g=255 -> rc=256 SMB_EINVAL        g= 31 -> rc=0 count=32 stale= 1
                                  g= 32 -> rc=0 count=32 stale= 0
bwrite: g in [1,32] -> transmitted_BCNT = g (garbage length on the wire)
```

## Observed (run_fixed.log — fix.diff applied to the harness copy)

`fix.diff` clamps the driver out-count to what it actually stored:

```
g=1 -> count=1 stale=0    g=5 -> count=5 stale=0
g=31 -> count=31 stale=0  g=32 -> count=32 stale=0
```

The driver-side disclosure is gone for BOTH input vectors (garbage count
and the known DF-1091 hostile len: T4 fixed stops at register 0x24,
count=32). The EINVAL gate for g∈{0,33..255} remains by design until
smb.c initializes `bcount` (`bcount = s->rcount;` before smb.c:323) —
that one-line smb.c change is the complete upstream fix for the
uninitialized-input side and is recommended alongside.

## Build & run

Same rig as DF-5506: `guest# cd /root/amdh && sh build.sh && sh run.sh`
(tests T3/T3w are this finding; T1/T2/T2b belong to DF-5506).
