Takashi Iwai b93c2a
From: Mathias Krause <minipli@grsecurity.net>
Takashi Iwai b93c2a
Subject: [PATCH] drm/vmwgfx: Fix stale file descriptors on failed usercopy
Takashi Iwai b93c2a
Patch-mainline: v5.17-rc2
Takashi Iwai b93c2a
Git-commit: a0f90c8815706981c483a652a6aefca51a5e191c
Takashi Iwai b93c2a
References: CVE-2022-22942 bsc#1195065
Takashi Iwai b93c2a
Takashi Iwai b93c2a
A failing usercopy of the fence_rep object will lead to a stale entry in
Takashi Iwai b93c2a
the file descriptor table as put_unused_fd() won't release it. This
Takashi Iwai b93c2a
enables userland to refer to a dangling 'file' object through that still
Takashi Iwai b93c2a
valid file descriptor, leading to all kinds of use-after-free
Takashi Iwai b93c2a
exploitation scenarios.
Takashi Iwai b93c2a
Takashi Iwai b93c2a
Fix this by deferring the call to fd_install() until after the usercopy
Takashi Iwai b93c2a
has succeeded.
Takashi Iwai b93c2a
Takashi Iwai b93c2a
Cc: Zack Rusin <zackr@vmware.com>
Takashi Iwai b93c2a
Fixes: c906965dee22 ("drm/vmwgfx: Add export fence to file descriptor support")
Takashi Iwai b93c2a
[mks: backport to v5.16 and older]
Takashi Iwai b93c2a
Signed-off-by: Mathias Krause <minipli@grsecurity.net>
Takashi Iwai b93c2a
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai b93c2a
Takashi Iwai b93c2a
---
Takashi Iwai b93c2a
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.h     |    5 +---
Takashi Iwai b93c2a
 drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c |   33 ++++++++++++++++----------------
Takashi Iwai b93c2a
 drivers/gpu/drm/vmwgfx/vmwgfx_fence.c   |    2 -
Takashi Iwai b93c2a
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c     |    2 -
Takashi Iwai b93c2a
 4 files changed, 21 insertions(+), 21 deletions(-)
Takashi Iwai b93c2a
Takashi Iwai b93c2a
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
Takashi Iwai b93c2a
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
Takashi Iwai b93c2a
@@ -936,15 +936,14 @@ extern int vmw_execbuf_fence_commands(st
Takashi Iwai b93c2a
 				      struct vmw_private *dev_priv,
Takashi Iwai b93c2a
 				      struct vmw_fence_obj **p_fence,
Takashi Iwai b93c2a
 				      uint32_t *p_handle);
Takashi Iwai b93c2a
-extern void vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
Takashi Iwai b93c2a
+extern int vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
Takashi Iwai b93c2a
 					struct vmw_fpriv *vmw_fp,
Takashi Iwai b93c2a
 					int ret,
Takashi Iwai b93c2a
 					struct drm_vmw_fence_rep __user
Takashi Iwai b93c2a
 					*user_fence_rep,
Takashi Iwai b93c2a
 					struct vmw_fence_obj *fence,
Takashi Iwai b93c2a
 					uint32_t fence_handle,
Takashi Iwai b93c2a
-					int32_t out_fence_fd,
Takashi Iwai b93c2a
-					struct sync_file *sync_file);
Takashi Iwai b93c2a
+					int32_t out_fence_fd);
Takashi Iwai b93c2a
 bool vmw_cmd_describe(const void *buf, u32 *size, char const **cmd);
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
 /**
Takashi Iwai b93c2a
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
Takashi Iwai b93c2a
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
Takashi Iwai b93c2a
@@ -3414,17 +3414,17 @@ int vmw_execbuf_fence_commands(struct dr
Takashi Iwai b93c2a
  * Also if copying fails, user-space will be unable to signal the fence object
Takashi Iwai b93c2a
  * so we wait for it immediately, and then unreference the user-space reference.
Takashi Iwai b93c2a
  */
Takashi Iwai b93c2a
-void
Takashi Iwai b93c2a
+int
Takashi Iwai b93c2a
 vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
Takashi Iwai b93c2a
 			    struct vmw_fpriv *vmw_fp, int ret,
Takashi Iwai b93c2a
 			    struct drm_vmw_fence_rep __user *user_fence_rep,
Takashi Iwai b93c2a
 			    struct vmw_fence_obj *fence, uint32_t fence_handle,
Takashi Iwai b93c2a
-			    int32_t out_fence_fd, struct sync_file *sync_file)
Takashi Iwai b93c2a
+			    int32_t out_fence_fd)
Takashi Iwai b93c2a
 {
Takashi Iwai b93c2a
 	struct drm_vmw_fence_rep fence_rep;
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
 	if (user_fence_rep == NULL)
Takashi Iwai b93c2a
-		return;
Takashi Iwai b93c2a
+		return 0;
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
 	memset(&fence_rep, 0, sizeof(fence_rep));
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
@@ -3452,20 +3452,14 @@ vmw_execbuf_copy_fence_user(struct vmw_p
Takashi Iwai b93c2a
 	 * handle.
Takashi Iwai b93c2a
 	 */
Takashi Iwai b93c2a
 	if (unlikely(ret != 0) && (fence_rep.error == 0)) {
Takashi Iwai b93c2a
-		if (sync_file)
Takashi Iwai b93c2a
-			fput(sync_file->file);
Takashi Iwai b93c2a
-
Takashi Iwai b93c2a
-		if (fence_rep.fd != -1) {
Takashi Iwai b93c2a
-			put_unused_fd(fence_rep.fd);
Takashi Iwai b93c2a
-			fence_rep.fd = -1;
Takashi Iwai b93c2a
-		}
Takashi Iwai b93c2a
-
Takashi Iwai b93c2a
 		ttm_ref_object_base_unref(vmw_fp->tfile, fence_handle,
Takashi Iwai b93c2a
 					  TTM_REF_USAGE);
Takashi Iwai b93c2a
 		VMW_DEBUG_USER("Fence copy error. Syncing.\n");
Takashi Iwai b93c2a
 		(void) vmw_fence_obj_wait(fence, false, false,
Takashi Iwai b93c2a
 					  VMW_FENCE_WAIT_TIMEOUT);
Takashi Iwai b93c2a
 	}
Takashi Iwai b93c2a
+
Takashi Iwai b93c2a
+	return ret ? -EFAULT : 0;
Takashi Iwai b93c2a
 }
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
 /**
Takashi Iwai b93c2a
@@ -3807,16 +3801,23 @@ int vmw_execbuf_process(struct drm_file
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
 			(void) vmw_fence_obj_wait(fence, false, false,
Takashi Iwai b93c2a
 						  VMW_FENCE_WAIT_TIMEOUT);
Takashi Iwai b93c2a
+		}
Takashi Iwai b93c2a
+	}
Takashi Iwai b93c2a
+
Takashi Iwai b93c2a
+	ret = vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret,
Takashi Iwai b93c2a
+				    user_fence_rep, fence, handle, out_fence_fd);
Takashi Iwai b93c2a
+
Takashi Iwai b93c2a
+	if (sync_file) {
Takashi Iwai b93c2a
+		if (ret) {
Takashi Iwai b93c2a
+			/* usercopy of fence failed, put the file object */
Takashi Iwai b93c2a
+			fput(sync_file->file);
Takashi Iwai b93c2a
+			put_unused_fd(out_fence_fd);
Takashi Iwai b93c2a
 		} else {
Takashi Iwai b93c2a
 			/* Link the fence with the FD created earlier */
Takashi Iwai b93c2a
 			fd_install(out_fence_fd, sync_file->file);
Takashi Iwai b93c2a
 		}
Takashi Iwai b93c2a
 	}
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
-	vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret,
Takashi Iwai b93c2a
-				    user_fence_rep, fence, handle, out_fence_fd,
Takashi Iwai b93c2a
-				    sync_file);
Takashi Iwai b93c2a
-
Takashi Iwai b93c2a
 	/* Don't unreference when handing fence out */
Takashi Iwai b93c2a
 	if (unlikely(out_fence != NULL)) {
Takashi Iwai b93c2a
 		*out_fence = fence;
Takashi Iwai b93c2a
@@ -3834,7 +3835,7 @@ int vmw_execbuf_process(struct drm_file
Takashi Iwai b93c2a
 	 */
Takashi Iwai b93c2a
 	vmw_validation_unref_lists(&val_ctx);
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
-	return 0;
Takashi Iwai b93c2a
+	return ret;
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
 out_unlock_binding:
Takashi Iwai b93c2a
 	mutex_unlock(&dev_priv->binding_mutex);
Takashi Iwai b93c2a
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
Takashi Iwai b93c2a
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
Takashi Iwai b93c2a
@@ -1167,7 +1167,7 @@ int vmw_fence_event_ioctl(struct drm_dev
Takashi Iwai b93c2a
 	}
Takashi Iwai b93c2a
 
Takashi Iwai b93c2a
 	vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence,
Takashi Iwai b93c2a
-				    handle, -1, NULL);
Takashi Iwai b93c2a
+				    handle, -1);
Takashi Iwai b93c2a
 	vmw_fence_obj_unreference(&fence);
Takashi Iwai b93c2a
 	return 0;
Takashi Iwai b93c2a
 out_no_create:
Takashi Iwai b93c2a
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
Takashi Iwai b93c2a
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
Takashi Iwai b93c2a
@@ -2561,7 +2561,7 @@ void vmw_kms_helper_validation_finish(st
Takashi Iwai b93c2a
 	if (file_priv)
Takashi Iwai b93c2a
 		vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv),
Takashi Iwai b93c2a
 					    ret, user_fence_rep, fence,
Takashi Iwai b93c2a
-					    handle, -1, NULL);
Takashi Iwai b93c2a
+					    handle, -1);
Takashi Iwai b93c2a
 	if (out_fence)
Takashi Iwai b93c2a
 		*out_fence = fence;
Takashi Iwai b93c2a
 	else