DF-3761 / trigger.c
/* * DF-3761 trigger - alc(4) alc_rxintr() ignores alc_process_limit * ============================================================== * * HARDWARE PRECONDITION: an Atheros AR81xx NIC receiving a sustained * line-rate packet flood. The audit guest has no such device (env.txt: * NO_ATHEROS_PCI, ifconfig -l = "vtnet0 lo0"), so this file documents * the trigger; there is nothing meaningful to execute here beyond the * presence probe in run.sh. * * DragonFly alc_rxintr() (sys/dev/netif/alc/if_alc.c:3222-3289): * * for (prog = 0; (ifp->if_flags & IFF_RUNNING) != 0;) { * rrd = &sc->alc_rdata.alc_rr_ring[rr_cons]; * status = le32toh(rrd->status); * if ((status & RRD_VALID) == 0) * break; * ... * alc_rxeof(sc, rrd); // allocates a fresh cluster * // per Rx buffer (alc_newbuf * // :3187 m_getcl + dmamap load) * } * * has NO work bound. The driver *does* expose a bound - the * process_limit knob (sysctl hw.alcN.process_limit, if_alc.c:1696-1713, * ALC_PROC_DEFAULT 64, if_alcvar.h:71-73) - but nothing in the driver * ever reads it in the loop: it is a dead knob. * * FreeBSD bounds the identical loop (sys/dev/alc/if_alc.c, * alc_rxintr(sc, count): "if (count-- <= 0) break;"), and DragonFly's * sibling driver msk(4) bounds its Rx walk too * (sys/dev/netif/msk/if_msk.c:3225 "if (rxprog > sc->msk_process_limit)"). * * Attack shape (remote, unauthenticated, default config): * flood alc0 at line rate (1.488 Mpps of 64-byte frames at 1 GbE); * per frame alc_rxintr performs an m_getcl cluster allocation + * bus_dmamap_load + RRS processing + if_input enqueue, which is * slower than the arrival rate, so the return ring is perpetually * non-empty and the loop never terminates. alc_intr disabled the * NIC interrupt at entry (if_alc.c:3082, INTR_DIS_INT) and only * re-enables it after the loop (:3110), and the whole loop runs with * the interface serializer held: * * - the NIC's interrupt thread pins one CPU at 100% forever; * - every other driver entry point that needs the serializer * (ifconfig, any ioctl, alc_tick) blocks indefinitely; * - callouts on that CPU starve (the interrupt thread never yields). * * Fix: bound the loop with the existing knob (fix.diff). */ int DF_3761_trigger_is_a_documented_remote_flood; /* nothing to run */ |