DragonFlyBSD Kernel Audit
DF-2875 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/subr_diskiocom.c	2026-09-02 18:33:18.862281095 +0000
+++ b/sys/kern/subr_diskiocom.c	2026-09-02 18:33:36.210063774 +0000
@@ -425,6 +425,8 @@
 		 * the buffer cache buffer for the case where we can just
 		 * use the message's data pointer.
 		 */
+		kdmsg_data_t *adata;
+
 		reterr = 0;
 		if (msg->aux_size >= msg->any.blk_write.bytes)
 			bp = getpbuf(NULL);
@@ -435,14 +437,25 @@
 		bp->b_cmd = BUF_CMD_WRITE;
 		bp->b_bcount = msg->any.blk_write.bytes;
 		bp->b_resid = bp->b_bcount;
+		bio->bio_caller_info2.ptr = NULL;
 		if (msg->aux_size >= msg->any.blk_write.bytes) {
+			/*
+			 * DF-2875: the detached aux buffer must be owned by
+			 * THIS bio, not by the shared single-slot
+			 * iost->data.  Pipelined writes in one transaction
+			 * used to overwrite the slot, freeing the most
+			 * recent buffer while its bio was still in flight
+			 * and leaking all earlier ones.
+			 */
 			bp->b_data = msg->aux_data;
-			kdmsg_detach_aux_data(msg, &iost->data);
+			adata = kmalloc(sizeof(*adata), M_DEVBUF,
+					M_WAITOK | M_ZERO);
+			kdmsg_detach_aux_data(msg, adata);
+			bio->bio_caller_info2.ptr = adata;
 		} else {
 			bcopy(msg->aux_data, bp->b_data, msg->aux_size);
 			bzero(bp->b_data + msg->aux_size,
 			      msg->any.blk_write.bytes - msg->aux_size);
-			bzero(&iost->data, sizeof(iost->data));
 		}
 		bio->bio_offset = msg->any.blk_write.offset;
 		bio->bio_caller_info1.ptr = msg->state;
@@ -595,6 +608,17 @@
 		cmd = DMSG_LNK_ERROR;
 		data = bp->b_data;
 		bytes = bp->b_bcount;
+		/*
+		 * DF-2876: on error or short reads the buffer content is
+		 * stale (whatever a previous pbuf user left behind); do
+		 * not disclose it.  Return only completed bytes.
+		 */
+		if (bp->b_flags & B_ERROR) {
+			data = NULL;
+			bytes = 0;
+		} else if (bp->b_resid) {
+			bytes = bp->b_bcount - bp->b_resid;
+		}
 		/* fall through */
 	case BUF_CMD_WRITE:
 		if (bp->b_flags & B_ERROR) {
@@ -603,7 +627,18 @@
 			error = 0;
 			resid = bp->b_resid;
 		}
-		kdmsg_free_aux_data(&iost->data);
+		/*
+		 * DF-2875: free exactly the aux data owned by THIS bio,
+		 * not the shared iost->data slot.
+		 */
+		{
+			kdmsg_data_t *adata = bio->bio_caller_info2.ptr;
+			if (adata) {
+				kdmsg_free_aux_data(adata);
+				kfree(adata, M_DEVBUF);
+				bio->bio_caller_info2.ptr = NULL;
+			}
+		}
 		break;
 	case BUF_CMD_FLUSH:
 	case BUF_CMD_FREEBLKS:
--- a/sys/kern/kern_dmsg.c	2026-09-02 18:33:18.862281095 +0000
+++ b/sys/kern/kern_dmsg.c	2026-09-02 18:33:36.234063474 +0000
@@ -136,11 +136,28 @@
 	/*
 	 * Destroy the current connection
 	 */
+	int retries;
+
+	/*
+	 * Destroy the current connection
+	 */
 	lockmgr(&iocom->msglk, LK_EXCLUSIVE);
 	atomic_set_int(&iocom->msg_ctl, KDMSG_CLUSTERCTL_KILLRX);
+	retries = 10;
 	while (iocom->msgrd_td || iocom->msgwr_td) {
 		wakeup(&iocom->msg_ctl);
 		lksleep(iocom, &iocom->msglk, 0, "clstrkl", hz);
+		/*
+		 * DF-2877: the old reader can be parked in fp_read() on a
+		 * non-cooperating peer socket forever.  Force EOF after
+		 * 10 seconds just like kdmsg_iocom_uninit() does, so this
+		 * ioctl eventually returns instead of deadlocking the
+		 * caller in an unkillable state.
+		 */
+		if (--retries == 0 && iocom->msg_fp) {
+			fp_shutdown(iocom->msg_fp, SHUT_RDWR);
+			/* retries allowed to go negative, keep looping */
+		}
 	}
 
 	/*
--- a/sys/kern/subr_disk.c	2026-09-02 18:33:18.862281095 +0000
+++ b/sys/kern/subr_disk.c	2026-09-02 18:33:36.234063474 +0000
@@ -1189,6 +1189,17 @@
 	}
 
 	if (ap->a_cmd == DIOCRECLUSTER && dev == dp->d_cdev) {
+		/*
+		 * DF-2874: wiring a kernel-side raw-disk dmsg server to an
+		 * arbitrary fd turns the kernel into a root-credential
+		 * proxy for the peer (dev_dopen with proc0.p_ucred and
+		 * unrestricted raw strategy); require write access to the
+		 * device node as defense-in-depth.
+		 */
+		if ((ap->a_fflag & FWRITE) == 0) {
+			error = EPERM;
+			return error;
+		}
 		error = disk_iocom_ioctl(dp, ap->a_cmd, ap->a_data);
 		return error;
 	}