[PATCH] drm/radeon: copy userspace cmd to local copy before processing it v3

j.glisse at gmail.com j.glisse at gmail.com
Fri Feb 8 15:13:50 PST 2013


From: Jerome Glisse <jglisse at redhat.com>

In some rare case were packet is big enough to go over page boundary
we might not have copied yet the userspace data into the local copy
resulting in kernel reading garbage data.

Without this patch kernel might submit unprocessed/unrelocated cmd
to the GPU which might lead to GPU lockup.

v2: Make sure we do copy all the page and don't forget some when
    the packet count dw is bigger than 1 page
v3: Rebase patch against Linus master

Signed-off-by: Jerome Glisse <jglisse at redhat.com>
---
 drivers/gpu/drm/radeon/evergreen_cs.c | 35 ++++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/radeon/r600_cs.c      | 19 ++++++++++++++++++-
 2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index 7a44566..51ad74a 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -1021,7 +1021,7 @@ static int evergreen_cs_packet_parse(struct radeon_cs_parser *p,
 			      unsigned idx)
 {
 	struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx];
-	uint32_t header;
+	uint32_t header, i, npages;
 
 	if (idx >= ib_chunk->length_dw) {
 		DRM_ERROR("Can not parse packet at %d after CS end %d !\n",
@@ -1052,6 +1052,11 @@ static int evergreen_cs_packet_parse(struct radeon_cs_parser *p,
 			  pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw);
 		return -EINVAL;
 	}
+	/* make sure we copied packet fully from userspace */
+	npages = ((idx + pkt->count + 1) >> 10) - (idx >> 10);
+	for (i = 1; i <= npages; i++) {
+		radeon_get_ib_value(p, (idx & 0xfffffc00) + i * 0x400);
+	}
 	return 0;
 }
 
@@ -2909,12 +2914,16 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 				return -EINVAL;
 			}
 			if (tiled) {
+				/* make sure we copied packet fully from userspace */
+				radeon_get_ib_value(p, idx + 6);
 				dst_offset = ib[idx+1];
 				dst_offset <<= 8;
 
 				ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
 				p->idx += count + 7;
 			} else {
+				/* make sure we copied packet fully from userspace */
+				radeon_get_ib_value(p, idx + 2);
 				dst_offset = ib[idx+1];
 				dst_offset |= ((u64)(ib[idx+2] & 0xff)) << 32;
 
@@ -2945,6 +2954,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 					switch (misc) {
 					case 0:
 						/* L2T, frame to fields */
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 9);
 						if (idx_value & (1 << 31)) {
 							DRM_ERROR("bad L2T, frame to fields DMA_PACKET_COPY\n");
 							return -EINVAL;
@@ -2983,6 +2994,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 						break;
 					case 1:
 						/* L2T, T2L partial */
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 11);
 						if (p->family < CHIP_CAYMAN) {
 							DRM_ERROR("L2T, T2L Partial is cayman only !\n");
 							return -EINVAL;
@@ -3005,6 +3018,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 						break;
 					case 3:
 						/* L2T, broadcast */
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 9);
 						if (idx_value & (1 << 31)) {
 							DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n");
 							return -EINVAL;
@@ -3043,6 +3058,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 						break;
 					case 4:
 						/* L2T, T2L */
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 8);
 						/* detile bit */
 						if (idx_value & (1 << 31)) {
 							/* tiled src, linear dst */
@@ -3079,6 +3096,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 						break;
 					case 5:
 						/* T2T partial */
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 12);
 						if (p->family < CHIP_CAYMAN) {
 							DRM_ERROR("L2T, T2L Partial is cayman only !\n");
 							return -EINVAL;
@@ -3089,6 +3108,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 						break;
 					case 7:
 						/* L2T, broadcast */
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 9);
 						if (idx_value & (1 << 31)) {
 							DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n");
 							return -EINVAL;
@@ -3132,6 +3153,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 				} else {
 					switch (misc) {
 					case 0:
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 8);
 						/* detile bit */
 						if (idx_value & (1 << 31)) {
 							/* tiled src, linear dst */
@@ -3176,6 +3199,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 					switch (misc) {
 					case 0:
 						/* L2L, byte */
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 4);
 						src_offset = ib[idx+2];
 						src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32;
 						dst_offset = ib[idx+1];
@@ -3198,6 +3223,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 						break;
 					case 1:
 						/* L2L, partial */
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 8);
 						if (p->family < CHIP_CAYMAN) {
 							DRM_ERROR("L2L Partial is cayman only !\n");
 							return -EINVAL;
@@ -3211,6 +3238,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 						break;
 					case 4:
 						/* L2L, dw, broadcast */
+						/* make sure we copied packet fully from userspace */
+						radeon_get_ib_value(p, idx + 6);
 						r = r600_dma_cs_next_reloc(p, &dst2_reloc);
 						if (r) {
 							DRM_ERROR("bad L2L, dw, broadcast DMA_PACKET_COPY\n");
@@ -3251,6 +3280,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 					}
 				} else {
 					/* L2L, dw */
+					/* make sure we copied packet fully from userspace */
+					radeon_get_ib_value(p, idx + 4);
 					src_offset = ib[idx+2];
 					src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32;
 					dst_offset = ib[idx+1];
@@ -3274,6 +3305,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
 			}
 			break;
 		case DMA_PACKET_CONSTANT_FILL:
+			/* make sure we copied packet fully from userspace */
+			radeon_get_ib_value(p, idx + 3);
 			r = r600_dma_cs_next_reloc(p, &dst_reloc);
 			if (r) {
 				DRM_ERROR("bad DMA_PACKET_CONSTANT_FILL\n");
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 69ec24a..d36f9e6 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -796,7 +796,7 @@ static int r600_cs_packet_parse(struct radeon_cs_parser *p,
 			unsigned idx)
 {
 	struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx];
-	uint32_t header;
+	uint32_t header, i, npages;
 
 	if (idx >= ib_chunk->length_dw) {
 		DRM_ERROR("Can not parse packet at %d after CS end %d !\n",
@@ -827,6 +827,11 @@ static int r600_cs_packet_parse(struct radeon_cs_parser *p,
 			  pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw);
 		return -EINVAL;
 	}
+	/* make sure we copied packet fully from userspace */
+	npages = ((idx + pkt->count + 1) >> 10) - (idx >> 10);
+	for (i = 1; i <= npages; i++) {
+		radeon_get_ib_value(p, (idx & 0xfffffc00) + i * 0x400);
+	}
 	return 0;
 }
 
@@ -2623,12 +2628,16 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
 				return -EINVAL;
 			}
 			if (tiled) {
+				/* make sure we copied packet fully from userspace */
+				radeon_get_ib_value(p, idx + 4);
 				dst_offset = ib[idx+1];
 				dst_offset <<= 8;
 
 				ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
 				p->idx += count + 5;
 			} else {
+				/* make sure we copied packet fully from userspace */
+				radeon_get_ib_value(p, idx + 2);
 				dst_offset = ib[idx+1];
 				dst_offset |= ((u64)(ib[idx+2] & 0xff)) << 32;
 
@@ -2654,6 +2663,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
 				return -EINVAL;
 			}
 			if (tiled) {
+				/* make sure we copied packet fully from userspace */
+				radeon_get_ib_value(p, idx + 6);
 				idx_value = radeon_get_ib_value(p, idx + 2);
 				/* detile bit */
 				if (idx_value & (1 << 31)) {
@@ -2680,6 +2691,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
 				p->idx += 7;
 			} else {
 				if (p->family >= CHIP_RV770) {
+					/* make sure we copied packet fully from userspace */
+					radeon_get_ib_value(p, idx + 4);
 					src_offset = ib[idx+2];
 					src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32;
 					dst_offset = ib[idx+1];
@@ -2691,6 +2704,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
 					ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
 					p->idx += 5;
 				} else {
+					/* make sure we copied packet fully from userspace */
+					radeon_get_ib_value(p, idx + 3);
 					src_offset = ib[idx+2];
 					src_offset |= ((u64)(ib[idx+3] & 0xff)) << 32;
 					dst_offset = ib[idx+1];
@@ -2715,6 +2730,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
 			}
 			break;
 		case DMA_PACKET_CONSTANT_FILL:
+			/* make sure we copied packet fully from userspace */
+			radeon_get_ib_value(p, idx + 3);
 			if (p->family < CHIP_RV770) {
 				DRM_ERROR("Constant Fill is 7xx only !\n");
 				return -EINVAL;
-- 
1.7.11.7



More information about the dri-devel mailing list