DragonFlyBSD Kernel Audit
DF-1146 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/i915/i915_gem.c b/sys/dev/drm/i915/i915_gem.c
--- a/sys/dev/drm/i915/i915_gem.c
+++ b/sys/dev/drm/i915/i915_gem.c
@@ -2263,6 +2263,20 @@
 	page_offset = (unsigned long)offset >> PAGE_SHIFT;
 
 	/*
+	 * Reject faults beyond the GEM object's size.  drm_gem_mmap_single()
+	 * passes the user mmap length straight through to cdev_pager_allocate(),
+	 * so the backing vm_object can be larger than the GEM object; without
+	 * this check an oversized mmap would let us compute a GMADR address
+	 * past this object's GTT slot and read/write neighbouring slots.
+	 */
+	if (offset >= obj->base.size) {
+#ifdef __DragonFly__
+		up_read(&area->vm_mm->mmap_sem);
+#endif
+		return VM_PAGER_ERROR;
+	}
+
+	/*
 	 * vm_fault() has supplied us with a busied page placeholding
 	 * the operation.  This presents a lock order reversal issue
 	 * again i915_gem_release_mmap() for our device mutex.
@@ -2388,8 +2402,23 @@
 #endif
 
 	/* Finally, remap it using the new GTT offset */
-	m = vm_phys_fictitious_to_vm_page(ggtt->gmadr.start +
-			vma->node.start + offset);
+	{
+		uint64_t ggtt_addr = ggtt->gmadr.start +
+			vma->node.start + offset;
+
+		/*
+		 * For I915_GGTT_VIEW_PARTIAL VMAs the node maps only a chunk
+		 * of the object starting at view.partial.offset, so the
+		 * object-relative fault offset must be shifted to be
+		 * chunk-relative before adding the node address, otherwise we
+		 * return pages from neighbouring GTT slots.
+		 */
+		if (vma->ggtt_view.type == I915_GGTT_VIEW_PARTIAL)
+			ggtt_addr -= (uint64_t)vma->ggtt_view.partial.offset <<
+				PAGE_SHIFT;
+
+		m = vm_phys_fictitious_to_vm_page(ggtt_addr);
+	}
 
 	if (m == NULL) {
 		kprintf("i915: caught bug() (phys_fict_to_vm)\n");