# DF-3576 — Divide-by-zero kernel panic in iwn_set_timing() via beacon with bintval=0

## What this is

Evidence pack for a **remote-triggerable kernel division-by-zero (panic)** in
`sys/dev/netif/iwn/if_iwn.c:5596-5597` (`iwn_set_timing()`).

## Root cause (source trace, all lines verified in this tree)

```
attacker AP beacons with bintval = 0 (one 16-bit field in an 802.11 beacon)
  -> sys/netproto/802_11/wlan/ieee80211_input.c:545   scan->bintval = le16toh(...)
  -> ieee80211_input.c:700-707    marks IEEE80211_BPARSE_BINTVAL_INVALID
     ...but the STA scan-table consumer NEVER checks that status bit:
  -> sys/netproto/802_11/wlan/ieee80211_scan_sta.c:314  ise->se_intval = sp->bintval;
  -> sys/netproto/802_11/wlan/ieee80211_node.c:818      ni->ni_intval = se->se_intval;   (ieee80211_sta_join)
  -> station completes association (evil-twin AP answers auth/assoc)
  -> iwn_newstate RUN -> iwn_run (if_iwn.c:7237) -> iwn_set_timing (if_iwn.c:5583)
  -> if_iwn.c:5596   val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU;   == 0
  -> if_iwn.c:5597   mod = le64toh(cmd.tstamp) % val;                    % 0
  -> integer divide error (#DE) in kernel mode -> kernel panic
```

## What this pack proves (and what it cannot)

The QEMU audit guest is virtio-only: it has `if_iwn.ko` and the driver
registered (`kldstat -v` shows `pci/iwn`) but **no Intel WiFi device**, so the
in-kernel path cannot be executed end-to-end. Therefore:

1. `iwn_timing_div0.c` executes the **exact arithmetic** of
   `iwn_set_timing()` (copied verbatim from if_iwn.c:5590-5598) with
   `ni_intval = 0` — the value that a bintval=0 beacon produces through the
   net80211 chain above. Result: **SIGFPE (exit 136), 4/4 runs**; the
   control value (`ni_intval = 100`) survives. A compile-time-folded
   division cannot fault, so this is a genuine hardware `div`/`idiv` fault —
   in the kernel it is a fatal #DE.
2. The reachability chain (beacon -> scan cache -> node -> iwn_run) is proven
   by the source citations above; every hop was read in this tree.

## Build / run (on the DragonFly guest)

```
cc -O2 -Wall -o iwn_timing_div0 iwn_timing_div0.c
./iwn_timing_div0        # expect: control OK, then "Floating point exception", exit 136
```

## Expected output

See `run.log` — control prints `survived`, malicious call prints
`ni_intval=0 -> val=0 ; taking modulo` and the process dies with SIGFPE
(exit=136). Stress reruns in `run.2.log`.

## Fix

Guard the divisor (mirrors what the driver already does elsewhere, e.g.
`iwn4965_get_temperature()` guards `r1 == r3`): see `fix.diff`.
