sk_rxeof() Rx walk has no work bound β for(;;) loop's only exit is the OWN bit, so a line-rate remote flood keeps the walk running forever under the card-global serializer (DF-3761 family)
Summary
sk_rxeof processes completed RX descriptors in an unbounded for(;;) whose only exit is the NIC-owned OWN bit on the next descriptor; every processed descriptor is immediately reposted, so under a remote line-rate flood the BMU re-completes descriptors behind the walker and the loop never exits - inside sk_intr holding the single card-global serializer shared by both ports. Remote-unauthenticated RX livelock: softclock (incl. the tick), netisr, and the other port of a dual-port NIC all starve. Hardware-gated; budget fix mirrors msk's in-tree process_limit.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3904 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.5 KB | β raw | |
| sk_rxeof_walk_shape.c | β | 1.3 KB | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| manifest.json | β | 962 B | view raw | |
| verdict.json | β | 2.9 KB | view raw |
DF-3904 β sk_rxeof() Rx walk has no work bound (DF-3761 family, sk(4) instance)
File: sys/dev/netif/sk/if_sk.c (pass 2, GLM 5.3)
Hardware-gated: the audit guest has no SysKonnect SK-98xx/Yukon NIC, so
the driver path cannot be exercised at runtime. Source-level certainty only;
status inconclusive per family convention (identical treatment as the
alc(4) original DF-3761).
The bug (source)
sk_rxeof() (sys/dev/netif/sk/if_sk.c:1803-1888) processes completed RX
descriptors in an unbounded for (;;) loop:
- only exit conditions are (a) the next descriptor still has
SK_RXCTL_OWNset (:1833-1837) β i.e. the walker caught up with the NIC β or (b) nothing else: no packet counter, no budget. - every processed descriptor is immediately handed back to the NIC with
ownership via
sk_newbuf()/repost (:1873,:1862,:1875), so under a line-rate remote flood the BMU can re-complete descriptors behind the walker's back; after one lap (256 descriptors) the loop reaches index 0 again, finds it completed, and keeps going β indefinitely. - the walk runs inside
sk_intr()(:2212-2221) which holds the ONE card-globalsc->sk_serializerfor the whole interrupt (bus_setup_intr(..., &sc->sk_serializer)at:1514), on both ports of a dual-port card.
Consequence (on sk hardware): a remote unauthenticated flood pins the
interrupt CPU and the card serializer forever β RX livelock / remote DoS,
starving softclock (including the very sk_tick DF-3903 is about),
netisr, and the other port's TX/RX on the same serializer. This is the
same defect class as DF-3761 (alc(4)); the sibling driver msk(4) bounds
the identical walk with sc->msk_process_limit
(sys/dev/netif/msk/if_msk.c:3225, tunable mskc.process_limit),
proving the fix shape in-tree.
Why not runtime-verified
No SysKonnect PCI NIC exists on the audit QEMU guest (pciconf -l has no
0x10b3/0x1148 Yukon/GEnesis IDs), so sk_rxeof is unreachable at runtime.
The unbounded loop is however directly visible in source and requires no
hardware state to reason about. verdict.json records inconclusive
(matching how DF-3761/3806/3850-family hardware-gated findings were
persisted), reproduced=0.
Fix
fix.diff: add an SK_RX_PROC_MAX (64) packet budget to sk_rxeof,
saving the resume index (cd->sk_rx_prod = i) before breaking β exactly
what the loop already does at the OWN-bit exit (:1835), so the
interrupt re-entry protocol is preserved. Mirrors msk(4)'s
msk_process_limit.
Fix verification
inconclusivefix.diff authored against the read-only sys/ tree (budget + resume-index exit identical in shape to the existing OWN-bit exit). Not runnable on this guest (no sk NIC); the same fix shape is proven in-tree by msk(4).
findings/poc/DF-3904/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-3904/README.md (loop shape + only-exit analysis); findings/poc/DF-3904/sk_rxeof_walk_shape.c (annotated loop shape); findings/poc/DF-3904/fix.diff (budget + resume index, msk-style)
PoC changes
No seed PoC. No runnable trigger exists without SysKonnect hardware; the pack documents the source-level loop shape and the msk(4) in-tree precedent for the fix.
Verified recommended fix
Bound sk_rxeof with a per-interrupt packet budget (SK_RX_PROC_MAX=64) saving cd->sk_rx_prod before break, mirroring msk(4)'s mskc.process_limit.
Verdict
Source-certain, hardware-gated (no SysKonnect NIC on the audit guest) -> inconclusive per family convention, same treatment as the alc(4) original DF-3761. sk_rxeof (sys/dev/netif/sk/if_sk.c:1820-1888) walks completed RX descriptors in a for(;;) loop whose ONLY exit is the NIC-owned (SK_RXCTL_OWN) bit on the next descriptor (:1833-1837); every processed descriptor is immediately reposted to the NIC (:1862/:1873/:1875), so a line-rate remote flood keeps re-completing descriptors behind the walker and the loop never observes OWN - it runs forever inside sk_intr (:2212-2221) holding the ONE card-global sc->sk_serializer (bus_setup_intr with &sc->sk_serializer at :1514), on both ports of dual-port SK-98xx cards. Remote-unauthenticated RX livelock/DoS: the interrupt CPU is pinned, softclock/netisr and the other port starve. The sibling driver msk(4) bounds the identical walk via sc->msk_process_limit (sys/dev/netif/msk/if_msk.c:3225, tunable mskc.process_limit). fix.diff adds an SK_RX_PROC_MAX=64 budget with the same resume-index protocol the OWN exit already uses (cd->sk_rx_prod = i).
No comments yet.