⬢ DragonFlyBSD Kernel Audit
DF-4531 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/drm_sysctl.c
+++ b/sys/dev/drm/drm_sysctl.c
@@ -267,6 +267,16 @@
 
 	DRM_LOCK(dev);
 
+	/*
+	 * dev->filelist is mutated under dev->filelist_mutex
+	 * (drm_open_helper()/drm_close()), not under dev->struct_mutex:
+	 * DRM_LOCK() never serialized these two walks.  A concurrent
+	 * open()/close() between the count pass and the copy pass made
+	 * pass 2 write past the count-sized allocation (heap overflow)
+	 * or copy through freed drm_file nodes (use-after-free read).
+	 */
+	mutex_lock(&dev->filelist_mutex);
+
 	privcount = 0;
 	list_for_each_entry(priv, &dev->filelist, lhead)
 		privcount++;
@@ -274,12 +284,19 @@
 	tempprivs = kmalloc(sizeof(struct drm_file) * privcount, M_DRM,
 			    M_WAITOK | M_NULLOK);
 	if (tempprivs == NULL) {
+		mutex_unlock(&dev->filelist_mutex);
 		DRM_UNLOCK(dev);
 		return ENOMEM;
 	}
 	i = 0;
-	list_for_each_entry(priv, &dev->filelist, lhead)
+	list_for_each_entry(priv, &dev->filelist, lhead) {
+		if (i >= privcount)	/* never trust a stale count */
+			break;
 		tempprivs[i++] = *priv;
+	}
+	privcount = i;		/* exact number actually copied */
+
+	mutex_unlock(&dev->filelist_mutex);
 
 	DRM_UNLOCK(dev);