# DF-3907 — xl_rxeof() interrupt-mode Rx walk has no work bound (DF-3761 family, xl(4) instance)

**File:** `sys/dev/netif/xl/if_xl.c` (pass 2, GLM 5.3)
**Hardware-gated:** the audit guest has no 3Com 3c90x NIC; source-level
certainty; status `inconclusive` per family convention (same treatment as
DF-3761/DF-3904).

## The bug (source)

`xl_rxeof(struct xl_softc *sc, int count)` (`sys/dev/netif/xl/if_xl.c:1948`)
already has the *plumbing* for a work bound — but only the ifpoll(4) path
uses it:

```c
while ((rxstat = le32toh(sc->xl_cdata.xl_rx_head->xl_ptr->xl_status))) {
#ifdef IFPOLL_ENABLE
        if (count >= 0 && count-- == 0)     /* :1960 - bound ONLY when count >= 0 */
                break;
#endif
        ...
}
```

Every **interrupt-path** caller passes `-1`, disabling the bound entirely:

- `xl_intr()` `:2335` — `xl_rxeof(sc, -1)` (main RX path)
- `xl_intr()` `:2340` — `while (xl_rx_resync(sc)) xl_rxeof(sc, -1);`
  (unbounded resync retry loop)
- `xl_start_body()` `:2616` — `xl_rxeof(sc, -1)` (TX-completion RX assist)
- `xl_watchdog()` `:3123` — `xl_rxeof(sc, -1)`

Additionally, for non-905B (boomerang) chips the function ends with a
stall/reload/`goto again` (`:2041-2063`) that restarts the whole unbounded
walk whenever the upload engine hit the end of the ring — precisely the
state a line-rate flood produces.

The walk runs inside `xl_intr()` holding `ifp->if_serializer`
(`bus_setup_intr(..., ifp->if_serializer)` at `:1529-1531`).

Consequence (on xl hardware): a remote unauthenticated line-rate flood
keeps `xl_status` non-zero forever → the interrupt handler never returns,
pinning its CPU and the serializer (RX livelock / remote DoS; DF-3761
family, alc(4) twin).

## Why not runtime-verified

No 3c90x NIC on the audit QEMU guest. The bound plumbing exists and is
proven correct in the ifpoll path (`xl_npoll_compat` passes a real
`count`, `:2273`); the finding is that the default interrupt mode never
uses it. `verdict.json` records `inconclusive`, reproduced=0.

## Fix

`fix.diff`: pass a `XL_RX_PROC_MAX` (64) budget from the interrupt-path
callers (the `count` plumbing already implements the bound — `count >= 0`
enables it), and cap the `while (xl_rx_resync(...))` retry loop. The
ifpoll path is unchanged.
