DragonFlyBSD Kernel Audit
DF-2707 / fix.diff
← back to finding ↓ download raw
--- 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 &