From 481eb890d4c989e61a998dca11797a3035f1b1de Mon Sep 17 00:00:00 2001 From: Krishna Reddy Date: Fri, 4 Nov 2016 12:45:53 -0700 Subject: video: tegra: nvmap: fix nvmap create handle vulnerability 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 Reviewed-on: http://git-master/r/1248391 Reviewed-on: https://git-master.nvidia.com/r/1512958 GVS: Gerrit_Virtual_Submit Reviewed-by: Bibek Basu Tested-by: Bibek Basu --- drivers/video/tegra/nvmap/nvmap_dmabuf.c | 19 +++--------- drivers/video/tegra/nvmap/nvmap_ioctl.c | 51 +++++++++++++++++++------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/video/tegra/nvmap/nvmap_dmabuf.c b/drivers/video/tegra/nvmap/nvmap_dmabuf.c index e0ade759a340..d5b0c8805766 100644 --- a/drivers/video/tegra/nvmap/nvmap_dmabuf.c +++ b/drivers/video/tegra/nvmap/nvmap_dmabuf.c @@ -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; } diff --git a/drivers/video/tegra/nvmap/nvmap_ioctl.c b/drivers/video/tegra/nvmap/nvmap_ioctl.c index a52dbab922b4..3f7573a9ffcc 100644 --- a/drivers/video/tegra/nvmap/nvmap_ioctl.c +++ b/drivers/video/tegra/nvmap/nvmap_ioctl.c @@ -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) -- cgit v1.2.3