DF-4531 / fix.diff
--- 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); |