drm/exynos: g2d: prevent integer overflow in
authorJoonyoung Shim <jy0922.shim@samsung.com>
Mon, 23 Jan 2017 09:13:54 +0000 (18:13 +0900)
committerInki Dae <inki.dae@samsung.com>
Mon, 30 Jan 2017 23:50:35 +0000 (08:50 +0900)
The size computations done in the ioctl function use an integer.
If userspace submits a request with req->cmd_nr or req->cmd_buf_nr
set to INT_MAX, the integer computations overflow later, leading
to potential (kernel) memory corruption.

Prevent this issue by enforcing a limit on the number of submitted
commands, so that we have enough headroom later for the size
computations.

Note that this change has no impact on the currently available
users in userspace, like e.g. libdrm/exynos.

While at it, also make a comment about the size computation more
detailed.

Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Tobias Jakobi <tjakobi@math.uni-bielefeld.de>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
drivers/gpu/drm/exynos/exynos_drm_g2d.c

index fbd13fabdf2daf93aeb79cb976c47e467fae6971..603d8425cca6cf015b1eca170f5d9d5540021535 100644 (file)
@@ -1193,6 +1193,17 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
        if (!node)
                return -ENOMEM;
 
+       /*
+        * To avoid an integer overflow for the later size computations, we
+        * enforce a maximum number of submitted commands here. This limit is
+        * sufficient for all conceivable usage cases of the G2D.
+        */
+       if (req->cmd_nr > G2D_CMDLIST_DATA_NUM ||
+           req->cmd_buf_nr > G2D_CMDLIST_DATA_NUM) {
+               dev_err(dev, "number of submitted G2D commands exceeds limit\n");
+               return -EINVAL;
+       }
+
        node->event = NULL;
 
        if (req->event_type != G2D_EVENT_NOT) {
@@ -1250,7 +1261,11 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
                cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF;
        }
 
-       /* Check size of cmdlist: last 2 is about G2D_BITBLT_START */
+       /*
+        * Check the size of cmdlist. The 2 that is added last comes from
+        * the implicit G2D_BITBLT_START that is appended once we have
+        * checked all the submitted commands.
+        */
        size = cmdlist->last + req->cmd_nr * 2 + req->cmd_buf_nr * 2 + 2;
        if (size > G2D_CMDLIST_DATA_NUM) {
                dev_err(dev, "cmdlist size is too big\n");