DF-2707 / fix.diff
--- a/sys/kern/kern_dmsg.c +++ b/sys/kern/kern_dmsg.c @@ -1667,10 +1667,30 @@ state->flags &= ~(KDMSG_STATE_INTERLOCK | KDMSG_STATE_SIGNAL); kdmsg_state_hold(state); + /* + * NOTE: A duplicate transmitted DELETE must be discarded instead of + * re-running the close sequence. This can occur when a received + * duplicate DELETE is absorbed and the state callback queues another + * terminating reply while our first reply is still pending transmit + * (kdmsg_msg_reply()'s unlocked txcmd check races our txcmd update, + * which happens only here, after transmission). Re-running the + * sequence would RB_REMOVE() an already-removed node (stale-pointer + * writes into the live state tree) and drop an rbtree ref that no + * longer exists (refcount underflow -> premature state free while + * further queued messages still reference it). + */ if (msg->any.head.cmd & DMSGF_DELETE) { KKASSERT((state->txcmd & DMSGF_DELETE) == 0); state->txcmd |= DMSGF_DELETE; if (state->rxcmd & DMSGF_DELETE) { + /* + * Already fully closed (duplicate DELETE transmit). + */ + if ((state->flags & KDMSG_STATE_RBINSERTED) == 0) { + kdmsg_msg_free(msg); + kdmsg_state_drop(state); + return; + } KKASSERT(state->flags & KDMSG_STATE_RBINSERTED); if (state->txcmd & DMSGF_REPLY) { KKASSERT(msg->any.head.cmd & |