aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2013-12-04 04:52:58 -0500
committerDaniel Vetter <daniel.vetter@ffwll.ch>2013-12-12 04:44:57 -0500
commit9ae9ab522094406915c27dc729d3ecef7b44af72 (patch)
tree2a1885196b61218e8570f7121c44a35bc058f899
parent5c015db7d9d2eaecf5223eaad7a05c0e8e180288 (diff)
drm/i915: Prevent double unref following alloc failure during execbuffer
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 27173f1f95db5e74ceb35fe9a2f2f348ea11bac9 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>
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c28
1 files changed, 20 insertions, 8 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index b7e787fb4649..a3ba9a8cd687 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -93,7 +93,7 @@ eb_lookup_vmas(struct eb_vmas *eb,
93{ 93{
94 struct drm_i915_gem_object *obj; 94 struct drm_i915_gem_object *obj;
95 struct list_head objects; 95 struct list_head objects;
96 int i, ret = 0; 96 int i, ret;
97 97
98 INIT_LIST_HEAD(&objects); 98 INIT_LIST_HEAD(&objects);
99 spin_lock(&file->table_lock); 99 spin_lock(&file->table_lock);
@@ -106,7 +106,7 @@ eb_lookup_vmas(struct eb_vmas *eb,
106 DRM_DEBUG("Invalid object handle %d at index %d\n", 106 DRM_DEBUG("Invalid object handle %d at index %d\n",
107 exec[i].handle, i); 107 exec[i].handle, i);
108 ret = -ENOENT; 108 ret = -ENOENT;
109 goto out; 109 goto err;
110 } 110 }
111 111
112 if (!list_empty(&obj->obj_exec_link)) { 112 if (!list_empty(&obj->obj_exec_link)) {
@@ -114,7 +114,7 @@ eb_lookup_vmas(struct eb_vmas *eb,
114 DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n", 114 DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n",
115 obj, exec[i].handle, i); 115 obj, exec[i].handle, i);
116 ret = -EINVAL; 116 ret = -EINVAL;
117 goto out; 117 goto err;
118 } 118 }
119 119
120 drm_gem_object_reference(&obj->base); 120 drm_gem_object_reference(&obj->base);
@@ -123,9 +123,13 @@ eb_lookup_vmas(struct eb_vmas *eb,
123 spin_unlock(&file->table_lock); 123 spin_unlock(&file->table_lock);
124 124
125 i = 0; 125 i = 0;
126 list_for_each_entry(obj, &objects, obj_exec_link) { 126 while (!list_empty(&objects)) {
127 struct i915_vma *vma; 127 struct i915_vma *vma;
128 128
129 obj = list_first_entry(&objects,
130 struct drm_i915_gem_object,
131 obj_exec_link);
132
129 /* 133 /*
130 * NOTE: We can leak any vmas created here when something fails 134 * NOTE: We can leak any vmas created here when something fails
131 * later on. But that's no issue since vma_unbind can deal with 135 * later on. But that's no issue since vma_unbind can deal with
@@ -138,10 +142,12 @@ eb_lookup_vmas(struct eb_vmas *eb,
138 if (IS_ERR(vma)) { 142 if (IS_ERR(vma)) {
139 DRM_DEBUG("Failed to lookup VMA\n"); 143 DRM_DEBUG("Failed to lookup VMA\n");
140 ret = PTR_ERR(vma); 144 ret = PTR_ERR(vma);
141 goto out; 145 goto err;
142 } 146 }
143 147
148 /* Transfer ownership from the objects list to the vmas list. */
144 list_add_tail(&vma->exec_list, &eb->vmas); 149 list_add_tail(&vma->exec_list, &eb->vmas);
150 list_del_init(&obj->obj_exec_link);
145 151
146 vma->exec_entry = &exec[i]; 152 vma->exec_entry = &exec[i];
147 if (eb->and < 0) { 153 if (eb->and < 0) {
@@ -155,16 +161,22 @@ eb_lookup_vmas(struct eb_vmas *eb,
155 ++i; 161 ++i;
156 } 162 }
157 163
164 return 0;
165
158 166
159out: 167err:
160 while (!list_empty(&objects)) { 168 while (!list_empty(&objects)) {
161 obj = list_first_entry(&objects, 169 obj = list_first_entry(&objects,
162 struct drm_i915_gem_object, 170 struct drm_i915_gem_object,
163 obj_exec_link); 171 obj_exec_link);
164 list_del_init(&obj->obj_exec_link); 172 list_del_init(&obj->obj_exec_link);
165 if (ret) 173 drm_gem_object_unreference(&obj->base);
166 drm_gem_object_unreference(&obj->base);
167 } 174 }
175 /*
176 * Objects already transfered to the vmas list will be unreferenced by
177 * eb_destroy.
178 */
179
168 return ret; 180 return ret;
169} 181}
170 182