DragonFlyBSD Kernel Audit
DF-1231 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/raid/aac/aac.c b/sys/dev/raid/aac/aac.c
--- a/sys/dev/raid/aac/aac.c
+++ b/sys/dev/raid/aac/aac.c
@@ -3525,25 +3525,39 @@
 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
 
 	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
+		/*
+		 * Serialize against aac_close_aif() which takes the same lock,
+		 * unlinks and kfrees the ctx. Without this lock the list walk
+		 * below races the unlink, and the tsleep race frees ctx while
+		 * we sleep -- aac_return_aif() would then deref freed memory.
+		 */
+		lockmgr(&sc->aac_aifq_lock, LK_EXCLUSIVE);
 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
 			if (agf.AdapterFibContext == ctx->unique)
 				break;
 		}
-		if (!ctx)
+		if (!ctx) {
+			lockmgr(&sc->aac_aifq_lock, LK_RELEASE);
 			return (EFAULT);
+		}
 
 		error = aac_return_aif(sc, ctx, agf.AifFib);
 		if (error == EAGAIN && agf.Wait) {
 			fwprintf(sc, HBA_FLAGS_DBG_AIF_B, "aac_getnext_aif(): waiting for AIF");
 			sc->aac_state |= AAC_STATE_AIF_SLEEPER;
 			while (error == EAGAIN) {
-				error = tsleep(sc->aac_aifq,
+				/* lksleep atomically releases aac_aifq_lock while
+				 * sleeping, so aac_close_aif() can run; it
+				 * reacquires the lock before returning, so ctx
+				 * cannot be freed underneath us. */
+				error = lksleep(sc->aac_aifq, &sc->aac_aifq_lock,
 					       PCATCH, "aacaif", 0);
 				if (error == 0)
 					error = aac_return_aif(sc, ctx, agf.AifFib);
 			}
 			sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
 		}
+		lockmgr(&sc->aac_aifq_lock, LK_RELEASE);
 	}
 	return(error);
 }