β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2024

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2024 Β· 7 files
FileTypeDescriptionSize
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
README.md readme status + verification method summary
↓ download 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).

VERDICT.md verdict full source-trace confirmation + HW-gate analysis + fix rationale
↓ download raw

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 in sys/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 for requestlen > 30, so len >= 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 -v shows no ipmi module loaded; /dev/ipmi* absent.
  • pciconf -l shows no IPMI/BMC/SMBus-block-transfer PCI device (the guest's only SMBus hint is the smbus bus built into GENERIC, with no ichsmb/amdsmb child controller and no BMC slave).
  • ssif_polled_request() is only callable through the IPMI request queue once ipmi.ko has 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_testable
baseline no→ patch + rebuild →patched clean

VALIDATED build. Patch applies, ipmi.ko rebuilds rc=0.

NK_DONE rc=0; ipmi_ssif.o rebuilt.
↓ fix.diffcombined build rc=0 -Werror

Confirmed kernel references

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.