[PATCH RFC 074/111] staging: etnaviv: validate user supplied command stream

Lucas Stach l.stach at pengutronix.de
Thu Apr 2 08:30:16 PDT 2015


From: Russell King <rmk+kernel at arm.linux.org.uk>

Parse the submitted command buffer for allowable GPU commands, and
validate that all commands fit wholely within the submitted buffer.
We allow the following commands:

- load state
- any of the draw commands
- stall
- nop

which denies attempts to link, call, return, etc from the supplied
command stream.  This, at least, ensures that the GPU should reach the
end of the submitted command set and return to our buffer.

Future validation of the load state commands will ensure that we prevent
userspace providing physical addresses via the GPU command stream, with
a future possibility of also validating that the boundaries of the
drawing commands lie wholely within the requested buffers.  For the
time being, this functionality is disabled.

Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
 drivers/staging/etnaviv/Makefile             |   1 +
 drivers/staging/etnaviv/etnaviv_cmd_parser.c | 103 +++++++++++++++++++++++++++
 drivers/staging/etnaviv/etnaviv_drv.h        |   3 +
 drivers/staging/etnaviv/etnaviv_gem_submit.c |   7 ++
 4 files changed, 114 insertions(+)
 create mode 100644 drivers/staging/etnaviv/etnaviv_cmd_parser.c

diff --git a/drivers/staging/etnaviv/Makefile b/drivers/staging/etnaviv/Makefile
index ef0cffabdcce..2b71c31b6501 100644
--- a/drivers/staging/etnaviv/Makefile
+++ b/drivers/staging/etnaviv/Makefile
@@ -4,6 +4,7 @@ ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
 endif
 
 etnaviv-y := \
+	etnaviv_cmd_parser.o \
 	etnaviv_drv.o \
 	etnaviv_gem.o \
 	etnaviv_gem_prime.o \
diff --git a/drivers/staging/etnaviv/etnaviv_cmd_parser.c b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
new file mode 100644
index 000000000000..4cc6944e4a8f
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
@@ -0,0 +1,103 @@
+#include <linux/kernel.h>
+
+#include "etnaviv_gem.h"
+#include "etnaviv_gpu.h"
+
+#include "cmdstream.xml.h"
+
+#define EXTRACT(val, field) (((val) & field##__MASK) >> field##__SHIFT)
+
+static bool etnaviv_validate_load_state(struct etnaviv_gpu *gpu, u32 *buf,
+	unsigned int state, unsigned int num)
+{
+	return true;
+	if (0x1200 - state < num * 4)
+		return false;
+	if (0x1228 - state < num * 4)
+		return false;
+	if (0x1238 - state < num * 4)
+		return false;
+	if (0x1284 - state < num * 4)
+		return false;
+	if (0x128c - state < num * 4)
+		return false;
+	if (0x1304 - state < num * 4)
+		return false;
+	if (0x1310 - state < num * 4)
+		return false;
+	if (0x1318 - state < num * 4)
+		return false;
+	if (0x1280c - state < num * 4 + 0x0c)
+		return false;
+	if (0x128ac - state < num * 4 + 0x0c)
+		return false;
+	if (0x128cc - state < num * 4 + 0x0c)
+		return false;
+	if (0x1297c - state < num * 4 + 0x0c)
+		return false;
+	return true;
+}
+
+bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
+	struct etnaviv_gem_object *obj, unsigned int offset, unsigned int size)
+{
+	u32 *start = obj->vaddr + offset * 4;
+	u32 *buf = start;
+	u32 *end = buf + size;
+
+	while (buf < end) {
+		u32 cmd = *buf;
+		unsigned int len, n, off;
+		unsigned int op = cmd >> 27;
+
+		switch (op) {
+		case FE_OPCODE_LOAD_STATE:
+			n = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_COUNT);
+			len = 1 + n;
+			if (buf + len > end)
+				break;
+
+			off = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_OFFSET);
+			if (!etnaviv_validate_load_state(gpu, buf + 1,
+							 off * 4, n)) {
+				dev_warn(gpu->dev, "%s: load state covers restricted state (0x%x-0x%x) at offset %u\n",
+					 __func__, off * 4, (off + n) * 4, buf - start);
+				return false;
+			}
+			break;
+
+		case FE_OPCODE_DRAW_2D:
+			n = EXTRACT(cmd, VIV_FE_DRAW_2D_HEADER_COUNT);
+			len = 2 + n * 2;
+			break;
+
+		case FE_OPCODE_DRAW_PRIMITIVES:
+			len = 4;
+			break;
+
+		case FE_OPCODE_DRAW_INDEXED_PRIMITIVES:
+			len = 6;
+			break;
+
+		case FE_OPCODE_NOP:
+		case FE_OPCODE_STALL:
+			len = 2;
+			break;
+
+		default:
+			dev_err(gpu->dev, "%s: op %u not permitted at offset %u\n",
+				__func__, op, buf - start);
+			return false;
+		}
+
+		buf += ALIGN(len, 2);
+	}
+
+	if (buf > end) {
+		dev_err(gpu->dev, "%s: commands overflow end of buffer: %u > %u\n",
+			__func__, buf - start, size);
+		return false;
+	}
+
+	return true;
+}
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 47aa74b36235..18b929f9d268 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -39,6 +39,7 @@
 
 struct etnaviv_gpu;
 struct etnaviv_mmu;
+struct etnaviv_gem_object;
 struct etnaviv_gem_submit;
 
 struct etnaviv_file_private {
@@ -112,6 +113,8 @@ int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
 u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit);
+bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
+	struct etnaviv_gem_object *obj, unsigned int offset, unsigned int size);
 
 #ifdef CONFIG_DEBUG_FS
 void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index c32fb4424eea..7bd4912ab8ad 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -394,6 +394,13 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		submit->cmd[i].size = submit_cmd.size / 4;
 		submit->cmd[i].obj = etnaviv_obj;
 
+		if (!etnaviv_cmd_validate_one(gpu, etnaviv_obj,
+					      submit->cmd[i].offset,
+					      submit->cmd[i].size)) {
+			ret = -EINVAL;
+			goto out;
+		}
+
 		ret = submit_reloc(submit, etnaviv_obj,
 				   submit_cmd.submit_offset,
 				   submit_cmd.nr_relocs, submit_cmd.relocs);
-- 
2.1.4



More information about the dri-devel mailing list