From da7ed1c5769ffb60c791d67e52688487a7627fcd Mon Sep 17 00:00:00 2001 From: xueyuli2 Date: Thu, 7 Aug 2025 09:27:50 +0800 Subject: [PATCH] amd/virtio: fix bo use-after-free race condition in amdvgpu_bo_free In amdvgpu_bo_free(), when the reference count drops to 0, vdrm_flush() is called before removing the bo from the handle_to_vbo hash table. Since vdrm_flush() is a time-consuming operation and is executed outside of the handle_to_vbo_mutex lock, another thread calling amdvgpu_bo_import() can concurrently find this bo in the hash table, increment its refcount, and attempt to use it. Once vdrm_flush() finishes, amdvgpu_bo_free() proceeds to remove the bo and call free(), leaving the importing thread with a dangling pointer, which leads to a use-after-free or double free crash. To fix this race condition, we must remove the bo from the hash table under the lock first. After the bo is safely unlinked and the lock is released, we can then perform the time-consuming vdrm_flush() and the actual memory release. Signed-off-by: zhaqian Part-of: --- src/amd/common/virtio/amdgpu_virtio_bo.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/amd/common/virtio/amdgpu_virtio_bo.c b/src/amd/common/virtio/amdgpu_virtio_bo.c index a7e4fa0ecae..cd57c94f72e 100644 --- a/src/amd/common/virtio/amdgpu_virtio_bo.c +++ b/src/amd/common/virtio/amdgpu_virtio_bo.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 Advanced Micro Devices, Inc. + * Copyright 2026 Advanced Micro Devices, Inc. * * SPDX-License-Identifier: MIT */ @@ -125,27 +125,29 @@ int amdvgpu_bo_export(amdvgpu_device_handle dev, amdvgpu_bo_handle bo, } int amdvgpu_bo_free(amdvgpu_device_handle dev, struct amdvgpu_bo *bo) { + simple_mtx_lock(&dev->handle_to_vbo_mutex); int refcnt = p_atomic_dec_return(&bo->refcount); if (refcnt == 0) { - /* Flush pending ops. */ - vdrm_flush(dev->vdev); - /* Remove it from the bo table. */ if (bo->host_blob->handle > 0) { - simple_mtx_lock(&dev->handle_to_vbo_mutex); void *entry = _mesa_hash_table_u64_search(dev->handle_to_vbo, bo->host_blob->handle); if (entry) { /* entry can be NULL for the shmem buffer. */ _mesa_hash_table_u64_remove(dev->handle_to_vbo, bo->host_blob->handle); } - simple_mtx_unlock(&dev->handle_to_vbo_mutex); } + simple_mtx_unlock(&dev->handle_to_vbo_mutex); + + /* Flush pending ops. */ + vdrm_flush(dev->vdev); if (bo->host_blob) destroy_host_blob(dev, bo->host_blob); free(bo); + } else { + simple_mtx_unlock(&dev->handle_to_vbo_mutex); } return 0;