DF-3428 / trigger_reserved_livelock.c
/* DF-3428 — generic_netmap_rxsync: 32-bit wrap of ring->reserved * validation lets the release-loop target escape [0, lim], * producing an unbounded kernel loop holding the kring lock. * * Bug (sys/net/netmap/netmap_generic.c:641, 691-711): * * u_int k, resvd = ring->reserved; // :641 user-writable (mmap) * ... * j = kring->nr_hwcur; * k = ring->cur; // :692 validated <= lim at :643 * if (resvd > 0) { * if (resvd + ring->avail >= lim + 1) { // :694 32-bit WRAP defeats check * ring->reserved = resvd = 0; // e.g. resvd=0xFFFFFFFF, avail=1 -> sum=0 * } * k = (k >= resvd) ? k - resvd : k + lim + 1 - resvd; // :698 underflow * } * if (j != k) { * for (n = 0; j != k; n++) { // :702 j cycles [0..lim], * ...ring->slot[j]... // k in [lim+2, 2*lim+2] * if (j++ == lim) j = 0; // -> j NEVER equals k * } * * With lim = 1023 (default netmap_generic_ringsize = 1024 slots, * netmap.c:216) and resvd = 0xFFFFFFFF: the wrap check passes * (0xFFFFFFFF + 1 == 0 < 1024), so k = cur + 1024 + 1 - 0xFFFFFFFF * == cur + 2 (mod 2^32) + lim -> always > lim -> infinite loop. * The slot writes stay in-bounds (pure livelock), but the loop runs * under the kring q_lock taken by the NIOCRXSYNC/poll path * (netmap.c:1474/1768 via nm_kr_get/netmap_ioctl), so the thread is * unkillable and the ring is wedged: local kernel DoS. * * Note: the sibling host-ring code netmap.c:909-920 has the same wrap * but only does arithmetic (no loop) there; the infinite loop is * specific to the generic adapter rxsync. * * Trigger (would-be, needs loadable netmap): * fd = open("/dev/netmap"); NIOCREGIF(generic adapter); mmap; * ring->cur = 0; ring->reserved = 0xFFFFFFFF; ring->avail = 1; * ioctl(fd, NIOCRXSYNC); // never returns * * Platform fact: netmap KLD unbuildable on this tree (netmap_build.log). */ int main(void) { return 1; } |