video: tegra: nvmap: fix nvmap create handle vulnerability
Krishna Reddy [Fri, 4 Nov 2016 19:45:53 +0000 (12:45 -0700)]
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

Change-Id: I337807e4360661beced8f9e1155c47b66607b8df
Signed-off-by: Krishna Reddy <vdumpa@nvidia.com>
Reviewed-on: http://git-master/r/1248391
Reviewed-on: https://git-master.nvidia.com/r/1512958
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
Tested-by: Bibek Basu <bbasu@nvidia.com>

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

index e0ade75..d5b0c88 100644 (file)
@@ -599,7 +599,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
@@ -615,14 +614,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)
@@ -634,12 +627,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 a52dbab..3f7573a 100644 (file)
@@ -227,6 +227,33 @@ const struct file_operations nvmap_fd_fops = {
        .mmap           = nvmap_share_mmap,
 };
 
+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;
@@ -242,14 +269,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)
@@ -315,7 +337,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)))
@@ -338,19 +359,9 @@ 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_id(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_map_into_caller_ptr(struct file *filp, void __user *arg, bool is32)