⬢ DragonFlyBSD Kernel Audit
DF-5462 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/cam/cam_extend.c
+++ b/sys/bus/cam/cam_extend.c
@@ -27,15 +27,43 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
+#include <sys/lock.h>
 
 #include "cam_extend.h"
 
+/*
+ * The pointer arrays are shared between periph attach/detach (writers:
+ * cam_extend_set growth kmalloc/bcopy/kfree of ea->ps, and
+ * cam_extend_release's NULL store) and every periph open/strategy/
+ * ioctl/close path (readers: cam_extend_get), with no CAM-level lock
+ * coupling the two sides -- e.g. scsi_pt.c's ptctor drops the sim lock
+ * around its cam_extend_set() call.  Growth therefore races lockless
+ * readers and can hand them a pointer read out of just-freed kernel
+ * memory.  Serialize all access with one leaf lock.
+ */
+static struct lock cam_extend_lk;
+
+static void
+cam_extend_lockinit(void *arg)
+{
+	lockinit(&cam_extend_lk, "camext", 0, 0);
+}
+SYSINIT(camextlk, SI_SUB_DRIVERS, SI_ORDER_FIRST, cam_extend_lockinit, NULL);
+
 struct extend_array
 {
 	int nelem;
 	void **ps;
 };
 
+static void *
+cam_extend_get_locked(struct extend_array *ea, int index)
+{
+	if (ea == NULL || index >= ea->nelem || index < 0)
+		return NULL;
+	return ea->ps[index];
+}
+
 /* EXTEND_CHUNK: Number of extend slots to allocate whenever we need a new
  * one.
  */
@@ -53,6 +81,9 @@
 void *
 cam_extend_set(struct extend_array *ea, int index, void *value)
 {
+	void *ret;
+
+	lockmgr(&cam_extend_lk, LK_EXCLUSIVE);
 	if (index >= ea->nelem) {
 		void **space;
 		space = kmalloc(sizeof(void *) * (index + EXTEND_CHUNK),
@@ -69,28 +100,33 @@
 	}
 	if (ea->ps[index]) {
 		kprintf("extend_set: entry %d already has storage.\n", index);
-		return 0;
-	}
-	else
+		ret = 0;
+	} else {
 		ea->ps[index] = value;
+		ret = value;
+	}
+	lockmgr(&cam_extend_lk, LK_RELEASE);
 
-	return value;
+	return (ret);
 }
 
 void *
 cam_extend_get(struct extend_array *ea,
 	       int index)
 {
-	if (ea == NULL || index >= ea->nelem || index < 0)
-		return NULL;
-	return ea->ps[index];
+	void *p;
+
+	lockmgr(&cam_extend_lk, LK_SHARED);
+	p = cam_extend_get_locked(ea, index);
+	lockmgr(&cam_extend_lk, LK_RELEASE);
+	return (p);
 }
 
 void
 cam_extend_release(struct extend_array *ea, int index)
 {
-	void *p = cam_extend_get(ea, index);
-	if (p) {
+	lockmgr(&cam_extend_lk, LK_EXCLUSIVE);
+	if (cam_extend_get_locked(ea, index) != NULL)
 		ea->ps[index] = NULL;
-	}
+	lockmgr(&cam_extend_lk, LK_RELEASE);
 }