DragonFlyBSD Kernel Audit
DF-1251 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/radeon/radeon.h b/sys/dev/drm/radeon/radeon.h
--- a/sys/dev/drm/radeon/radeon.h
+++ b/sys/dev/drm/radeon/radeon.h
@@ -2331,6 +2331,7 @@
 	int				disp_priority;
 	/* BIOS */
 	uint8_t				*bios;
+	uint32_t			bios_length;	/* DF-1251: allocation size of *bios, in bytes */
 	bool				is_atom_bios;
 	uint16_t			bios_header_start;
 	struct radeon_bo		*stolen_vga_memory;
diff --git a/sys/dev/drm/radeon/radeon_bios.c b/sys/dev/drm/radeon/radeon_bios.c
--- a/sys/dev/drm/radeon/radeon_bios.c
+++ b/sys/dev/drm/radeon/radeon_bios.c
@@ -66,6 +66,7 @@
 		return false;
 	}
 	rdev->bios = kmalloc(size, M_DRM, M_WAITOK);
+	rdev->bios_length = size;
 	if (rdev->bios == NULL) {
 		iounmap(bios);
 		return false;
@@ -96,6 +97,7 @@
 		return false;
 	}
 	rdev->bios = kzalloc(size, GFP_KERNEL);
+	rdev->bios_length = size;
 	if (rdev->bios == NULL) {
 		vga_pci_unmap_bios(vga_dev, bios);
 		return false;
@@ -151,6 +153,7 @@
 	}
 
 	rdev->bios = kmemdup(bios, size, GFP_KERNEL);
+	rdev->bios_length = size;
 	pmap_unmapdev((vm_offset_t)bios, 256 * 1024);
 	pci_write_config(bsddev, PCIR_BIOS, saved_rom_bar, 4);
 
@@ -267,6 +270,7 @@
 		return false;
 
 	rdev->bios = kmalloc(size, M_DRM, M_WAITOK);
+	rdev->bios_length = size;
 	if (!rdev->bios) {
 		DRM_ERROR("Unable to allocate bios\n");
 		return false;
@@ -715,6 +719,7 @@
 	}
 
 	rdev->bios = kmalloc(vhdr->ImageLength, M_DRM, M_WAITOK);
+	rdev->bios_length = vhdr->ImageLength;
 	memcpy(rdev->bios, &vbios->VbiosContent, vhdr->ImageLength);
 	ret = !!rdev->bios;
 
diff --git a/sys/dev/drm/radeon/radeon_combios.c b/sys/dev/drm/radeon/radeon_combios.c
--- a/sys/dev/drm/radeon/radeon_combios.c
+++ b/sys/dev/drm/radeon/radeon_combios.c
@@ -3007,7 +3007,12 @@
 	struct radeon_device *rdev = dev->dev_private;
 
 	if (offset) {
-		while (RBIOS16(offset)) {
+		/* DF-1251: bound the walk against the BIOS allocation; a malicious
+		 * VBIOS table with no in-bounds terminator otherwise walks RBIOS*
+		 * past rdev->bios and feeds heap-sourced addr/val into WREG32. */
+		while (rdev->bios_length != 0 &&
+		       (uint32_t)offset + 10 <= rdev->bios_length &&
+		       RBIOS16(offset)) {
 			uint16_t cmd = ((RBIOS16(offset) & 0xe000) >> 13);
 			uint32_t addr = (RBIOS16(offset) & 0x1fff);
 			uint32_t val, and_mask, or_mask;
@@ -3086,7 +3091,10 @@
 	struct radeon_device *rdev = dev->dev_private;
 
 	if (offset) {
-		while (RBIOS8(offset)) {
+		/* DF-1251: bound the walk against the BIOS allocation. */
+		while (rdev->bios_length != 0 &&
+		       (uint32_t)offset + 5 <= rdev->bios_length &&
+		       RBIOS8(offset)) {
 			uint8_t cmd = ((RBIOS8(offset) & 0xc0) >> 6);
 			uint8_t addr = (RBIOS8(offset) & 0x3f);
 			uint32_t val, shift, tmp;
@@ -3178,7 +3186,10 @@
 	uint32_t tmp;
 
 	if (offset) {
-		uint8_t val = RBIOS8(offset);
+		uint8_t val =
+		    (rdev->bios_length != 0 &&
+		     (uint32_t)offset < rdev->bios_length) ? RBIOS8(offset) : 0xff;
+		/* DF-1251: bound the walk against the BIOS allocation. */
 		while (val != 0xff) {
 			offset++;