DragonFlyBSD Kernel Audit
DF-2207 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/linux_iomapping.c b/sys/dev/drm/linux_iomapping.c
index 0000000..1111111 100644
--- a/sys/dev/drm/linux_iomapping.c
+++ b/sys/dev/drm/linux_iomapping.c
@@ -64,6 +64,7 @@
 	int found = 0;
 	int indx;
 	vm_paddr_t paddr_end;
+	size_t byte_size;
 
 	SLIST_FOREACH_MUTABLE(imp, &iomap_list, im_iomaps, tmp_imp) {
 		if (imp->pmap_addr == ptr) {
@@ -77,7 +78,16 @@
 		return;
 	}
 
-	paddr_end = imp->paddr + (imp->npages * PAGE_SIZE) - 1;
+	/*
+	 * DF-2207: Compute byte_size once as size_t so the npages * PAGE_SIZE
+	 * multiplication cannot overflow signed int.  struct iomap.npages is
+	 * int (asm/io.h) and PAGE_SIZE is (1<<PAGE_SHIFT) which also has type
+	 * int (param.h), so the bare product is a signed multiplication that
+	 * overflows (UB) for mappings >= ~2 GiB and sign-extends into the
+	 * unsigned long arguments of pmap_unmapdev()/pmap_change_attr().
+	 */
+	byte_size = (size_t)imp->npages * PAGE_SIZE;
+	paddr_end = imp->paddr + byte_size - 1;
 	/* Is this address space range backed by regular memory ? */
 	for (indx = 0; phys_avail[indx].phys_end != 0; ++indx) {
 		vm_paddr_t range_start = phys_avail[indx].phys_beg;
@@ -93,7 +103,7 @@
 
 	}
 
-	pmap_unmapdev((vm_offset_t)imp->pmap_addr, imp->npages * PAGE_SIZE);
+	pmap_unmapdev((vm_offset_t)imp->pmap_addr, byte_size);
 
 	lockmgr(&iomap_lock, LK_EXCLUSIVE);
 	SLIST_REMOVE(&iomap_list, imp, iomap, im_iomaps);