generic_netmap_rxsync: 32-bit wrap in the ring->reserved sanitization drives the userspace-release loop index out of [0,lim] β unkillable kernel livelock holding the kring lock
Summary
ring->reserved and ring->avail are mmap'd userspace-writable u_int32. The guard sums them in 32 bits, so reserved=0xFFFFFFFF with avail=1 wraps to 0 and passes; k then underflows (k = cur+lim+2 mod 2^32, always > lim), and the release loop 'for (n = 0; j != k; n++)' never terminates because j only cycles [0..lim]. The loop runs under the kring lock taken by the NIOCRXSYNC/poll paths: unkillable thread, wedged ring, pinned CPU. Slot writes stay in-bounds (livelock, not corruption). Local DoS behind /dev/netmap. The sibling host-ring code (netmap.c rxsync_from_host) has the same wrap but no loop. Fix: reject reserved > lim outright.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3428 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.5 KB | β raw | |
| VERDICT.md | β | 2.2 KB | β raw | |
| trigger_reserved_livelock.c | β | 2.0 KB | view raw | |
| build.sh | β | 114 B | view raw | |
| run.sh | β | 105 B | view raw | |
| netmap_build.log | β | 8.8 KB | view raw | |
| fix.diff | β | 1.0 KB | view raw | |
| verdict.json | β | 2.6 KB | view raw | |
| manifest.json | β | 836 B | view raw |
DF-3428 β generic_netmap_rxsync reserved wrap β infinite kernel loop
Build / Run
./build.shβ module build attempt (fails on this tree; sharednetmap_build.log)../run.shβ trigger sketch only; module absent.
The math (certain from source)
ring->reserved and ring->avail are mmap'd userspace-writable u_int32
(sys/net/netmap/netmap.h:221-223). With lim = nkr_num_slots - 1 = 1023:
resvd = 0xFFFFFFFF,avail = 1βresvd + ring->avail == 0(mod 2^32) β validation atnetmap_generic.c:694(>= lim + 1) passes.k = cur β€ lim < resvdβ else-branch:k = cur + 1024 + 1 - 0xFFFFFFFFβ‘cur + 1026(mod 2^32) βk β [1026, 2049], always > lim.- Release loop
for (n = 0; j != k; n++)(:702) βjonly ever takes values in[0, lim]β never terminates.
Locking: generic_netmap_rxsync runs under the kring busy/lock protocol
(netmap.c:1474 ioctl path / netmap.c:1768 poll path, nm_kr_get β
q_lock), so the looping thread is unkillable and every other user of the
ring blocks: unkillable local DoS. Slot writes stay in-bounds (livelock, not
corruption).
Precondition
/dev/netmap open (created 0660 root:wheel, netmap.c:2237) β same gate
as DF-0616/DF-3426. Severity Medium: local DoS behind a privileged device.
Verdict
build_failed β module unbuildable on this tree (identical blocker as
DF-3426/3427); arithmetic verified by hand and by contrast with the
non-looping host-ring variant (netmap.c:886-930).
DF-3428 VERDICT β rxsync reserved-overflow livelock
status: build_failed | reproduced: 0 | impact: none (not exercisable) | finding severity: Medium (local DoS, source-certain)
Reproduced?
No β netmap KLD unbuildable on this tree (see netmap_build.log). The
arithmetic, however, is fully determined:
resvd = ring->reserved // user-writable u_int32 (netmap.h:223, mmap'd)
resvd = 0xFFFFFFFF, ring->avail = 1
resvd + ring->avail // netmap_generic.c:694, 32-bit: == 0
0 >= lim + 1 (1024)? // NO -> check passes, resvd NOT reset
k = ring->cur = 0 // validated <= lim at :643
k < resvd -> k = k + lim + 1 - resvd // :698, mod 2^32
= 0 + 1024 + 1 - 4294967295
= 1026 (> lim = 1023)
for (n = 0; j != k; n++) // :702; j β [0..1023] forever, k = 1026
Every slot write in the loop is in-bounds (slot[j], j wraps at lim) β this
is a pure livelock of the calling thread while it holds the kring
(nm_kr_get/q_lock via netmap.c:1474 NIOCRXSYNC or :1768 poll),
wedging the ring and pinning the CPU at 100% in-kernel; the thread cannot be
killed (never sleeps).
Why the wrap check fails
:694 if (resvd + ring->avail >= lim + 1) performs the sum in 32-bit
u_int; two attacker-chosen large values wrap to a small one and bypass the
"invalid reserve" reset. The subtraction at :698 then underflows k out of
the [0, lim] range the loop can never reach.
Sibling check: netmap_rxsync_from_host (netmap.c:886-930) has the same
32-bit wrap but computes only deltas (n = k >= j ? k-j : k+lim-j) β no
loop, so only userspace-visible accounting is corrupted there. The infinite
loop is unique to generic_netmap_rxsync (netmap_generic.c:700-711).
Fix
Validate resvd against lim before use (it can never legitimately exceed
the slot count); see fix.diff. not_testable (module unbuildable).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablefix.diff adds a hard bound (resvd > lim -> reset); not validatable - module unbuildable.
fix.diff; netmap_build.log
Confirmed kernel references
Detail
Exploit chain
would-be: open /dev/netmap (0660 root:wheel) -> NIOCREGIF generic adapter -> mmap ring -> set cur=0, reserved=0xFFFFFFFF, avail=1 -> NIOCRXSYNC -> ioctl never returns, thread unkillable, ring wedged. Repeat per ring/CPU.
Evidence (decisive lines)
VERDICT.md (arithmetic trace); trigger_reserved_livelock.c; netmap_build.log; fix.diff
PoC changes
No seed; authored. Build attempt only (module unbuildable).
Verified recommended fix
Reject ring->reserved > lim outright before the release loop (resvd can never legitimately exceed the slot count); see fix.diff.
Verdict
generic_netmap_rxsync trusts the mmap'd userspace-writable ring->reserved: the sanitization at netmap_generic.c:694 sums resvd + ring->avail in 32 bits, so resvd=0xFFFFFFFF with avail=1 wraps to 0 and passes; the subsequent k = (k >= resvd) ? ... : k + lim + 1 - resvd (:698) underflows k into [lim+2, 2*lim+2], and the userspace-release loop for (n = 0; j != k; n++) (:702) can never satisfy j == k because j only cycles [0..lim] -> unbounded kernel loop with the kring lock held (NIOCRXSYNC/poll path, netmap.c:1474/1768): unkillable local DoS. Slot writes stay in-bounds (livelock, not corruption). Runtime verification impossible: netmap KLD unbuildable on this tree (netmap_build.log); arithmetic verified manually and contrasted with the non-looping host-ring variant (netmap.c:886-930).
No comments yet.