⬢ DragonFlyBSD Kernel Audit
DF-3578 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3578",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Division by zero in iwn4965_set_txpower(): interpolate() (if_iwn.c:5636-5637) divides by chans[1].num - chans[0].num, two raw EEPROM bytes bulk-loaded without validation at if_iwn.c:2314; a malicious/corrupt 4965 EEPROM carrying equal sample channel numbers makes the divisor zero and the kernel takes a #DE at device bring-up (iwn_config -> ops->set_txpower, if_iwn.c:6798). The verbatim macros were executed on the DragonFly guest with EEPROM values routed through volatile stores: control (36/64) survives, malicious (36/36) dies SIGFPE exit 136, deterministic 4/4. First harness iteration was silently constant-folded by gcc -O2 (documented in VERDICT.md) and had to be hardened - a real hardware idiv, not a compiler fold. In-kernel execution impossible on the virtio-only guest (no 4965 card), hence inconclusive-at-runtime / certain-at-source.",
  "exploit_chain": "malicious/tampered 4965 EEPROM with chans[0].num == chans[1].num in any power group -> machine boots / ifconfig wlan0 up -> iwn4965_read_eeprom loads sc->bands unvalidated -> iwn_config -> iwn4965_set_txpower -> interpolate() divides by 0 -> #DE -> kernel panic (persistent DoS: faults again on every bring-up).",
  "evidence": "run.log (control OK; malicious EEPROM (nums 36/36) then exit=136 SIGFPE), run.2.log (3/3 stress), iwn_eeprom_div0.c (verbatim macros, volatile-fed operands), VERDICT.md (source trace + constant-folding pitfall), fix.diff.",
  "kernel_refs": [
    "sys/dev/netif/iwn/if_iwn.c:2314",
    "sys/dev/netif/iwn/if_iwn.c:5633",
    "sys/dev/netif/iwn/if_iwn.c:5636",
    "sys/dev/netif/iwn/if_iwn.c:5707",
    "sys/dev/netif/iwn/if_iwn.c:6798",
    "sys/dev/netif/iwn/if_iwn.c:7199"
  ],
  "poc_changes": "No seed existed; harness authored in this run. Values fed through volatile after the plain-struct version was constant-folded by gcc -O2 (folded 0/0 does not fault; the hardened version faults 4/4).",
  "attempts": 4,
  "guest_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",
  "runtime_sec": 10,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o iwn_eeprom_div0 iwn_eeprom_div0.c",
  "run_cmd": "./iwn_eeprom_div0   # expect exit=136 (SIGFPE) after control succeeds",
  "code_hash": "8335b52c5ed94d90e64b02f4dd4c3f793fb21da3b6f8f72208e0f9d421ddd3b5",
  "notes": "Threat model: malicious/corrupt EEPROM = physical tamper or root-controlled flashing (audit-strategy surface EEPROM parse). Sibling pattern: maxpwr values ARE range-checked at if_iwn.c:2305-2309, the band sample channels are not.",
  "recommended_fix": "Validate chans[1].num > chans[0].num (return EINVAL otherwise) before interpolate() in iwn4965_set_txpower, or validate the whole sc->bands table after the EEPROM bulk read.",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff authored against the read-only sys/ tree; kernel-build validation impossible on the virtio-only guest (no 4965 hardware, and the trigger is EEPROM contents). Baseline fault primitive reproduced (SIGFPE exit 136); the guard returns EINVAL before the division.",
  "fix_evidence": "findings/poc/DF-3578/fix.diff"
}