SMBus block count byte is omitted in both directions: every Block Write through iicsmb is malformed on the wire and every Block Read returns one-byte-shifted data
Summary
bwrite writes [cmd][data...] skipping the count byte; bread transfers *count raw bytes without consuming the device's count - wire-proven on the production chain (stock modules): 'WRITE len=1: 07' then 'WRITE len=5: de ad...' with no 0x05, and spec-compliant replies land shifted (data[0] = the count). Every other in-tree smbus driver does both. ipmi_ssif (the only kernel block consumer) cannot work over this bridge. Count-in-write + read-count-first fix validated (wire [07][05][data], rcount=8 unshifted, deterministic 6x).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5522 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.5 KB | β raw | |
| VERDICT.md | β | 2.9 KB | β raw | |
| df5521_rig.c | β | 7.9 KB | view raw | |
| df5521_poc.c | β | 6.8 KB | view raw | |
| build.sh | β | 83 B | view raw | |
| load_chain.sh | β | 294 B | view raw | |
| build.log | β | 5.4 KB | view raw | |
| env.txt | β | 980 B | view raw | |
| run.baseline.log | β | 1.3 KB | view raw | |
| run.fixed.log | β | 1.3 KB | view raw | |
| fix.diff | β | 1.7 KB | view raw | |
| manifest.json | β | 692 B | view raw | |
| verdict.json | β | 3.8 KB | view raw |
DF-5522 β iicsmb block read/write omit the SMBus count byte in both directions: every Block Write is malformed on the wire and every Block Read returns one-byte-shifted data
Files
Same rig/PoC as DF-5521 (shared evidence pack artifacts copied here for self-containment):
df5521_rig.c software iicbus controller; the emulated slave is
SPEC-COMPLIANT: within one transaction its byte stream is
[count][data...] and it continues across reads
df5521_poc.c proto mode drives SMB_BWRITE + SMB_BREAD on /dev/smb0
run.baseline.log stock-module wire capture (no count byte; shifted data)
run.fixed.log patched-module wire capture (count byte present/consumed)
fix.diff the count-byte fix (also carries the [1,32] validation
of DF-5523, which the rewritten bread needs anyway)
Reproduce
- build + load chain (see build.sh / load_chain.sh; same as DF-5521)
/root/df5521_poc proto- compare
dmesg | grep df5521-wirewith the two logs here.
Baseline: WRITE len=1 bytes: 07 is followed directly by
WRITE len=5 bytes: de ad be ef 99 β no count byte. SMBus 2.0 Block
Write is [addr/W][cmd][count][data...]; ichsmb/intpm/amdsmb all transmit
the count (ichsmb: bus_write_1(sc->io_res, ICH_D0, count)). On read the
bridge never consumes the slave's count byte: it transfers *count raw
bytes, so a compliant slave's count byte lands in the caller's data[0]
and every reply is mis-parsed one byte to the right.
DF-5522 VERDICT β REPRODUCED (wire-protocol conformance bug, both block directions)
The bug
iicsmb_bwrite() (sys/bus/iicbus/iicsmb.c:463-482) writes [cmd] then
count raw data bytes β it never transmits the SMBus byte-count that the
Block Write protocol places between the command and the data
(SMBus spec 2.0 Β§"Block Write": Write, Command, Byte Count, Data...).
iicsmb_bread() (iicsmb.c:485-507) symmetrically never consumes the
count byte the slave sends after the repeated start β it transfers
*count raw bytes and reports the raw transfer length.
Every other in-tree smbus controller driver does both: ichsmb_bwrite
programs ICH_D0 = count and the ICH hardware transmits it
(sys/bus/smbus/ichsmb/ichsmb.c, ichsmb_bwrite), ichsmb_bread returns
sc->block_count read off the wire; intpm/amdsmb are equivalent. ig4's
write-side variant of this exact mistake was filed as DF-5385 (Low).
Consequences for real consumers: /dev/smb block ioctls through an
iicsmb-backed bus put the device's count byte into the caller's data
(stream shifted one byte, length wrong); ipmi_ssif (the only in-tree
kernel block-transfer consumer) mis-parses every SSIF reply
(ssif_buf[0] is the BMC's count byte, not the multi-part indicator it
tests) and its Block Writes are NAKed by any spec-compliant BMC β
IPMI-over-SSIF through this bridge cannot work.
Verification
Rig (see DF-5521 pack): software controller whose slave is deliberately
SPEC-COMPLIANT (sends [count][data...], continues across reads).
Stock modules β run.baseline.log:
WRITE len=1 bytes: 07 WRITE len=5 bytes: de ad be ef 99 <- no count byte between them
Fixed modules β run.fixed.log:
WRITE len=1 bytes: 07 WRITE len=1 bytes: 05 <- count byte on the wire WRITE len=5 bytes: de ad be ef 99 ... READ req_len=1 (consumes the device's count byte = 08) READ req_len=8 (reads exactly the device-declared block) => rcount=8, rbuf=a0 a1 a2 a3 a4 a5 a6 a7 (unshifted, deterministic)
No memory-safety impact by itself (lengths stay bounded by the callers); correctness/data-integrity class β severity Low, consistent with DF-5385.
Fix
fix.diff: transmit the count byte in bwrite; in bread read the device
count first, clamp it to the caller's *count and to the 32-byte SMBus
block limit, then transfer exactly that many bytes and report it.
Note: the new bwrite error path (goto error on count-byte write failure)
inherits DF-1095's known started-state issue until that fix lands; the
rewritten bread STOPs on all paths as before.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedStock: wire shows no count byte on Block Write and no count consumption on Block Read (data one-byte-shifted). Patched (fix.diff): wire shows [cmd][count][data]; reads consume the count (req_len=1) then honor the device block length (req_len=8, rcount=8, rbuf=a0..a7 unshifted); behavior deterministic across 6 consecutive ioctls. Validated as part of the combined fixed-module build shared with DF-5521/5523/5525.
run.fixed.log vs run.baseline.log; fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
run.baseline.log (stock: 'WRITE len=1 bytes: 07' followed directly by 'WRITE len=5 bytes: de ad be ef 99' -- no count byte; reads shift the slave count into data); run.fixed.log (patched: 'WRITE len=1 bytes: 05' count byte present; 'READ req_len=1' consumes count then 'READ req_len=8' -- rcount=8, rbuf=a0..a7 unshifted); fix.diff
PoC changes
shared rig with DF-5521; the rig's slave was made spec-compliant on purpose (byte stream [count][data...] continuing across reads within a transaction) so the bridge's wire behavior is directly observable
Verified recommended fix
bwrite: transmit the count byte between cmd and data; bread: read the device's count byte first, clamp to min(device count, count, 32), transfer exactly that many bytes and report it as count. See fix.diff (validated live).
Verdict
REPRODUCED (protocol conformance, no memory-safety impact): iicsmb_bwrite (iicsmb.c:463-482) never transmits the SMBus block byte-count after the command byte and iicsmb_bread (iicsmb.c:485-507) never consumes the slave's count byte after the repeated start -- it transfers *count raw bytes. Live wire capture with the production chain (isa->dfa_iic0->iicbus0->iicsmb0->smbus0->smb0) against a deliberately spec-compliant emulated slave: stock modules put 'de ad be ef 99' immediately after '07' with no count byte, and block reads returned the slave's count byte shifted into data[0]; with fix.diff the wire is [07][05][data] and reads consume the device count (req_len=1 then req_len=8, rcount=8, unshifted data a0..a7, deterministic). Every other in-tree smbus driver does both halves (ichsmb programs ICH_D0=count; intpm/amdsmb equivalent) -- the bridge is the odd one out, and ipmi_ssif (the only in-tree kernel block consumer) cannot work over it (reply parsing shifts one byte; Block Writes malformed). Severity Low per family precedent DF-5385 (ig4 write-side variant). FIX VALIDATED live (run.fixed.log).
No comments yet.