⬢ DragonFlyBSD Kernel Audit
DF-3813 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/raid/mpr/mpr.c
+++ b/sys/dev/raid/mpr/mpr.c
@@ -573,6 +573,7 @@
 
 		/* Record that we reallocated everything */
 		sc->mpr_flags |= MPR_FLAGS_REALLOCATED;
+		sc->realloc_gen++;
 	}
 
 	/*
@@ -3560,6 +3561,7 @@
 	int error, rc;
 	struct timeval cur_time, start_time;
 	struct mpr_command *cm = *cmp;
+	u_int realloc_gen = sc->realloc_gen;
 
 	if (sc->mpr_flags & MPR_FLAGS_DIAGRESET) 
 		return  EBUSY;
@@ -3617,6 +3619,19 @@
 		}
 		error = ETIMEDOUT;
 	}
+	/*
+	 * A concurrent diag reset may have reallocated the command pool
+	 * while we slept: mprsas_complete_all_commands() woke us without
+	 * freeing our command, and mpr_iocfacts_free() then kfreed the
+	 * whole commands[] array under us.  Detect that via the generation
+	 * counter and tell the caller the command is gone, exactly like
+	 * the timeout path above.
+	 */
+	if (sc->realloc_gen != realloc_gen) {
+		*cmp = NULL;
+		if (error == 0)
+			error = ENXIO;
+	}
 	return (error);
 }
 
@@ -3630,6 +3645,7 @@
 	int error, rc;
 	struct timeval cur_time, start_time;
 	struct mpr_command *cm = *cmp;
+	u_int realloc_gen = sc->realloc_gen;
 
 	error = 0;
 
@@ -3639,6 +3655,12 @@
 
 	getmicrouptime(&start_time);
 	while ((cm->cm_flags & MPR_CM_FLAGS_COMPLETE) == 0) {
+		if (sc->realloc_gen != realloc_gen) {
+			/* commands[] was reallocated out from under us */
+			*cmp = NULL;
+			error = ENXIO;
+			break;
+		}
 		mpr_intr_locked(sc);
 
 		if (lockowned(&sc->mpr_lock))
--- a/sys/dev/raid/mpr/mprvar.h
+++ b/sys/dev/raid/mpr/mprvar.h
@@ -313,6 +313,7 @@
 	u_int				reqframesz;
 	u_int				replyframesz;
 	u_int				atomic_desc_capable;
+	u_int				realloc_gen;	/* ++ when commands[] is reallocated */
 	int				tm_cmds_active;
 	int				io_cmds_active;
 	int				io_cmds_highwater;