Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0008a0c7 authored by Mathias Krause's avatar Mathias Krause Committed by Greg Kroah-Hartman
Browse files

drm/vmwgfx: Fix stale file descriptors on failed usercopy



commit a0f90c8815706981c483a652a6aefca51a5e191c upstream.

A failing usercopy of the fence_rep object will lead to a stale entry in
the file descriptor table as put_unused_fd() won't release it. This
enables userland to refer to a dangling 'file' object through that still
valid file descriptor, leading to all kinds of use-after-free
exploitation scenarios.

Fix this by deferring the call to fd_install() until after the usercopy
has succeeded.

Fixes: c906965d ("drm/vmwgfx: Add export fence to file descriptor support")
Signed-off-by: default avatarMathias Krause <minipli@grsecurity.net>
Signed-off-by: default avatarZack Rusin <zackr@vmware.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6717900f
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -855,15 +855,14 @@ extern int vmw_execbuf_fence_commands(struct drm_file *file_priv,
				      struct vmw_private *dev_priv,
				      struct vmw_fence_obj **p_fence,
				      uint32_t *p_handle);
extern void vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
extern int vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
					struct vmw_fpriv *vmw_fp,
					int ret,
					struct drm_vmw_fence_rep __user
					*user_fence_rep,
					struct vmw_fence_obj *fence,
					uint32_t fence_handle,
					int32_t out_fence_fd,
					struct sync_file *sync_file);
					int32_t out_fence_fd);
extern int vmw_validate_single_buffer(struct vmw_private *dev_priv,
				      struct ttm_buffer_object *bo,
				      bool interruptible,
+17 −17
Original line number Diff line number Diff line
@@ -3873,20 +3873,19 @@ int vmw_execbuf_fence_commands(struct drm_file *file_priv,
 * object so we wait for it immediately, and then unreference the
 * user-space reference.
 */
void
int
vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
			    struct vmw_fpriv *vmw_fp,
			    int ret,
			    struct drm_vmw_fence_rep __user *user_fence_rep,
			    struct vmw_fence_obj *fence,
			    uint32_t fence_handle,
			    int32_t out_fence_fd,
			    struct sync_file *sync_file)
			    int32_t out_fence_fd)
{
	struct drm_vmw_fence_rep fence_rep;

	if (user_fence_rep == NULL)
		return;
		return 0;

	memset(&fence_rep, 0, sizeof(fence_rep));

@@ -3914,20 +3913,14 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
	 * and unreference the handle.
	 */
	if (unlikely(ret != 0) && (fence_rep.error == 0)) {
		if (sync_file)
			fput(sync_file->file);

		if (fence_rep.fd != -1) {
			put_unused_fd(fence_rep.fd);
			fence_rep.fd = -1;
		}

		ttm_ref_object_base_unref(vmw_fp->tfile,
					  fence_handle, TTM_REF_USAGE);
		DRM_ERROR("Fence copy error. Syncing.\n");
		(void) vmw_fence_obj_wait(fence, false, false,
					  VMW_FENCE_WAIT_TIMEOUT);
	}

	return ret ? -EFAULT : 0;
}

/**
@@ -4287,16 +4280,23 @@ int vmw_execbuf_process(struct drm_file *file_priv,

			(void) vmw_fence_obj_wait(fence, false, false,
						  VMW_FENCE_WAIT_TIMEOUT);
		}
	}

	ret = vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret,
				    user_fence_rep, fence, handle, out_fence_fd);

	if (sync_file) {
		if (ret) {
			/* usercopy of fence failed, put the file object */
			fput(sync_file->file);
			put_unused_fd(out_fence_fd);
		} else {
			/* Link the fence with the FD created earlier */
			fd_install(out_fence_fd, sync_file->file);
		}
	}

	vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret,
				    user_fence_rep, fence, handle,
				    out_fence_fd, sync_file);

	/* Don't unreference when handing fence out */
	if (unlikely(out_fence != NULL)) {
		*out_fence = fence;
@@ -4315,7 +4315,7 @@ int vmw_execbuf_process(struct drm_file *file_priv,
	 */
	vmw_resource_list_unreference(sw_context, &resource_list);

	return 0;
	return ret;

out_unlock_binding:
	mutex_unlock(&dev_priv->binding_mutex);
+1 −1
Original line number Diff line number Diff line
@@ -1169,7 +1169,7 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
	}

	vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence,
				    handle, -1, NULL);
				    handle, -1);
	vmw_fence_obj_unreference(&fence);
	return 0;
out_no_create:
+1 −1
Original line number Diff line number Diff line
@@ -2662,7 +2662,7 @@ void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv,
	if (file_priv)
		vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv),
					    ret, user_fence_rep, fence,
					    handle, -1, NULL);
					    handle, -1);
	if (out_fence)
		*out_fence = fence;
	else