# VERDICT — DF-3662 (wpi_get_power_index interpolate divide-by-zero)

## Bottom line

**Code-certain, hardware-gated: `inconclusive` (kernel-level), arithmetic
`reproduced` 6/6 in the verbatim harness.** The divide-by-zero is real,
deterministic, and sits on the mandatory bring-up path of the driver; the
only reason the kernel itself cannot be crashed on our guest is that the
guest has no Intel 3945ABG PCI device (verified: `pciconf -l` shows zero
`vendor=0x8086` entries; `ifconfig -l` = `vtnet0 lo0`). This mirrors the
treatment of the iwn twins DF-3576/DF-3578 (same class, same guest
limitation, same harness methodology).

## Root cause (path:line)

`sys/dev/netif/wpi/if_wpi.c:3812-3813`:

```c
idx = interpolate(pwr, sample[0].power, sample[0].index,
    sample[1].power, sample[1].index, 19);
```

`interpolate()` (if_wpi.c:3782-3783) expands to `fdivround(a, b, 19)` =
`(((1<<19)*(a))/(b) + (1<<19)/2) / (1<<19)` — an **integer division whose
denominator `b = sample[1].power - sample[0].power`** is the difference of
two raw `int8_t` EEPROM bytes. `wpi_read_eeprom_group()` (if_wpi.c:1574-1610)
bulk-loads the five groups with no monotonicity/distinctness validation, and
the sample-walk (if_wpi.c:3808-3810) only guarantees the loop stops inside
`group->samples[3..4]`; it does not guarantee the two selected samples differ
in power.

Two concrete zero-divisor shapes:

1. **All-zero / corrupt EEPROM group** (flash corruption on a genuine card):
   every `sample[].power == 0` ⇒ `pwr (=0) > sample[1].power (=0)` never
   true ⇒ walk falls through to `samples[3]` ⇒ denominator
   `samples[4].power - samples[3].power == 0` ⇒ `0/0`.
2. **Adjacent equal powers** (crafted device): e.g. powers `{20,20,12,8,4}`
   with `pwr=28` ⇒ walk breaks at `samples[0]` ⇒ denominator
   `samples[1].power - samples[0].power == 0`.

In-kernel this is a #DE on the DIV instruction → `kern_trap`/`trap fatal`
→ **kernel panic** on `ifconfig wpi0 up`, on every channel change, and every
60 s calibration tick (wpi_calib_timeout → wpi_power_calibration →
wpi_set_txpower, if_wpi.c:1887-1897, 3714).

## Reachability / threat model

- Requires the EEPROM content of the card to be crafted or corrupt. The
  EEPROM is on-device; a local unprivileged user cannot write it through any
  driver path (no ioctl surface at all in wpi; sysctl is debug-only and
  compiled out without WPI_DEBUG). ⇒ attacker = malicious PCIe device
  masquerading as 3945ABG, physical EEPROM tampering, or flash corruption.
- Severity therefore **Low** (crafted-hardware DoS), matching DF-3578.

## What was run (guest: DragonFly 6.5-DEVELOPMENT, cc 8.3)

- `build.sh` → `cc -O2` both variants (build.log, zero warnings).
- `run.sh` + 3-run stress (`run.log`, `run.stress.log`):
  - baseline case A (realistic monotonic samples): `idx=77`, exit 0 —
    driver behaves normally on valid EEPROM.
  - baseline case B (all-zero group): **SIGFPE, exit 136** (0/0).
  - baseline case C (adjacent equal powers): **SIGFPE, exit 136**.
  - Deterministic 6/6 faulting runs, 3/3 control survivals.
- `wpi_pwr_div0_fixed.c` (fix.diff guard applied): **A/B/C all exit 0**,
  control output identical (`idx=77`) ⇒ fix changes nothing for valid EEPROM
  and removes the fault for degenerate samples. Fix validated at
  arithmetic-harness level only (no kernel build possible without the
  device; the guard is 4 lines whose kernel effect is exactly the tested
  arithmetic).

## Verdict fields

- status: `inconclusive` (kernel-level repro impossible: no wpi hardware;
  arithmetic-level repro deterministic — iwn-twin convention DF-3576/3578)
- impact: `panic`
- confidence: `certain` (code path + arithmetic both proven from source)
- fix_status: `not_testable` at kernel level; harness-level fix validation
  passed (see run.stress.log `== fixed ==` block).

## Not this finding (checked and killed)

- `wpi_set_timing` `% val` div0 (if_wpi.c:3673-3674): `ni->ni_intval == 0`
  is unreachable in this tree — net80211 rejects beacon bintval outside
  [25,1000] at parse time (ieee80211_input.c:700-707,
  IEEE80211_BPARSE_BINTVAL_INVALID; every consumer drops the frame:
  ieee80211_sta.c:1332, ieee80211_adhoc.c:701, ieee80211_hostap.c:1707,
  ieee80211_mesh.c:1904) and the only other writer, `ic->ic_bintval`, is
  ioctl-range-checked (ieee80211_ioctl.c:3021-3023, default 100 at
  ieee80211.c:372). The DF-3576 premise ("scan consumer stores it anyway")
  does not hold in the current tree. Driver-side zero-guard still
  recommended as defense-in-depth.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
