⬢ DragonFlyBSD Kernel Audit
DF-5539 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5539",
  "status": "reproduced",
  "reproduced": true,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "iicbb_print_child() (sys/bus/iicbus/iicbb.c:164-186) reads an uninitialized stack byte oldaddr (declared :169, address passed to the parent reset at :173) whenever the parents iicbb_reset returns anything other than IIC_ENOADDR, prints it to console/dmesg (:181-182) and writes it back as the restored bus address (:179). No in-tree parent can reach that branch with oldaddr written: lpbb_resets ppbus-busy path returns 0 without touching *oldaddr (lpbb.c:186-190), viabb/cxm always return IIC_ENOADDR. Reproduced on the DragonFly guest with a production-code KLD harness that loads the stock in-tree iicbb.ko under a fake GPIO parent (emulated I2C slave, faithful lpbb busy-path reset): 16/16 attach events printed on iicbb0 addr 0xff -- the untouched stack slot. Machine-code proof: the stock iicbb.ko disassembly of iicbb_print_child contains no write to -0x21(%rbp), only the lea passing &oldaddr and two reads. The same harness drove the full transfer state machine green (write echo, read pattern, NACK->IIC_ENOACK with bus stopped, repeated-start), so the checklist (a)-(d) was exercised live. fix.diff (init oldaddr=0 + else-branch only on IIC_NOERR) validated by rebuilding only iicbb.ko: output becomes deterministic addr 0x0 with zero uninitialized bytes and an unchanged state machine.",
  "exploit_chain": "",
  "evidence": "run.out + dmesg.out: 16x iicbus0: <Philips I2C bus> on iicbb0 addr 0xff and green [fakebb] state-machine markers (write ok=1 read_ok=1, nacked error=2 IIC_ENOACK bus_started_after=0, repstart byte=0xc0, resets_with_oldaddr=1). stock_disasm.txt: iicbb_print_child has NO write to -0x21(%rbp) (only lea at +0x3d and movzbl reads at +0x69/+0xb1). fix_disasm.txt: patched prologue adds movb $0x0,-0x21(%rbp). fix_dmesg.out: addr 0x0, zero occurrences of the uninitialized byte.",
  "kernel_refs": [
    "sys/bus/iicbus/iicbb.c:169",
    "sys/bus/iicbus/iicbb.c:173",
    "sys/bus/iicbus/iicbb.c:177-183",
    "sys/dev/misc/lpbb/lpbb.c:186-190",
    "sys/dev/powermng/viapm/viapm.c:507-514",
    "sys/dev/video/cxm/cxm_i2c.c:273-296",
    "sys/kern/subr_bus.c:2004",
    "sys/kern/subr_bus.c:3007-3013"
  ],
  "poc_changes": "No seed PoC existed (pass-2 finding verified in the same run). Authored the family-standard production-code KLD harness from scratch: iicbb_reg.ko registers the real iicbb_driver on a fake fakebb bus; iicbb_harness.ko instantiates fakebb0 on nexus0 with a full iicbb_if vtable (bit-level emulated slave: START/STOP edge detection, SCL-edge sampling, ACK/NACK injection, TX pattern) whose reset returns 0 without writing *oldaddr (lpbb busy-path faithful), pre-sprays the kstack with a compile-time pattern, and drives the real iicbus API. Iterations: (1) kmod.mk clobbers CFLAGS so -D spray flags were moved to a generated spray_conf.h; (2) 64-frame spray hit the kstack guard page (double fault) -- reduced to 24 frames; (3) module-registration ordering inside one .ko made the attach cascade lag fakebb_attach -- split the (fakebb,iicbb) DRIVER_MODULE into a separate preload KLD.",
  "attempts": 5,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026  root:dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 1900,
  "guest_dirty": 0,
  "build_cmd": "cd /root/df5539 && ./build.sh",
  "run_cmd": "cd /root/df5539 && ./run.sh",
  "code_hash": "f5cf242ca2b84957a6e26b4c2603440c136e8472e42d52cd370f95043d7e6ae3",
  "notes": "Leak is one byte of uninitialized kernel stack to console/dmesg (readable by unprivileged users); trigger is boot-time attach or module load (root). Severity Low; no escalation path for this class. The stack spray could not claim the exact slot (in-cascade device_add_child/kmalloc internals rewrite that depth after the spray) -- the disassembly no-write proof plus 16/16 deterministic 0xff disclosures carry the evidence. Adjacent out-of-file observations for follow-up: iicbus.c:144-146 plain device_add_child leaves NULL ivars while iic.c:115 iicbus_get_addr() dereferences them (NULL-deref hazard when a child driver probes ic/iic/iicsmb children); iic.c:366-369 I2CRDWR copies out IIC_M_RD buffers even after a failed transfer (uninitialized kmalloc heap disclosure to userspace).",
  "recommended_fix": "Initialize u_char oldaddr = 0 in iicbb_print_child and enter the address-print/restore branch only when the parent reset returns IIC_NOERR (see fix.diff).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (initialize oldaddr + gate the else-branch on error == IIC_NOERR) applied to the guests /usr/src copy of sys/bus/iicbus/iicbb.c and only iicbb.ko rebuilt (module, no kernel rebuild). Baseline stock module: 16/16 addr 0xff uninitialized disclosures. Patched module: deterministic addr 0x0, zero occurrences of the uninitialized byte, transfer state machine unchanged (write_ok=1 read_ok=1). Patched disassembly adds the initializing movb $0x0,-0x21(%rbp) store. Guest /usr/src restored to pristine and stock module reloaded afterwards.",
  "fix_evidence": "fix.diff; fix_build.log; fix_dmesg.out (iicbus0: <Philips I2C bus> on iicbb0 addr 0x0, zero addr 0xff); fix_disasm.txt line 16 (movb $0x0,-0x21(%rbp)); dmesg.out (baseline 16x addr 0xff).",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026  root:dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64 (patched iicbb.ko only)"
}