Skip to content

Commit

Permalink
drm/i915: Prevent double unref following alloc failure during execbuffer
Browse files Browse the repository at this point in the history
Whilst looking up the objects required for an execbuffer, an untimely
allocation failure in creating the vma results in the object being
unreferenced from two lists. The ownership during the lookup is meant to
be moved from the list of objects being looked to the vma, and this
double unreference upon error results in a use-after-free.

Fixes regression from
commit 27173f1
Author: Ben Widawsky <ben@bwidawsk.net>
Date:   Wed Aug 14 11:38:36 2013 +0200

    drm/i915: Convert execbuf code to use vmas

Based on the fix by Ben Widawsky.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Ben Widawsky <ben@bwidawsk.net>
Cc: stable@vger.kernel.org
[danvet: Bikeshed the crucial comment above the ownership transfer as
discussed on irc.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
  • Loading branch information
Chris Wilson authored and Daniel Vetter committed Dec 12, 2013
1 parent 5c015db commit 9ae9ab5
Showing 1 changed file with 20 additions and 8 deletions.
28 changes: 20 additions & 8 deletions drivers/gpu/drm/i915/i915_gem_execbuffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ eb_lookup_vmas(struct eb_vmas *eb,
{
struct drm_i915_gem_object *obj;
struct list_head objects;
int i, ret = 0;
int i, ret;

INIT_LIST_HEAD(&objects);
spin_lock(&file->table_lock);
Expand All @@ -106,15 +106,15 @@ eb_lookup_vmas(struct eb_vmas *eb,
DRM_DEBUG("Invalid object handle %d at index %d\n",
exec[i].handle, i);
ret = -ENOENT;
goto out;
goto err;
}

if (!list_empty(&obj->obj_exec_link)) {
spin_unlock(&file->table_lock);
DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n",
obj, exec[i].handle, i);
ret = -EINVAL;
goto out;
goto err;
}

drm_gem_object_reference(&obj->base);
Expand All @@ -123,9 +123,13 @@ eb_lookup_vmas(struct eb_vmas *eb,
spin_unlock(&file->table_lock);

i = 0;
list_for_each_entry(obj, &objects, obj_exec_link) {
while (!list_empty(&objects)) {
struct i915_vma *vma;

obj = list_first_entry(&objects,
struct drm_i915_gem_object,
obj_exec_link);

/*
* NOTE: We can leak any vmas created here when something fails
* later on. But that's no issue since vma_unbind can deal with
Expand All @@ -138,10 +142,12 @@ eb_lookup_vmas(struct eb_vmas *eb,
if (IS_ERR(vma)) {
DRM_DEBUG("Failed to lookup VMA\n");
ret = PTR_ERR(vma);
goto out;
goto err;
}

/* Transfer ownership from the objects list to the vmas list. */
list_add_tail(&vma->exec_list, &eb->vmas);
list_del_init(&obj->obj_exec_link);

vma->exec_entry = &exec[i];
if (eb->and < 0) {
Expand All @@ -155,16 +161,22 @@ eb_lookup_vmas(struct eb_vmas *eb,
++i;
}

return 0;


out:
err:
while (!list_empty(&objects)) {
obj = list_first_entry(&objects,
struct drm_i915_gem_object,
obj_exec_link);
list_del_init(&obj->obj_exec_link);
if (ret)
drm_gem_object_unreference(&obj->base);
drm_gem_object_unreference(&obj->base);
}
/*
* Objects already transfered to the vmas list will be unreferenced by
* eb_destroy.
*/

return ret;
}

Expand Down

0 comments on commit 9ae9ab5

Please sign in to comment.