--- sys/kern/kern_dmsg.c 2026-06-29 12:51:19.000000000 +0000 +++ /tmp/opencode/v26/fixtree/sys/kern/kern_dmsg.c 2026-10-24 16:53:55.813821054 +0000 @@ -92,8 +92,7 @@ #define kdmsg_state_hold(state) _kdmsg_state_hold(state) #define kdmsg_state_drop(state) _kdmsg_state_drop(state) #endif -static void _kdmsg_state_hold(kdmsg_state_t *state KDMSG_DEBUG_ARGS); -static void _kdmsg_state_drop(kdmsg_state_t *state KDMSG_DEBUG_ARGS); +/* DF-2709: prototypes now in (exported for hammer2) */ static void kdmsg_iocom_thread_rd(void *arg); static void kdmsg_iocom_thread_wr(void *arg); @@ -227,10 +226,15 @@ (msg->any.head.cmd & DMSGF_DELETE)) { /* * iocom->conn_state has a state ref, drop it when clearing. + * + * DF-2709: conn_state is shared with hammer2_volconf_update() + * and other readers; interlock the drop with msglk. */ + lockmgr(&iocom->msglk, LK_EXCLUSIVE); if (iocom->conn_state) kdmsg_state_drop(iocom->conn_state); iocom->conn_state = NULL; + lockmgr(&iocom->msglk, LK_RELEASE); kdmsg_msg_reply(msg, 0); } @@ -901,6 +905,16 @@ msg->state = state; /* inherits freerd ref */ state->parent = pstate; KKASSERT(state->iocom == iocom); + /* + * DF-2711: cap the number of transactions a peer may + * have open; without a quota a peer can pin unbounded + * kernel memory (~160 bytes per state). + */ + if (iocom->peer_states >= KDMSG_MAX_PEER_STATES) { + error = DMSG_ERR_LOSTLINK; + break; + } + ++iocom->peer_states; state->flags |= KDMSG_STATE_RBINSERTED | KDMSG_STATE_SUBINSERTED | KDMSG_STATE_OPPOSITE; @@ -1727,7 +1741,6 @@ kdmsg_state_drop(state); } -static void _kdmsg_state_hold(kdmsg_state_t *state KDMSG_DEBUG_ARGS) { @@ -1737,7 +1750,6 @@ #endif } -static void _kdmsg_state_drop(kdmsg_state_t *state KDMSG_DEBUG_ARGS) { @@ -1759,8 +1771,11 @@ KKASSERT((state->flags & KDMSG_STATE_SUBINSERTED) == 0); KKASSERT(TAILQ_EMPTY(&state->subq)); - if (state != &state->iocom->state0) + if (state != &state->iocom->state0) { + if (state->flags & KDMSG_STATE_OPPOSITE) /* DF-2711 */ + --state->iocom->peer_states; kfree(state, iocom->mmsg); + } } kdmsg_msg_t * --- sys/vfs/hammer2/hammer2_iocom.c 2026-06-29 12:51:19.000000000 +0000 +++ /tmp/opencode/v26/fixtree/sys/vfs/hammer2/hammer2_iocom.c 2026-10-24 16:46:16.983664688 +0000 @@ -371,17 +371,26 @@ hammer2_volconf_update(hammer2_dev_t *hmp, int index) { kdmsg_msg_t *msg; + kdmsg_state_t *state; - /* XXX interlock against connection state termination */ - kprintf("volconf update %p\n", hmp->iocom.conn_state); - if (hmp->iocom.conn_state) { - kprintf("TRANSMIT VOLCONF VIA OPEN CONN TRANSACTION\n"); - msg = kdmsg_msg_alloc(hmp->iocom.conn_state, + /* + * DF-2709: interlock against connection state termination. + * Take a state reference under msglk so the peer-driven + * kdmsg_lnk_conn_reply drop cannot free it out from under us. + */ + lockmgr(&hmp->iocom.msglk, LK_EXCLUSIVE); + state = hmp->iocom.conn_state; + if (state) + _kdmsg_state_hold(state); + lockmgr(&hmp->iocom.msglk, LK_RELEASE); + if (state) { + msg = kdmsg_msg_alloc(state, DMSG_LNK_HAMMER2_VOLCONF, NULL, NULL); H2_LNK_VOLCONF(msg)->copy = hmp->voldata.copyinfo[index]; H2_LNK_VOLCONF(msg)->mediaid = hmp->voldata.fsid; H2_LNK_VOLCONF(msg)->index = index; kdmsg_msg_write(msg); + _kdmsg_state_drop(state); } }