DragonFlyBSD Kernel Audit
DF-1246 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/radeon/evergreen_cs.c b/sys/dev/drm/radeon/evergreen_cs.c
--- a/sys/dev/drm/radeon/evergreen_cs.c
+++ b/sys/dev/drm/radeon/evergreen_cs.c
@@ -2814,6 +2814,38 @@
 		count = GET_DMA_COUNT(header);
 		sub_cmd = GET_DMA_SUB_CMD(header);
 
+		/* DF-1246: ensure the full packet BODY fits in the IB before we
+		 * read/write ib[idx+N]. Without this, only the header word is
+		 * bounds-checked (evergreen_cs.c:2806) and a packet placed near
+		 * the end of the IB causes OOB read+write past the IB allocation.
+		 * 'need' is the maximum body word count each packet type touches. */
+		{
+			unsigned int need = 1;  /* header, already validated */
+			switch (cmd) {
+			case DMA_PACKET_WRITE:
+				need = (sub_cmd == 8) ? 2 : 3;
+				break;
+			case DMA_PACKET_COPY:
+				switch (sub_cmd) {
+				case 0x00: case 0x40: case 0x4d: need = 5; break;
+				case 0x41: need = 6; break;
+				case 0x44: need = 7; break;
+				case 0x08: case 0x49: case 0x4c: need = 9; break;
+				case 0x48: case 0x4b: case 0x4f: need = 10; break;
+				default: need = 1; break;  /* bad sub_cmd: EINVAL below */
+				}
+				break;
+			case DMA_PACKET_CONSTANT_FILL: need = 4; break;
+			case DMA_PACKET_NOP: need = 1; break;
+			default: need = 1; break;  /* unknown cmd: EINVAL below */
+			}
+			if ((unsigned int)idx + need > ib_chunk->length_dw) {
+				DRM_ERROR("DMA packet at %u needs %u dwords, IB has %u\n",
+					  idx, need, ib_chunk->length_dw);
+				return -EINVAL;
+			}
+		}
+
 		switch (cmd) {
 		case DMA_PACKET_WRITE:
 			r = r600_dma_cs_next_reloc(p, &dst_reloc);