From: Russell King <rmk+kernel@xxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxx> --- 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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel