β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2877

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).

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2877 Β· 11 files
FileTypeDescriptionSize
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 β€” hold mode: 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 state D with wchan clstrkl and survives kill -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.

VERDICT.md
↓ download raw

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

fixed
baseline reproduced→ patch + rebuild →patched clean

Fix kernel: second attach blocks 10.49s (retry+shutdown escape), then completes (BLK_OPEN reply error=0); no D-state process remains.

['run.fix.log']
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Wed Sep 2 18:43:16 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (fix.diff applied, make nativekernel)

Confirmed kernel references

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.