video: tegra: nvmap: fix nvmap create handle vulnerability
skadamati [Thu, 15 Dec 2016 11:23:22 +0000 (16:23 +0530)]
Handle the race condition between malicious fd close and
copy_to_user error, which can create use after free condition.
This is fixed by deferring the fd install, which eliminates
the race that leads to use after free condition.
Fixing Google Bug 32160775.

Bug 1835857
Bug 200260161
Bug 1849492
Bug 1825283
CVE-2016-8424 (A-31606947)

Change-Id: I337807e4360661beced8f9e1155c47b66607b8df
Reviewed-on: http://git-master/r/1248391
(cherry picked from commit c26f2a34c189bef2d99740a420b2ab4023d912c0)
Reviewed-on: http://git-master/r/1273324
Signed-off-by: Gagan Grover <ggrover@nvidia.com>
Reviewed-on: http://git-master/r/1285852
(cherry picked from commit b1513dff2b4bd35d1b400645642bce8dcf3c96c7)
Reviewed-on: http://git-master/r/1299501
(cherry picked from commit 3993b1f51cd24e93b460d24b2659f0c7a6c6cf8a)
Reviewed-on: http://git-master/r/1311422
GVS: Gerrit_Virtual_Submit
Reviewed-by: Vinayak Pane <vpane@nvidia.com>

drivers/video/tegra/nvmap/nvmap_dmabuf.c
drivers/video/tegra/nvmap/nvmap_ioctl.c

index cfff9fc..f5203b7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * dma_buf exporter for nvmap
  *
- * Copyright (c) 2012-2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2017, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -638,7 +638,6 @@ err_nomem:
 int __nvmap_dmabuf_fd(struct nvmap_client *client,
                      struct dma_buf *dmabuf, int flags)
 {
-       int fd;
        int start_fd = CONFIG_NVMAP_FD_START;
 
 #ifdef CONFIG_NVMAP_DEFER_FD_RECYCLE
@@ -654,14 +653,8 @@ int __nvmap_dmabuf_fd(struct nvmap_client *client,
         * __FD_SETSIZE limitation issue for select(),
         * pselect() syscalls.
         */
-       fd = __alloc_fd(current->files, start_fd,
-                       sysctl_nr_open, flags);
-       if (fd < 0)
-               return fd;
-
-       fd_install(fd, dmabuf->file);
-
-       return fd;
+       return __alloc_fd(current->files, start_fd,
+                         sysctl_nr_open, flags);
 }
 
 int nvmap_get_dmabuf_fd(struct nvmap_client *client, struct nvmap_handle *h)
@@ -673,12 +666,8 @@ int nvmap_get_dmabuf_fd(struct nvmap_client *client, struct nvmap_handle *h)
        if (IS_ERR(dmabuf))
                return PTR_ERR(dmabuf);
        fd = __nvmap_dmabuf_fd(client, dmabuf, O_CLOEXEC);
-       if (fd < 0)
-               goto err_out;
-       return fd;
-
-err_out:
-       dma_buf_put(dmabuf);
+       if (IS_ERR_VALUE(fd))
+               dma_buf_put(dmabuf);
        return fd;
 }
 
index ddf802b..1eabbe9 100644 (file)
@@ -3,7 +3,7 @@
  *
  * User-space interface to nvmap
  *
- * Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -208,6 +208,33 @@ out:
        return err;
 }
 
+static int nvmap_install_fd(struct nvmap_client *client,
+       struct nvmap_handle *handle, int fd, void __user *arg,
+       void *op, size_t op_size, bool free)
+{
+       int err = 0;
+
+       if (IS_ERR_VALUE(fd)) {
+               err = fd;
+               goto fd_fail;
+       }
+
+       if (copy_to_user(arg, op, op_size)) {
+               err = -EFAULT;
+               goto copy_fail;
+       }
+
+       fd_install(fd, handle->dmabuf->file);
+       return err;
+
+copy_fail:
+       put_unused_fd(fd);
+fd_fail:
+       if (free)
+               nvmap_free_handle(client, handle);
+       return err;
+}
+
 int nvmap_ioctl_getfd(struct file *filp, void __user *arg)
 {
        struct nvmap_handle *handle;
@@ -223,14 +250,9 @@ int nvmap_ioctl_getfd(struct file *filp, void __user *arg)
 
        op.fd = nvmap_get_dmabuf_fd(client, handle);
        nvmap_handle_put(handle);
-       if (op.fd < 0)
-               return op.fd;
 
-       if (copy_to_user(arg, &op, sizeof(op))) {
-               sys_close(op.fd);
-               return -EFAULT;
-       }
-       return 0;
+       return nvmap_install_fd(client, handle,
+                               op.fd, arg, &op, sizeof(op), 0);
 }
 
 int nvmap_ioctl_alloc(struct file *filp, void __user *arg)
@@ -339,7 +361,6 @@ int nvmap_ioctl_create(struct file *filp, unsigned int cmd, void __user *arg)
        struct nvmap_create_handle op;
        struct nvmap_handle_ref *ref = NULL;
        struct nvmap_client *client = filp->private_data;
-       int err = 0;
        int fd = 0;
 
        if (copy_from_user(&op, arg, sizeof(op)))
@@ -362,19 +383,10 @@ int nvmap_ioctl_create(struct file *filp, unsigned int cmd, void __user *arg)
                return PTR_ERR(ref);
 
        fd = nvmap_get_dmabuf_fd(client, ref->handle);
-       if (fd < 0)
-               err = fd;
-
        op.handle = fd;
 
-       if (copy_to_user(arg, &op, sizeof(op))) {
-               err = -EFAULT;
-               nvmap_free_handle(client, __nvmap_ref_to_handle(ref));
-       }
-
-       if (err && fd > 0)
-               sys_close(fd);
-       return err;
+       return nvmap_install_fd(client, ref->handle, fd,
+                               arg, &op, sizeof(op), 1);
 }
 
 int nvmap_ioctl_get_param(struct file *filp, void __user *arg, bool is32)
@@ -784,15 +796,11 @@ int nvmap_ioctl_create_from_ivc(struct file *filp, void __user *arg)
                return PTR_ERR(ref);
 
        fd = nvmap_get_dmabuf_fd(client, ref->handle);
-       if (fd < 0)
-               err = fd;
-
        op.handle = fd;
-
-       if (copy_to_user(arg, &op, sizeof(op))) {
-               nvmap_free_handle_fd(client, fd);
-               return -EFAULT;
-       }
+       err = nvmap_install_fd(client, ref->handle, fd,
+                               arg, &op, sizeof(op), 1);
+       if (err)
+               return err;
 
        ref->handle->peer = peer;