⬢ DragonFlyBSD Kernel Audit
DF-4458 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/drm_gem.c
+++ b/sys/dev/drm/drm_gem.c
@@ -1085,6 +1100,7 @@
 {
 	struct drm_gem_object *gem_obj;
 	struct vm_object *vm_obj;
+	vm_ooffset_t maoff;
 
 	DRM_LOCK(dev);
 	gem_obj = drm_gem_object_from_offset(dev, *offset);
@@ -1096,9 +1112,25 @@
 
 	drm_gem_object_reference(gem_obj);
 	DRM_UNLOCK(dev);
+
+	/*
+	 * The caller (vm_mmap -> dev_dmmap_single) passes the mmap(2)
+	 * length through verbatim, and the user directly controls bits
+	 * 0..39 of the fake offset (the in-object byte offset).  The
+	 * upstream size clamp lives in drm_gem_mmap_obj() ("obj_size <
+	 * vma->vm_end - vma->vm_start"), which is #if 0'd on DragonFly,
+	 * so nothing else bounds the fault range against the object:
+	 * enforce [maoff, maoff + size) within the object here before
+	 * creating the device pager.
+	 */
+	maoff = DRM_GEM_MAPPING_MAPOFF(*offset);
+	if (size > gem_obj->size || maoff > gem_obj->size - size) {
+		drm_gem_object_unreference_unlocked(gem_obj);
+		return (EINVAL);
+	}
 	vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE,
 	    dev->driver->gem_vm_ops, size, nprot,
-	    DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred);
+	    maoff, curthread->td_ucred);
 	if (vm_obj == NULL) {
 		drm_gem_object_unreference_unlocked(gem_obj);
 		return (EINVAL);