[PATCH] drm/gem: Expose the buffer object handle to userspace last
Tvrtko Ursulin
tvrtko.ursulin at linux.intel.com
Mon Feb 20 10:23:27 UTC 2023
On 20/02/2023 10:01, Christian König wrote:
> Am 20.02.23 um 10:55 schrieb Tvrtko Ursulin:
>>
>> Hi,
>>
>> On 14/02/2023 13:59, Christian König wrote:
>>> Am 14.02.23 um 13:50 schrieb Tvrtko Ursulin:
>>>> From: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
>>>>
>>>> Currently drm_gem_handle_create_tail exposes the handle to userspace
>>>> before the buffer object constructions is complete. This allowing
>>>> of working against a partially constructed object, which may also be in
>>>> the process of having its creation fail, can have a range of negative
>>>> outcomes.
>>>>
>>>> A lot of those will depend on what the individual drivers are doing in
>>>> their obj->funcs->open() callbacks, and also with a common failure mode
>>>> being -ENOMEM from drm_vma_node_allow.
>>>>
>>>> We can make sure none of this can happen by allocating a handle last,
>>>> although with a downside that more of the function now runs under the
>>>> dev->object_name_lock.
>>>>
>>>> Looking into the individual drivers open() hooks, we have
>>>> amdgpu_gem_object_open which seems like it could have a potential
>>>> security
>>>> issue without this change.
>>>>
>>>> A couple drivers like qxl_gem_object_open and vmw_gem_object_open
>>>> implement no-op hooks so no impact for them.
>>>>
>>>> A bunch of other require a deeper look by individual owners to asses
>>>> for
>>>> impact. Those are lima_gem_object_open, nouveau_gem_object_open,
>>>> panfrost_gem_open, radeon_gem_object_open and
>>>> virtio_gpu_gem_object_open.
>>>>
>>>> Putting aside the risk assesment of the above, some common scenarios to
>>>> think about are along these lines:
>>>>
>>>> 1)
>>>> Userspace closes a handle by speculatively "guessing" it from a second
>>>> thread.
>>>>
>>>> This results in an unreachable buffer object so, a memory leak.
>>>>
>>>> 2)
>>>> Same as 1), but object is in the process of getting closed (failed
>>>> creation).
>>>>
>>>> The second thread is then able to re-cycle the handle and idr_remove
>>>> would
>>>> in the first thread would then remove the handle it does not own
>>>> from the
>>>> idr.
>>>>
>>>> 3)
>>>> Going back to the earlier per driver problem space - individual impact
>>>> assesment of allowing a second thread to access and operate on a
>>>> partially
>>>> constructed handle / object. (Can something crash? Leak information?)
>>>>
>>>> In terms of identifying when the problem started I will tag some
>>>> patches
>>>> as references, but not all, if even any, of them actually point to a
>>>> broken state. I am just identifying points at which more opportunity
>>>> for
>>>> issues to arise was added.
>>>
>>> Yes I've looked into this once as well, but couldn't completely solve
>>> it for some reason.
>>>
>>> Give me a day or two to get this tested and all the logic swapped
>>> back into my head again.
>>
>> Managed to recollect what the problem with earlier attempts was?
>
> Nope, that's way to long ago. I can only assume that I ran into problems
> with the object_name_lock.
>
> Probably best to double check if that doesn't result in a lock inversion
> when somebody grabs the reservation lock in their ->load() callback.
Hmm I don't immediately follow the connection. But I have only found
radeon_driver_load_kms as using the load callback. Is there any lockdep
enabled CI for that driver which could tell us if there is a problem there?
Regards,
Tvrtko
>
> Regards,
> Christian.
>
>>
>> Regards,
>>
>> Tvrtko
>>
>>> Christian.
>>>
>>>>
>>>> References: 304eda32920b ("drm/gem: add hooks to notify driver when
>>>> object handle is created/destroyed")
>>>> References: ca481c9b2a3a ("drm/gem: implement vma access management")
>>>> References: b39b5394fabc ("drm/gem: Add drm_gem_object_funcs")
>>>> Cc: dri-devel at lists.freedesktop.org
>>>> Cc: Rob Clark <robdclark at chromium.org>
>>>> Cc: Ben Skeggs <bskeggs at redhat.com>
>>>> Cc: David Herrmann <dh.herrmann at gmail.com>
>>>> Cc: Noralf Trønnes <noralf at tronnes.org>
>>>> Cc: David Airlie <airlied at gmail.com>
>>>> Cc: Daniel Vetter <daniel at ffwll.ch>
>>>> Cc: amd-gfx at lists.freedesktop.org
>>>> Cc: lima at lists.freedesktop.org
>>>> Cc: nouveau at lists.freedesktop.org
>>>> Cc: Steven Price <steven.price at arm.com>
>>>> Cc: virtualization at lists.linux-foundation.org
>>>> Cc: spice-devel at lists.freedesktop.org
>>>> Cc: Zack Rusin <zackr at vmware.com>
>>>> ---
>>>> drivers/gpu/drm/drm_gem.c | 48
>>>> +++++++++++++++++++--------------------
>>>> 1 file changed, 24 insertions(+), 24 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
>>>> index aa15c52ae182..e3d897bca0f2 100644
>>>> --- a/drivers/gpu/drm/drm_gem.c
>>>> +++ b/drivers/gpu/drm/drm_gem.c
>>>> @@ -356,52 +356,52 @@ drm_gem_handle_create_tail(struct drm_file
>>>> *file_priv,
>>>> u32 *handlep)
>>>> {
>>>> struct drm_device *dev = obj->dev;
>>>> - u32 handle;
>>>> int ret;
>>>> WARN_ON(!mutex_is_locked(&dev->object_name_lock));
>>>> if (obj->handle_count++ == 0)
>>>> drm_gem_object_get(obj);
>>>> + ret = drm_vma_node_allow(&obj->vma_node, file_priv);
>>>> + if (ret)
>>>> + goto err_put;
>>>> +
>>>> + if (obj->funcs->open) {
>>>> + ret = obj->funcs->open(obj, file_priv);
>>>> + if (ret)
>>>> + goto err_revoke;
>>>> + }
>>>> +
>>>> /*
>>>> - * Get the user-visible handle using idr. Preload and perform
>>>> - * allocation under our spinlock.
>>>> + * Get the user-visible handle using idr as the _last_ step.
>>>> + * Preload and perform allocation under our spinlock.
>>>> */
>>>> idr_preload(GFP_KERNEL);
>>>> spin_lock(&file_priv->table_lock);
>>>> -
>>>> ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT);
>>>> -
>>>> spin_unlock(&file_priv->table_lock);
>>>> idr_preload_end();
>>>> - mutex_unlock(&dev->object_name_lock);
>>>> if (ret < 0)
>>>> - goto err_unref;
>>>> -
>>>> - handle = ret;
>>>> + goto err_close;
>>>> - ret = drm_vma_node_allow(&obj->vma_node, file_priv);
>>>> - if (ret)
>>>> - goto err_remove;
>>>> + mutex_unlock(&dev->object_name_lock);
>>>> - if (obj->funcs->open) {
>>>> - ret = obj->funcs->open(obj, file_priv);
>>>> - if (ret)
>>>> - goto err_revoke;
>>>> - }
>>>> + *handlep = ret;
>>>> - *handlep = handle;
>>>> return 0;
>>>> +err_close:
>>>> + if (obj->funcs->close)
>>>> + obj->funcs->close(obj, file_priv);
>>>> err_revoke:
>>>> drm_vma_node_revoke(&obj->vma_node, file_priv);
>>>> -err_remove:
>>>> - spin_lock(&file_priv->table_lock);
>>>> - idr_remove(&file_priv->object_idr, handle);
>>>> - spin_unlock(&file_priv->table_lock);
>>>> -err_unref:
>>>> - drm_gem_object_handle_put_unlocked(obj);
>>>> +err_put:
>>>> + if (--obj->handle_count == 0)
>>>> + drm_gem_object_put(obj);
>>>> +
>>>> + mutex_unlock(&dev->object_name_lock);
>>>> +
>>>> return ret;
>>>> }
>>>
>
More information about the lima
mailing list