kdmsg_iocom_reconnect() kill-wait deadlocks the DIOCRECLUSTER caller unkillably when the old peer socket is idle-but-open (no fp_shutdown escape, unlike iocom_uninit)
| Field | Value |
|---|---|
| ID | DF-2877 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-667 / CWE-835 |
| File | sys/kern/subr_diskiocom.c |
| Lines | sink kern_dmsg.c:139-144 (escape pattern :283-294) |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-09-02 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
The reconnect kill-wait (kern_dmsg.c:139-144, reached via DIOCRECLUSTER β disk_iocom_reconnect) sleeps non-PCATCH waiting for the old reader thread, but that thread is parked inside fp_read() on the old peer socket and only re-checks KILLRX at loop top. A silent-but-open peer pins it forever; kdmsg_iocom_uninit escapes with a 10-second fp_shutdown (:283-294) β reconnect has no such escape, so the ioctl never returns and the process is unkillable (D state, wchan clstrkl, survives kill -9 β verified twice), and the disk's iocom stays wedged for all future attaches. Stock exposure because the boot-time hammer2 daemon DIOCRECLUSTERs every disk. Privileged local user. Fix: mirror iocom_uninit's fp_shutdown escape (validated: blocks 10.49s then completes).
Recommended fix
Validated fix.diff in findings/poc/DF-2877/.
Timeline
- 2026-09-02 Discovered during pass-2 audit of subr_diskiocom.c (GLM 5.3); unkillable wedge reproduced + fix validated same run.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2877 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| dfpeer.c | β | 15.4 KB | view raw | |
| run_f4.sh | β | 1.2 KB | view raw | |
| run.log | β | 1.0 KB | view raw | |
| fix.diff | β | 950 B | view raw | |
| build.sh | β | 51 B | view raw | |
| run.sh | β | 262 B | view raw | |
| env.txt | β | 373 B | view raw | |
| VERDICT.md | β | 1.9 KB | β raw | |
| run.fix.log | β | 445 B | view raw | |
| verdict.json | β | 2.6 KB | view raw | |
| README.md | β | 1.8 KB | β raw |
DF-2877 β kdmsg_iocom_reconnect() kill-wait deadlocks unkillably when the
old peer neither writes nor closes (no fp_shutdown escape)
What this pack contains
dfpeer.cβholdmode: attaches and parks holding the kernel's peer socket open with no traffic.run_f4.shβ attach #1 parks; attach #2 (DIOCRECLUSTER) wedges; the wedged process is in stateDwith wchanclstrkland surviveskill -9(verified live:STILL_ALIVE_UNKILLABLE).
Root cause
kdmsg_iocom_reconnect (sys/kern/kern_dmsg.c:139-144) sets KILLRX and
waits for the old reader/writer threads with a non-PCATCH lksleep loop.
The old reader is parked inside fp_read() on the old peer socket and
only observes KILLRX at the top of its loop, so it never exits until the
peer sends data or closes. Unlike kdmsg_iocom_uninit (:283-294), which
fp_shutdown()s the pipe after 10 seconds, reconnect has no escape:
the calling thread (any DIOCRECLUSTER β e.g. the second of two
hammer2 service daemon connects after a partial restart, or any
root-held fd handoff) blocks forever in an unkillable kernel sleep, and
the disk's iocom stays half-killed for all future attaches.
Reachability on a stock system: verified that the boot-time
hammer2 service daemon DIOCRECLUSTERs every disk with a pipe
(sbin/hammer2/cmd_service.c:898); any second connect to a connected disk
while the old pipe is idle wedges. Triggering requires the ability to
open the disk device (SYSCAP_RESTRICTEDROOT), hence severity Low.
Build & run (root)
cc -O -Wall -o /tmp/dfpeer dfpeer.c sh run_f4.sh # ends with STILL_ALIVE_UNKILLABLE # cleanup: kill the 'hold' instance -> EOF releases the wedge
On the fix kernel, attach #2 blocks ~10s, then the old fp is shutdown, the ioctl completes, and the process is killable throughout.
DF-2877 VERDICT
Status: reproduced (dos β unkillable kernel deadlock of the
DIOCRECLUSTER caller; disk iocom blocked for all future connects)
Guest: DragonFly 6.5-DEVELOPMENT X86_64_GENERIC (env.txt).
What was run
run_f4.sh:
1. attach #1: dfpeer hold /dev/vbd0 240 β attaches via DIOCRECLUSTER
and parks with the kernel's peer socket open, no traffic (the old
reader thread parks inside fp_read on this socket),
2. attach #2: dfpeer openwr /dev/vbd0 3 β the ioctl wedges inside
kdmsg_iocom_reconnect's kill-wait loop,
3. kill -9 the wedged process β still alive, state D,
wchan clstrkl (ps output captured in run log; live proof:
STILL_ALIVE_UNKILLABLE).
Root cause
sys/kern/kern_dmsg.c:139-144: the kill-wait sleeps with flags 0 (no
PCATCH β signals never interrupt; the ioctl never returns) and there is
no fp_shutdown escape, unlike kdmsg_iocom_uninit (:283-294) which
shitcans the pipe after 10 s. The old reader only checks KILLRX at the
top of its loop, so a silent-but-open peer socket pins it inside
fp_read forever.
Stock relevance (observed on this guest): the boot-time
hammer2 service daemon DIOCRECLUSTERs every disk with a pipe
(sbin/hammer2/cmd_service.c:850-901; all *-msgrd/*-msgwr threads
exist from boot). Any second connect to an already-connected disk whose
old pipe is idle wedges β including a reconnect attempt by a restarted
daemon while the old daemon's threads are still parked.
Triggering requires opening the disk device (SYSCAP_RESTRICTEDROOT in diskopen, sys/kern/subr_disk.c:78-80), hence severity Low; but the effect is an unkillable process plus a dead iocom for that disk.
Fix validation
fix.diff (10-second retry counter, then fp_shutdown of the old fp β mirroring kdmsg_iocom_uninit) applied, kernel rebuilt, PoC re-run: attach #2 blocks ~10 s, then completes normally; process is killable throughout (run.fix.log).
Fix verification
fixedFix kernel: second attach blocks 10.49s (retry+shutdown escape), then completes (BLK_OPEN reply error=0); no D-state process remains.
['run.fix.log']
Confirmed kernel references
- sys/kern/kern_dmsg.c:139
- sys/kern/kern_dmsg.c:141
- sys/kern/kern_dmsg.c:143
- sys/kern/kern_dmsg.c:287
- sbin/hammer2/cmd_service.c:898
Detail
Exploit chain
privileged local (needs disk open = SYSCAP_RESTRICTEDROOT): attach#1 holds peer socket open idle -> attach#2 wedges unkillably + disk iocom dead; release only by closing attach#1's fds
Evidence (decisive lines)
['run.log (pid in D2/clstrkl alive after kill -9: STILL_ALIVE_UNKILLABLE)', 'VERDICT.md', 'run.fix.log (10.49s then success)']
PoC changes
No seed; added dfpeer 'hold' mode (attach + park)
Verified recommended fix
Mirror kdmsg_iocom_uninit: 10s retry counter then fp_shutdown of the old msg_fp so the kill-wait always terminates
Verdict
kdmsg_iocom_reconnect (kern_dmsg.c:139-144) waits for the old reader/writer iocom threads with a non-PCATCH lksleep loop and, unlike kdmsg_iocom_uninit (:283-294), has NO fp_shutdown escape. The old reader parks inside fp_read() on the old peer socket and only re-checks KILLRX at loop top, so a silent-but-open peer pins it forever; the DIOCRECLUSTER caller then blocks in an UNKILLABLE kernel sleep (verified live: process in D state, wchan clstrkl, survives kill -9 - twice, once accidentally and once scripted) and the disk's iocom stays wedged for all future attaches. On a stock boot the 'hammer2 service' daemon DIOCRECLUSTERs every disk with a pipe (cmd_service.c:898), so any second connect to a connected-but-idle disk wedges. Fix kernel: attach #2 waits 10.49s, then completes normally.
No comments yet.