⬢ DragonFlyBSD Kernel Audit
DF-4413 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/radeon/radeon_vm.c b/sys/dev/drm/radeon/radeon_vm.c
--- a/sys/dev/drm/radeon/radeon_vm.c
+++ b/sys/dev/drm/radeon/radeon_vm.c
@@ -548,11 +548,30 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
 
 		/* drop mutex to allocate and clear page table */
 		mutex_unlock(&vm->mutex);
 
 		r = radeon_bo_create(rdev, RADEON_VM_PTE_COUNT * 8,
 				     RADEON_GPU_PAGE_SIZE, true,
 				     RADEON_GEM_DOMAIN_VRAM, 0,
 				     NULL, NULL, &pt);
 		if (r)
-			return r;
+			goto error_rollback;
 
 		r = radeon_vm_clear_bo(rdev, pt);
 		if (r) {
 			radeon_bo_unref(&pt);
-			return r;
+			goto error_rollback;
 		}
@@ -575,6 +594,21 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
 	mutex_unlock(&vm->mutex);
 	return 0;
 
+error_rollback:
+	/*
+	 * The mapping was committed to vm->va / vm->cleared BEFORE the
+	 * page-table allocation loop.  Leaving it there with NULL page
+	 * tables makes the next radeon_vm_bo_update() walk dereference
+	 * vm->page_tables[pt_idx].bo == NULL in radeon_vm_update_ptes()
+	 * and panics the kernel.  Take the mapping back out instead.
+	 * vm->mutex is NOT held here (dropped above), so re-acquire it.
+	 */
+	mutex_lock(&vm->mutex);
+	if (bo_va->it.start || bo_va->it.last) {
+		interval_tree_remove(&bo_va->it, &vm->va);
+		lockmgr(&vm->status_lock, LK_EXCLUSIVE);
+		bo_va->it.start = 0;
+		bo_va->it.last = 0;
+		list_del_init(&bo_va->vm_status);
+		lockmgr(&vm->status_lock, LK_RELEASE);
+	}
+	mutex_unlock(&vm->mutex);
+	return r;
+
 error_unreserve:
 	radeon_bo_unreserve(bo_va->bo);
 	return r;