DragonFlyBSD Kernel Audit
DF-0628 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netproto/smb/smb_conn.h b/sys/netproto/smb/smb_conn.h
--- a/sys/netproto/smb/smb_conn.h
+++ b/sys/netproto/smb/smb_conn.h
@@ -432,6 +432,7 @@
 };
 
 #define	SMBIOD_SHUTDOWN		0x0001
+#define	SMBIOD_EXITED		0x0002	/* DF-0628: iod kthread has exited */
 
 struct smbiod {
 	int			iod_id;

diff --git a/sys/netproto/smb/smb_iod.c b/sys/netproto/smb/smb_iod.c
--- a/sys/netproto/smb/smb_iod.c
+++ b/sys/netproto/smb/smb_iod.c
@@ -679,6 +679,11 @@
 			break;
 		tsleep(&iod->iod_flags, 0, "90idle", iod->iod_sleeptimo);
 	}
+	/* DF-0628: signal exit to smb_iod_destroy() which is waiting before
+	 * freeing iod. Without this barrier, the destroyer can kfree(iod)
+	 * while this thread is still executing between wakeup(evp) and here. */
+	iod->iod_flags |= SMBIOD_EXITED;
+	wakeup(iod);
 	kthread_exit_compat();
 }
 
@@ -717,6 +722,15 @@
 smb_iod_destroy(struct smbiod *iod)
 {
 	smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL);
+	/*
+	 * DF-0628: the SYNC handshake above guarantees the SHUTDOWN event was
+	 * *processed* (SMBIOD_SHUTDOWN set in iod_flags), not that the iod
+	 * kthread has exited. Wait for the kthread to actually exit before
+	 * reclaiming iod; otherwise the kthread re-reads iod->iod_flags
+	 * (smb_iod_thread:675,678) after the free, a use-after-free.
+	 */
+	while ((iod->iod_flags & SMBIOD_EXITED) == 0)
+		tsleep(iod, 0, "90iodx", hz);
 	smb_sl_destroy(&iod->iod_rqlock);
 	smb_sl_destroy(&iod->iod_evlock);
 	kfree(iod, M_SMBIOD);