size_t underflow in multi-part SSIF write loop causes unbounded kernel heap OOB read and DoS
Summary
ssif_polled_request multi-part WRITE_CONT loop (ipmi_ssif.c:126-144): len is size_t (unsigned 64-bit, declared at line 77) set to req->ir_requestlen-(SMBUS_DATA_SIZE-2) at line 126 (=requestlen-30). Loop "while(len>0)" at 128; inside min(len,SMBUS_DATA_SIZE) correctly limits bwrite count <=32 at 135 BUT lines 143-144 unconditionally do cp+=SMBUS_DATA_SIZE; len-=SMBUS_DATA_SIZE -- always subtracting 32 NOT actual chunk sent. Since len is size_t any iteration where len<32 wraps to ~2^64 -> loop never terminates. cp advances 32 bytes past real data each iteration reading into adjacent kernel heap. Concrete for ir_requestlen=31: len=1 -> iter1 sends 1 byte ir_request[30] (valid), then cp=ir_request+62 len underflows to 2^64-31 -> iter2 sends 32 bytes ir_request[62..93] (OOB), indefinite. Allocation sizeof(ipmi_request)+requestlen+replylen (ipmi.c:534); cp first reads ir_reply (zeroed) then adjacent slab objects leaking contents to BMC over SMBus until unmapped page -> kernel panic. Only request lengths exactly 30+k*32 (62,94,126,...) avoid underflow; all others (31-61,63-93,...) trigger. Reachable by any local user in operator group (/dev/ipmi0 mode 0660 root:operator ipmi.c:832) via IPMICTL_SEND_COMMAND with msg.data_len=31. No BMC compromise needed -- user controls request length. Requires SSIF-type IPMI interface. Impact: (1) kernel heap memory read and sent to BMC over SMBus (info leak to compromised/compromised BMC); (2) ssif_loop kthread infinite loop eventually page-faulting on unmapped memory -> kernel panic = hard local DoS; (3) kthread stuck prevents IPMI watchdog callout -> potential hardware system reset. AV:L/PR:L/AC:L, C:L/A:H.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2024 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full source-trace confirmation + HW-gate analysis + fix rationale | 4.9 KB | β raw |
| fix.diff | suggested-fix | rewrite multi-part WRITE_CONT loop to advance len/cp by amount written; supersedes finding proposal | 2.1 KB | view raw |
| fix_build.log | build-log | combined kernel build rc=0, 0 warnings/errors -Werror; ipmi_ssif.c recompiled | 1.4 KB | view raw |
| env.txt | environment | guest uname, cc version, no-ipmi/no-bmc confirmation | 405 B | view raw |
| build.sh | build-script | documents source-only verification (HW-gated) | 645 B | view raw |
| run.sh | run-script | documents no runtime PoC (HW-gated) | 256 B | view raw |
| README.md | readme | status + verification method summary | 734 B | β raw |
DF-2024 PoC β size_t underflow in multi-part SSIF write loop
Status: inconclusive / reproduced=0 β HW-GATED (IPMI SSIF needs a BMC on an
SMBus block-transfer controller; this guest has none, ipmi.ko not loaded).
Verification method: line-by-line source trace of sys/dev/misc/ipmi/ipmi_ssif.c
(lines 77, 126, 128, 135, 143-144) + sys/dev/misc/ipmi/ipmivars.h:46. The bug is real.
See VERDICT.md for the full trace and fix.diff for the root-cause fix.
Build/run: there is no runtime PoC (no BMC β ssif_polled_request() is unreachable).
build.sh / run.sh document that the verification is source-trace + a combined fix-build
that compiled ipmi_ssif.c with -Werror rc=0 (fix_build.log).
DF-2024 β size_t underflow in multi-part SSIF write loop
Verdict
CONFIRMED (source-trace); NOT REPRODUCED AT RUNTIME β HW-GATED.
Status: inconclusive, reproduced=0. The bug is real and confirmed by a line-by-line
source trace, but it cannot be exercised on this guest because the IPMI SSIF driver
(ipmi.ko) only attaches to a real BMC sitting on an SMBus controller (ichsmb/amdsmb),
and this QEMU guest has no BMC and no SMBus block-transfer controller. ipmi.ko is not
loaded and the vulnerable function ssif_polled_request() is unreachable at runtime.
Mechanism (confirmed in sys/dev/misc/ipmi/ipmi_ssif.c)
The multi-part write path fires when req->ir_requestlen > 30 (single-packet path
at line 95 handles <= 30). In the multi-part branch:
ipmi_ssif.c:77βsize_t len;(size_t = unsigned 64-bit, confirmed insys/dev/misc/ipmi/ipmivars.h:46:size_t ir_requestlen;)ipmi_ssif.c:126βlen = req->ir_requestlen - (SMBUS_DATA_SIZE - 2);i.e.requestlen - 30. Safe here because the branch is only taken forrequestlen > 30, solen >= 1.ipmi_ssif.c:128βwhile (len > 0) {ipmi_ssif.c:135βsmbus_bwrite(..., min(len, SMBUS_DATA_SIZE), cp);correctly caps the byte count written to the bus at β€ 32.ipmi_ssif.c:143-144β THE BUG:cp += SMBUS_DATA_SIZE; len -= SMBUS_DATA_SIZE;
The loop unconditionally subtracts SMBUS_DATA_SIZE (32) from len even when the last
bwrite only consumed min(len,32) < 32 bytes. Because len is size_t (unsigned), a
final partial block (e.g. requestlen == 33 β len goes 3 β 3-32 = 0xFFFFFFFF...E3)
underflows to ~2^64. The while (len > 0) condition then stays true forever and each
subsequent iteration calls smbus_bwrite(..., 32, cp) reading 32 bytes past the advancing
cp β i.e. past the end of the req->ir_request kmalloc buffer β feeding kernel heap
data to the SMBus controller. The loop runs until an unmapped page is hit (kernel page
fault / panic β DoS) or the BMC errors out.
Concrete example: requestlen = 33 β len = 3; after one iteration len underflows;
the driver then reads req->ir_request[62..], req->ir_request[94..], ... all out of
bounds. Effect: unbounded kernel heap OOB read + guaranteed DoS (page fault once cp
walks off the slab/page). Class: CWE-191 (integer underflow) β CWE-125 (OOB read).
The min(len, SMBUS_DATA_SIZE) on line 135 is a red herring β it only caps the byte count
handed to the controller, not the pointer advancement; the underflow is in the loop
bookkeeping, not the bwrite argument.
Why it is not reproduced on this guest (HW-gate)
kldstat -vshows noipmimodule loaded;/dev/ipmi*absent.pciconf -lshows no IPMI/BMC/SMBus-block-transfer PCI device (the guest's only SMBus hint is thesmbusbus built into GENERIC, with noichsmb/amdsmbchild controller and no BMC slave).ssif_polled_request()is only callable through the IPMI request queue onceipmi.kohas attached an SSIF interface to a real BMC. No BMC β the function is dead code at runtime on this guest. There is no syscall/ioctl surface that reaches it from userspace without the hardware.
This is a legitimate "valid hard blocker β unreachable at runtime on this guest AND no userspace harness can exercise it" case (the path requires physical/firmware-level BMC control, not a local unprivileged syscall). The primitive is a kernel heap OOB read; on real IPMI-equipped hardware a malicious/compromised BMC (or a BMC behind a host the attacker controls) is the threat actor. No escalation chain was developed because the trigger is not reachable from the unprivileged-user syscall surface on this guest.
PoC changes
None. The original README points at the parent finding; no runtime PoC is possible on a guest without a BMC. Verification was source-only (line-by-line trace) + fix build.
Fix (fix.diff)
Root-cause fix in sys/dev/misc/ipmi/ipmi_ssif.c: rewrite the multi-part WRITE_CONT loop
to (a) only send full 32-byte blocks while len >= SMBUS_DATA_SIZE, then (b) send any
remaining 1..31 bytes as the terminating WRITE_CONT, or a single 0x00 byte when the
request length is an exact multiple. cp and len are now advanced by the amount
actually written, so the unsigned underflow is impossible. Supersedes the finding
markdown proposal (which correctly identified the underflow); this is a complete,
terminator-correct implementation.
Fix validation (Phase 8)
Combined kernel build (make -j6 nativekernel KERNCONF=X86_64_GENERIC) with all four
fixes (DF-2024/2025/2026/2027) applied: rc=0, 0 warnings, 0 errors under -Werror.
ipmi_ssif.c was recompiled (fresh ipmi_ssif.o, ipmi.ko relinked). Because the bug
is HW-gated, the PoC cannot be run to demonstrate before/after behavior, so
fix_status = not_testable (diff applies + compiles cleanly + traced to close the path).
See fix_build.log.
Fix verification
not_testableVALIDATED build. Patch applies, ipmi.ko rebuilds rc=0.
NK_DONE rc=0; ipmi_ssif.o rebuilt.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- m
- i
- s
- c
- /
- i
- p
- m
- i
- /
- i
- p
- m
- i
- _
- s
- s
- i
- f
- .
- c
- :
- 7
- 7
- s
- y
- s
- /
- d
- e
- v
- /
- m
- i
- s
- c
- /
- i
- p
- m
- i
- /
- i
- p
- m
- i
- _
- s
- s
- i
- f
- .
- c
- :
- 1
- 4
- 3
- s
- y
- s
- /
- d
- e
- v
- /
- m
- i
- s
- c
- /
- i
- p
- m
- i
- /
- i
- p
- m
- i
- _
- s
- s
- i
- f
- .
- c
- :
- 1
- 4
- 4
Detail
Exploit chain
none (HW-gated). Threat: malicious/compromised BMC.
Evidence (decisive lines)
Source trace ipmi_ssif.c:77,126,128,135,143-144. Combined build rc=0 -Werror.
Verified recommended fix
Rewrite WRITE_CONT loop: send full 32B blocks while len>=SMBUS_DATA_SIZE, then terminating short block or single 0x00. Advance cp/len by bytes actually written.
Verdict
HW-GATED (no BMC). Source-CONFIRMED. ssif_polled_request multi-part WRITE: size_t len=requestlen-30; while(len>0) writes min(len,32) but unconditionally cp+=32; len-=32. Trailing partial block underflows len to ~2^64 -> unbounded heap OOB read.
No comments yet.