Peer-freezable iocom reader: writer parked in fp_write with KDMSG_STATE_INTERLOCK set turns the 'dmrace' 1s-sleep loop into an indefinite cluster-link stall
| Field | Value |
|---|---|
| ID | DF-2710 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-667 Improper Locking |
| File | sys/kern/kern_dmsg.c |
| Lines | 1636-1637 (set), 1663-1667 (clear), 825-828 (wait loop) |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-08-30 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
kdmsg_state_msgtx() sets KDMSG_STATE_INTERLOCK at dequeue time; only cleanuptx β after fp_write() returns β clears it. A peer that stops reading parks the writer mid-transmission with INTERLOCK set; kdmsg_state_msgrx() then sleeps 1s per iteration on that state and the reader consumes nothing else, freezing both directions of the cluster link (kernel rcvbuf fills; all peer writes EAGAIN) until the peer resumes reading.
Threat model & preconditions
Any cluster peer freezes the kernel's cluster messaging indefinitely (withhold reads + one DELETE for an in-transmission msgid); on a clustered hammer2 mount, cluster-dependent VOPs block for the duration β recoverable system-wide DoS.
Proof of concept
VERIFIED on the non-INVARIANTS guest with a single DELETE (isolated from DF-2707): PAD probe 863/1024 then EAGAIN; 1024/1024 after drain; clean unmount (findings/poc/DF-2710/).
Recommended fix
Bound the dmrace wait (bail after a few iterations) or move the interlock from state-level to queue-level so writer back-pressure cannot dead-wait the reader (diff sketch in the pack).
References
- DF-2707 (uses this park primitive)
Timeline
- 2026-08-30 Discovered during pass-2 audit of kern_dmsg.c (GLM 5.3); reproduced same run.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2710 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| df2707_trigger.c | β | 13.5 KB | view raw | |
| manifest.json | β | 765 B | view raw | |
| verdict.json | β | 2.2 KB | view raw |
DF-2710 β peer-freezable iocom reader: parked writer turns the
"dmrace" INTERLOCK wait (kern_dmsg.c:827) into an indefinite cluster
link stall
What was verified (non-INVARIANTS kernel #1)
kdmsg_state_msgtx() sets KDMSG_STATE_INTERLOCK on the state of each
message it dequeues for transmission (kern_dmsg.c:1636-1637); the bit is
cleared only by the same thread's kdmsg_state_cleanuptx() after
fp_write() returns (:1663-1667). If the peer stops reading, the writer
parks inside fp_write() (unix-socket send buffer full) with INTERLOCK
set. kdmsg_state_msgrx() for any subsequent message resolving to that
state β e.g. the peer's DELETE for that msgid β hits the interlock and
sleeps 1 second per iteration in the again: loop (kern_dmsg.c:825-828)
while holding no other progress: the reader cannot consume anything
else, so the kernel-side receive buffer fills and all cluster traffic
in both directions stalls until the peer resumes reading.
PoC: df2707_trigger wedge (shared source) β parks the writer with
SO_SNDBUF=2048 + junk transactions, sends a single DELETE (no
duplicates β this is the benign control for DF-2707), then probes with
non-blocking one-way LNK_PAD bursts:
WEDGE_PROBE1_WRITTEN=863/1024 <== reader stalled (rcvbuf full) WEDGE_PROBE2_WRITTEN=1024/1024 (reader recovered after drain) UNMOUNT_OK
Reader demonstrably stops consuming (send-side EAGAIN after 863 frames β rcvbuf capacity) and recovers the moment the peer drains. A peer can hold the link frozen indefinitely; on a clustered hammer2 mount any cluster-dependent VOP then blocks (recoverable system-wide DoS while the peer withholds reads).
Build & run
scp -F dfbsd-qemu/config findings/poc/DF-2710/df2707_trigger.c dfbsd:/root/poc/df2707/ dfbsd-qemu/vm.sh run_root 'cd /root/poc/df2707 && cc -O -I/usr/src/sys -o t df2707_trigger.c && ./t wedge'
Fix direction
Bound the interlock wait (e.g. break out after a few iterations and
treat the message as EALREADY), or clear INTERLOCK before blocking in
fp_write() (queue-level interlock instead of state-level), so a
back-pressured writer cannot dead-wait the reader.
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
['run.wedge_probe.txt (WEDGE_PROBE1_WRITTEN=863/1024 stalled; WEDGE_PROBE2_WRITTEN=1024/1024 recovered; UNMOUNT_OK)', 'run.attack2_ssh.log + run.attack2_trigger.out (natural occurrence during DF-2707 run 2: trigger blocked writing churn after DRAIN_DONE)']
PoC changes
wedge mode of the DF-2707 trigger: parks the writer, sends a single DELETE for an in-transmission state, probes with non-blocking one-way LNK_PAD bursts before/after drain.
Verified recommended fix
Bound the dmrace wait (bail to EALREADY after a few 1s iterations) or move the interlock from state-level to queue-level so a back-pressured writer cannot dead-wait the reader.
Verdict
kdmsg_state_msgtx() sets KDMSG_STATE_INTERLOCK when dequeuing a message (kern_dmsg.c:1636-1637) and only kdmsg_state_cleanuptx() after fp_write() returns clears it (:1663-1667). A peer that stops reading parks the writer inside fp_write with INTERLOCK set; kdmsg_state_msgrx() then sleeps 1s per iteration on that state (:825-828, 'dmrace') and the reader consumes nothing else - the whole cluster link freezes in both directions until the peer resumes reading. Verified with a single DELETE (no duplicates, isolating it from DF-2707): PAD probe wrote 863/1024 then EAGAIN (kernel rcvbuf full, reader stalled); after drain the probe wrote 1024/1024 and the mount unmounted cleanly. Indefinite peer-held freeze of cluster messaging; cluster-dependent VOPs on a clustered hammer2 mount block for the duration.
No comments yet.