⬢ DragonFlyBSD Kernel Audit
DF-3993 / fix.diff
← back to finding ↓ download raw
fix for DF-3993: NULL asic->ring[] slot deref via user-selected UVD/VCE CS ring

Root cause (two layers):
 1) radeon_cs_get_ring() maps RADEON_CS_RING_UVD/VCE to ring indices for
    every asic family, although the per-family dispatch tables in
    radeon_asic.c only wire those rings on TN/SI/CI/KV (VCE) resp.
    RV610+ (UVD);
 2) radeon_cs_parser_init() dereferences asic->ring[p->ring]->cs_parse /
    ->ib_parse before checking that the ring slot itself is non-NULL.

Fix: gate the UVD/VCE cases by family exactly like the DMA case already
does, and defend the deref with an explicit slot-NULL check.

Not applied to sys/ (audit tree is read-only); authored against
sys/dev/drm/radeon/radeon_cs.c.

diff --git a/sys/dev/drm/radeon/radeon_cs.c b/sys/dev/drm/radeon/radeon_cs.c
index 1111111..2222222 100644
--- a/sys/dev/drm/radeon/radeon_cs.c
+++ b/sys/dev/drm/radeon/cs.c
@@ -233,8 +233,16 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority)
 		break;
 	case RADEON_CS_RING_UVD:
+		/* UVD ring exists on RV610+ only (radeon_asic.c tables) */
+		if (p->rdev->family < CHIP_RV610)
+			return -EINVAL;
 		p->ring = R600_RING_TYPE_UVD_INDEX;
 		break;
 	case RADEON_CS_RING_VCE:
+		/* VCE rings exist on TN/SI/CI/KV only (radeon_asic.c tables) */
+		if (p->rdev->family < CHIP_ARUBA)
+			return -EINVAL;
 		p->ring = TN_RING_TYPE_VCE1_INDEX;
 		break;
 	}
@@ -371,11 +379,17 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
 		if (radeon_cs_get_ring(p, ring, priority))
 			return -EINVAL;
 
+		/* the ring slot itself may be absent on this asic family */
+		if (p->rdev->asic->ring[p->ring] == NULL) {
+			DRM_ERROR("Ring %d not present on asic!\n", p->ring);
+			return -EINVAL;
+		}
+
 		/* we only support VM on some SI+ rings */
 		if ((p->cs_flags & RADEON_CS_USE_VM) == 0) {
 			if (p->rdev->asic->ring[p->ring]->cs_parse == NULL) {
 				DRM_ERROR("Ring %d requires VM!\n", p->ring);
 				return -EINVAL;
 			}
 		} else {
-			if (p->rdev->asic->ring[p->ring]->ib_parse == NULL) {
+			if (p->rdev->asic->ring[p->ring]->ib_parse == NULL) {
 				DRM_ERROR("VM not supported on ring %d!\n",
 					  p->ring);
 				return -EINVAL;