DF-3662 / verdict.json
{ "finding_id": "DF-3662", "status": "inconclusive", "reproduced": 0, "impact": "panic", "confidence": "certain", "verdict": "Code-certain, hardware-gated: wpi_get_power_index() (if_wpi.c:3812) feeds interpolate()/fdivround() (if_wpi.c:3782-3783) two raw int8_t EEPROM sample powers as (x2)-(x1); when they are equal (all-zero corrupt EEPROM group, or crafted adjacent-equal powers) the integer division by zero raises #DE and panics DragonFly at device bring-up (wpi_config -> wpi_set_txpower), on every channel change and every 60s calibration tick. Guest has the wpi driver compiled in (kldstat pci/wpi) but zero Intel PCI devices (pciconf vendor=0x8086 count 0), so no in-kernel repro is possible; verbatim-arithmetic harness reproduces deterministically 6/6 (SIGFPE exit 136 cases B/C; control case A survives idx=77), matching the DF-3576/DF-3578 iwn-twin methodology. Threat requires a crafted/corrupt EEPROM (malicious PCIe device masquerading as 3945ABG or flash corruption) - Low severity crafted-hardware DoS.", "exploit_chain": "", "evidence": "run.log: baseline case B Floating point exception (core dumped) -- exit 136 (0/0 from all-zero EEPROM group); case C exit 136 (adjacent equal powers); case A survives idx=77. run.stress.log: 6/6 faulting runs across 3 iterations, fixed variant A/B/C all rc=0. build.log: clean cc -O2 -Wall. env.txt: DragonFly 6.5-DEVELOPMENT, cc 8.3, ifconfig vtnet0 lo0 only, pciconf intel count 0 (hardware absent), kldstat pci/wpi present.", "kernel_refs": [ "sys/dev/netif/wpi/if_wpi.c:3812", "sys/dev/netif/wpi/if_wpi.c:3782", "sys/dev/netif/wpi/if_wpi.c:3808", "sys/dev/netif/wpi/if_wpi.c:1574", "sys/dev/netif/wpi/if_wpi.c:1598", "sys/dev/netif/wpi/if_wpi.c:3750", "sys/dev/netif/wpi/if_wpi.c:3724", "sys/dev/netif/wpi/if_wpi.c:1887" ], "poc_changes": "Original sketch (none existed - new finding) authored directly as verbatim-arithmetic harness: fdivround/interpolate macros, sample walk, ridx switch, clamp and EEPROM group bulk-load copied 1:1 from if_wpi.c:3773-3835 and 1574-1610; three cases (realistic control / all-zero corrupt group / crafted adjacent-equal powers).", "attempts": 3, "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": 90, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -o wpi_pwr_div0 wpi_pwr_div0.c && cc -O2 -Wall -o wpi_pwr_div0_fixed wpi_pwr_div0_fixed.c", "run_cmd": "./wpi_pwr_div0 A; ./wpi_pwr_div0 B; ./wpi_pwr_div0 C (and the _fixed variants; see run.sh)", "code_hash": "4946d67d4d342dff964994de7649a2e7d44bbc61980b1209700ca25d7ba5adae", "notes": "wpi twin of DF-3578 (iwn4965_set_txpower interpolate div0). wpi_set_timing %val div0 (if_wpi.c:3673-3674) was investigated and KILLED: ni_intval==0 unreachable in this tree (net80211 rejects bintval outside [25,1000]: ieee80211_input.c:700-707 BPARSE_BINTVAL_INVALID with frame dropped by every consumer ieee80211_sta.c:1332/adhoc.c:701/hostap.c:1707/mesh.c:1904; ic_bintval ioctl-range-checked ieee80211_ioctl.c:3021-3023). TX/CMD ring idx provenance also killed: desc->idx is uint8_t and WPI_TX_RING_COUNT==256, so wpi_tx_done/wpi_cmd_done indexing is in-bounds by construction.", "recommended_fix": "Guard degenerate samples before interpolate(): if (sample[1].power == sample[0].power) idx = sample[0].index; else idx = interpolate(...); (see fix.diff; validated at harness level - fixed variant survives all cases, control output unchanged)", "fix_status": "not_testable", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Kernel-level fix build impossible on this guest (no wpi PCI device; a kernel with the fix cannot attach it). Harness-level fix validation performed: wpi_pwr_div0_fixed.c applies the exact fix.diff guard and survives all three cases (A/B/C rc=0) while the baseline faults 6/6 on B/C; control case A output identical (idx=77), so valid EEPROM behavior is unchanged by the fix.", "fix_evidence": "run.stress.log == fixed == block: fixed A/B/C rc=0 vs baseline run1-3 B/C rc=136; run.log fixed-case outputs SURVIVED (no SIGFPE)" } |