]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
drm/vmwgfx: Fix up user_dmabuf refcounting
authorThomas Hellstrom <thellstrom@vmware.com>
Mon, 14 Sep 2015 08:13:11 +0000 (01:13 -0700)
committerLuis Henriques <luis.henriques@canonical.com>
Fri, 9 Oct 2015 13:41:04 +0000 (14:41 +0100)
commit 54c12bc374408faddbff75dbf1a6167c19af39c4 upstream.

If user space calls unreference on a user_dmabuf it will typically
kill the struct ttm_base_object member which is responsible for the
user-space visibility. However the dmabuf part may still be alive and
refcounted. In some situations, like for shared guest-backed surface
referencing/opening, the driver may try to reference the
struct ttm_base_object member again, causing an immediate kernel warning
and a later kernel NULL pointer dereference.

Fix this by always maintaining a reference on the struct
ttm_base_object member, in situations where it might subsequently be
referenced.

Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Brian Paul <brianp@vmware.com>
Reviewed-by: Sinclair Yeh <syeh@vmware.com>
[ luis: backported to 3.16: adjusted context ]
Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
drivers/gpu/drm/vmwgfx/vmwgfx_surface.c

index 6b252a887ae2dd1bd91f12b452c4fa54b0a77c40..a2cd01e0bbf754a8948d0140174405f4db9cfaab 100644 (file)
@@ -610,7 +610,8 @@ extern int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv,
                                 uint32_t size,
                                 bool shareable,
                                 uint32_t *handle,
-                                struct vmw_dma_buffer **p_dma_buf);
+                                struct vmw_dma_buffer **p_dma_buf,
+                                struct ttm_base_object **p_base);
 extern int vmw_user_dmabuf_reference(struct ttm_object_file *tfile,
                                     struct vmw_dma_buffer *dma_buf,
                                     uint32_t *handle);
@@ -624,7 +625,8 @@ extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo,
                                         uint32_t cur_validate_node);
 extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo);
 extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
-                                 uint32_t id, struct vmw_dma_buffer **out);
+                                 uint32_t id, struct vmw_dma_buffer **out,
+                                 struct ttm_base_object **base);
 extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
                                  struct drm_file *file_priv);
 extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
index 3a21633eba2f9fd284930d950d28397b8d612169..118e2206b79249d7435abdf6f320545cdd40a4a4 100644 (file)
@@ -873,7 +873,8 @@ static int vmw_translate_mob_ptr(struct vmw_private *dev_priv,
        struct vmw_relocation *reloc;
        int ret;
 
-       ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo);
+       ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo,
+                                    NULL);
        if (unlikely(ret != 0)) {
                DRM_ERROR("Could not find or use MOB buffer.\n");
                return -EINVAL;
@@ -934,7 +935,8 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
        struct vmw_relocation *reloc;
        int ret;
 
-       ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo);
+       ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo,
+                                    NULL);
        if (unlikely(ret != 0)) {
                DRM_ERROR("Could not find or use GMR region.\n");
                return -EINVAL;
index 87e39f68e9d07e0752ca80eddab912ccbf9ad5b6..e1898982b44afe508adccb8a60c565aecc6c5dd4 100644 (file)
@@ -484,7 +484,7 @@ int vmw_overlay_ioctl(struct drm_device *dev, void *data,
                goto out_unlock;
        }
 
-       ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf);
+       ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf, NULL);
        if (ret)
                goto out_unlock;
 
index 01d68f0a69dca74067b1dab583ab860711d6933a..78196a95b0add88abce93386cb7c75f94537814b 100644 (file)
@@ -355,7 +355,7 @@ int vmw_user_lookup_handle(struct vmw_private *dev_priv,
        }
 
        *out_surf = NULL;
-       ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf);
+       ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf, NULL);
        return ret;
 }
 
@@ -482,7 +482,8 @@ int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv,
                          uint32_t size,
                          bool shareable,
                          uint32_t *handle,
-                         struct vmw_dma_buffer **p_dma_buf)
+                         struct vmw_dma_buffer **p_dma_buf,
+                         struct ttm_base_object **p_base)
 {
        struct vmw_user_dma_buffer *user_bo;
        struct ttm_buffer_object *tmp;
@@ -516,6 +517,10 @@ int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv,
        }
 
        *p_dma_buf = &user_bo->dma;
+       if (p_base) {
+               *p_base = &user_bo->prime.base;
+               kref_get(&(*p_base)->refcount);
+       }
        *handle = user_bo->prime.base.hash.key;
 
 out_no_base_object:
@@ -627,6 +632,7 @@ int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data,
        struct vmw_dma_buffer *dma_buf;
        struct vmw_user_dma_buffer *user_bo;
        struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+       struct ttm_base_object *buffer_base;
        int ret;
 
        if ((arg->flags & (drm_vmw_synccpu_read | drm_vmw_synccpu_write)) == 0
@@ -639,7 +645,8 @@ int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data,
 
        switch (arg->op) {
        case drm_vmw_synccpu_grab:
-               ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &dma_buf);
+               ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &dma_buf,
+                                            &buffer_base);
                if (unlikely(ret != 0))
                        return ret;
 
@@ -647,6 +654,7 @@ int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data,
                                       dma);
                ret = vmw_user_dmabuf_synccpu_grab(user_bo, tfile, arg->flags);
                vmw_dmabuf_unreference(&dma_buf);
+               ttm_base_object_unref(&buffer_base);
                if (unlikely(ret != 0 && ret != -ERESTARTSYS &&
                             ret != -EBUSY)) {
                        DRM_ERROR("Failed synccpu grab on handle 0x%08x.\n",
@@ -688,7 +696,8 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
                return ret;
 
        ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile,
-                                   req->size, false, &handle, &dma_buf);
+                                   req->size, false, &handle, &dma_buf,
+                                   NULL);
        if (unlikely(ret != 0))
                goto out_no_dmabuf;
 
@@ -717,7 +726,8 @@ int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data,
 }
 
 int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
-                          uint32_t handle, struct vmw_dma_buffer **out)
+                          uint32_t handle, struct vmw_dma_buffer **out,
+                          struct ttm_base_object **p_base)
 {
        struct vmw_user_dma_buffer *vmw_user_bo;
        struct ttm_base_object *base;
@@ -739,7 +749,10 @@ int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
        vmw_user_bo = container_of(base, struct vmw_user_dma_buffer,
                                   prime.base);
        (void)ttm_bo_reference(&vmw_user_bo->dma.base);
-       ttm_base_object_unref(&base);
+       if (p_base)
+               *p_base = base;
+       else
+               ttm_base_object_unref(&base);
        *out = &vmw_user_bo->dma;
 
        return 0;
@@ -1000,7 +1013,7 @@ int vmw_dumb_create(struct drm_file *file_priv,
 
        ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile,
                                    args->size, false, &args->handle,
-                                   &dma_buf);
+                                   &dma_buf, NULL);
        if (unlikely(ret != 0))
                goto out_no_dmabuf;
 
@@ -1028,7 +1041,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv,
        struct vmw_dma_buffer *out_buf;
        int ret;
 
-       ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf);
+       ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf, NULL);
        if (ret != 0)
                return -EINVAL;
 
index c1559eeaffe9fe637eb2a1e31b1b070b6af29b76..4d7dedddbd97ff789acb046d5808578a930bbe63 100644 (file)
@@ -455,7 +455,7 @@ int vmw_shader_define_ioctl(struct drm_device *dev, void *data,
 
        if (arg->buffer_handle != SVGA3D_INVALID_ID) {
                ret = vmw_user_dmabuf_lookup(tfile, arg->buffer_handle,
-                                            &buffer);
+                                            &buffer, NULL);
                if (unlikely(ret != 0)) {
                        DRM_ERROR("Could not find buffer for shader "
                                  "creation.\n");
index 4ecdbf3e59da22ae8e6f887cddbaad1307bff299..9e190db6cc3702fca890560b02f42f78bfe6fd89 100644 (file)
@@ -43,6 +43,7 @@ struct vmw_user_surface {
        struct vmw_surface srf;
        uint32_t size;
        struct drm_master *master;
+       struct ttm_base_object *backup_base;
 };
 
 /**
@@ -652,6 +653,7 @@ static void vmw_user_surface_base_release(struct ttm_base_object **p_base)
        struct vmw_resource *res = &user_srf->srf.res;
 
        *p_base = NULL;
+       ttm_base_object_unref(&user_srf->backup_base);
        vmw_resource_unreference(&res);
 }
 
@@ -846,7 +848,8 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
                                            res->backup_size,
                                            true,
                                            &backup_handle,
-                                           &res->backup);
+                                           &res->backup,
+                                           &user_srf->backup_base);
                if (unlikely(ret != 0)) {
                        vmw_resource_unreference(&res);
                        goto out_unlock;
@@ -1309,7 +1312,8 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
 
        if (req->buffer_handle != SVGA3D_INVALID_ID) {
                ret = vmw_user_dmabuf_lookup(tfile, req->buffer_handle,
-                                            &res->backup);
+                                            &res->backup,
+                                            &user_srf->backup_base);
        } else if (req->drm_surface_flags &
                   drm_vmw_surface_flag_create_buffer)
                ret = vmw_user_dmabuf_alloc(dev_priv, tfile,
@@ -1317,7 +1321,8 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
                                            req->drm_surface_flags &
                                            drm_vmw_surface_flag_shareable,
                                            &backup_handle,
-                                           &res->backup);
+                                           &res->backup,
+                                           &user_srf->backup_base);
 
        if (unlikely(ret != 0)) {
                vmw_resource_unreference(&res);