[Mesa-dev] [PATCH 07/10] anv: Add sizeless anv_bo_cache_import()
Jason Ekstrand
jason at jlekstrand.net
Mon Oct 16 23:20:44 UTC 2017
On Mon, Oct 16, 2017 at 11:55 AM, Chad Versace <chadversary at chromium.org>
wrote:
> The patch renames anv_bo_cache_import() to
> anv_bo_cache_import_with_size(); and adds a new variant,
> anv_bo_cache_import(), that lacks the 'size' parameter. Same as
> pre-patch, anv_bo_cache_import_with_size() continues to validate the
> imported size.
>
> The patch is essentially a refactor patch. It should change no existing
> behavior.
>
> This split makes it easier to implement VK_ANDROID_native_buffer. When
> the user imports a gralloc hande into a VkImage using
> VK_ANDROID_native_buffer, the user provides no size. The driver must
> infer the size from the internals of the gralloc buffer.
> ---
> src/intel/vulkan/anv_allocator.c | 118 +++++++++++++++++++++++++-----
> ---------
> src/intel/vulkan/anv_device.c | 7 ++-
> src/intel/vulkan/anv_intel.c | 5 +-
> src/intel/vulkan/anv_private.h | 3 +
> src/intel/vulkan/anv_queue.c | 5 +-
> 5 files changed, 90 insertions(+), 48 deletions(-)
>
> diff --git a/src/intel/vulkan/anv_allocator.c b/src/intel/vulkan/anv_
> allocator.c
> index 401cea40e6..1deecc36d7 100644
> --- a/src/intel/vulkan/anv_allocator.c
> +++ b/src/intel/vulkan/anv_allocator.c
> @@ -1269,62 +1269,98 @@ anv_bo_cache_alloc(struct anv_device *device,
> return VK_SUCCESS;
> }
>
> +static VkResult
> +anv_bo_cache_import_locked(struct anv_device *device,
> + struct anv_bo_cache *cache,
> + int fd, struct anv_bo **bo_out)
> +{
> + uint32_t gem_handle = anv_gem_fd_to_handle(device, fd);
> + if (!gem_handle)
> + return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
> +
> + struct anv_cached_bo *bo = anv_bo_cache_lookup_locked(cache,
> gem_handle);
> + if (bo) {
> + __sync_fetch_and_add(&bo->refcount, 1);
> + return VK_SUCCESS;
> + }
> +
> + off_t size = lseek(fd, 0, SEEK_END);
> + if (size == (off_t)-1) {
> + anv_gem_close(device, gem_handle);
> + return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
> + }
> +
> + bo = vk_alloc(&device->alloc, sizeof(struct anv_cached_bo), 8,
> + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
> + if (!bo) {
> + anv_gem_close(device, gem_handle);
> + return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
> + }
> +
> + bo->refcount = 1;
> + anv_bo_init(&bo->bo, gem_handle, size);
> + _mesa_hash_table_insert(cache->bo_map, (void *)(uintptr_t)gem_handle,
> bo);
> + *bo_out = &bo->bo;
> +
> + return VK_SUCCESS;
> +}
> +
> VkResult
> anv_bo_cache_import(struct anv_device *device,
> struct anv_bo_cache *cache,
> - int fd, uint64_t size, struct anv_bo **bo_out)
> + int fd, struct anv_bo **bo_out)
> {
> + VkResult result;
> +
> pthread_mutex_lock(&cache->mutex);
> + result = anv_bo_cache_import_locked(device, cache, fd, bo_out);
> + pthread_mutex_unlock(&cache->mutex);
> +
> + return result;
> +}
> +
> +VkResult
> +anv_bo_cache_import_with_size(struct anv_device *device,
> + struct anv_bo_cache *cache,
> + int fd, uint64_t size,
> + struct anv_bo **bo_out)
>
I don't really like this function... I'd rather we do the size checks in
the caller but we can do that refactor later.
> +{
> + struct anv_bo *bo = NULL;
> + VkResult result;
>
> /* The kernel is going to give us whole pages anyway */
> size = align_u64(size, 4096);
>
> - uint32_t gem_handle = anv_gem_fd_to_handle(device, fd);
> - if (!gem_handle) {
> + pthread_mutex_lock(&cache->mutex);
> +
> + result = anv_bo_cache_import_locked(device, cache, fd, &bo);
> + if (result != VK_SUCCESS) {
> pthread_mutex_unlock(&cache->mutex);
> - return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
> + return result;
> }
>
> - struct anv_cached_bo *bo = anv_bo_cache_lookup_locked(cache,
> gem_handle);
> - if (bo) {
> - if (bo->bo.size != size) {
> - pthread_mutex_unlock(&cache->mutex);
> - return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
> - }
> - __sync_fetch_and_add(&bo->refcount, 1);
> - } else {
> - /* For security purposes, we reject BO imports where the size does
> not
> - * match exactly. This prevents a malicious client from passing a
> - * buffer to a trusted client, lying about the size, and telling the
> - * trusted client to try and texture from an image that goes
> - * out-of-bounds. This sort of thing could lead to GPU hangs or
> worse
> - * in the trusted client. The trusted client can protect itself
> against
> - * this sort of attack but only if it can trust the buffer size.
> - */
> - off_t import_size = lseek(fd, 0, SEEK_END);
> - if (import_size == (off_t)-1 || import_size < size) {
> - anv_gem_close(device, gem_handle);
> - pthread_mutex_unlock(&cache->mutex);
> - return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
> - }
> -
> - bo = vk_alloc(&device->alloc, sizeof(struct anv_cached_bo), 8,
> - VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
> - if (!bo) {
> - anv_gem_close(device, gem_handle);
> - pthread_mutex_unlock(&cache->mutex);
> - return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
> - }
> -
> - bo->refcount = 1;
> -
> - anv_bo_init(&bo->bo, gem_handle, size);
> -
> - _mesa_hash_table_insert(cache->bo_map, (void
> *)(uintptr_t)gem_handle, bo);
> + /* For security purposes, we reject BO imports where the size does not
> + * match exactly. This prevents a malicious client from passing a
> + * buffer to a trusted client, lying about the size, and telling the
> + * trusted client to try and texture from an image that goes
> + * out-of-bounds. This sort of thing could lead to GPU hangs or worse
> + * in the trusted client. The trusted client can protect itself
> against
> + * this sort of attack but only if it can trust the buffer size.
> + *
> + * To successfully prevent that attack, we must check the fd's size and
> + * complete the fd's import into the cache during the same critical
> + * section. Otherwise, if the cache were unlocked between importing
> the fd
> + * and checking the its size, then the attacker could exploit a race by
> + * importing the fd concurrently in separate threads.
> + */
> + if (bo->size != size) {
>
FYI: There are patches in-flight to make this a < rather than a !=.
> + anv_bo_cache_release(device, cache, bo);
> + pthread_mutex_unlock(&cache->mutex);
>
This is a problem... anv_bo_cache_release will try to take the lock but
it's already held here. I think the solution is to just not separate
bo_cach_import from bo_cache_import_locked and let bo_cache_import do all
the locking. There's no harm in doing an import and then releasing it
later if the size is wrong.
> + return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
> }
>
> pthread_mutex_unlock(&cache->mutex);
> - *bo_out = &bo->bo;
> + *bo_out = bo;
>
> return VK_SUCCESS;
> }
> diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c
> index 1634b5158c..94bbf8c819 100644
> --- a/src/intel/vulkan/anv_device.c
> +++ b/src/intel/vulkan/anv_device.c
> @@ -1542,9 +1542,10 @@ VkResult anv_AllocateMemory(
> assert(fd_info->handleType ==
> VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR);
>
> - result = anv_bo_cache_import(device, &device->bo_cache,
> - fd_info->fd,
> pAllocateInfo->allocationSize,
> - &mem->bo);
> + result = anv_bo_cache_import_with_size(device, &device->bo_cache,
> + fd_info->fd,
> +
> pAllocateInfo->allocationSize,
> + &mem->bo);
> if (result != VK_SUCCESS)
> goto fail;
>
> diff --git a/src/intel/vulkan/anv_intel.c b/src/intel/vulkan/anv_intel.c
> index d6bad44091..9c1321a065 100644
> --- a/src/intel/vulkan/anv_intel.c
> +++ b/src/intel/vulkan/anv_intel.c
> @@ -75,8 +75,9 @@ VkResult anv_CreateDmaBufImageINTEL(
>
> image = anv_image_from_handle(image_h);
>
> - result = anv_bo_cache_import(device, &device->bo_cache,
> - pCreateInfo->fd, image->size, &mem->bo);
> + result = anv_bo_cache_import_with_size(device, &device->bo_cache,
> + pCreateInfo->fd, image->size,
> + &mem->bo);
> if (result != VK_SUCCESS)
> goto fail_import;
>
> diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_
> private.h
> index 27d2c34203..ecdd9d5e32 100644
> --- a/src/intel/vulkan/anv_private.h
> +++ b/src/intel/vulkan/anv_private.h
> @@ -717,6 +717,9 @@ VkResult anv_bo_cache_alloc(struct anv_device *device,
> struct anv_bo_cache *cache,
> uint64_t size, struct anv_bo **bo);
> VkResult anv_bo_cache_import(struct anv_device *device,
> + struct anv_bo_cache *cache,
> + int fd, struct anv_bo **bo);
> +VkResult anv_bo_cache_import_with_size(struct anv_device *device,
> struct anv_bo_cache *cache,
> int fd, uint64_t size, struct anv_bo **bo);
> VkResult anv_bo_cache_export(struct anv_device *device,
> diff --git a/src/intel/vulkan/anv_queue.c b/src/intel/vulkan/anv_queue.c
> index aff7c53119..e26254a87e 100644
> --- a/src/intel/vulkan/anv_queue.c
> +++ b/src/intel/vulkan/anv_queue.c
> @@ -1006,6 +1006,7 @@ VkResult anv_ImportSemaphoreFdKHR(
> ANV_FROM_HANDLE(anv_device, device, _device);
> ANV_FROM_HANDLE(anv_semaphore, semaphore, pImportSemaphoreFdInfo->
> semaphore);
> int fd = pImportSemaphoreFdInfo->fd;
> + VkResult result;
>
> struct anv_semaphore_impl new_impl = {
> .type = ANV_SEMAPHORE_TYPE_NONE,
> @@ -1033,8 +1034,8 @@ VkResult anv_ImportSemaphoreFdKHR(
> } else {
> new_impl.type = ANV_SEMAPHORE_TYPE_BO;
>
> - VkResult result = anv_bo_cache_import(device, &device->bo_cache,
> - fd, 4096, &new_impl.bo);
> + result = anv_bo_cache_import_with_size(device,
> &device->bo_cache, fd,
> + 4096, &new_impl.bo);
> if (result != VK_SUCCESS)
> return result;
>
> --
> 2.13.0
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/mesa-dev/attachments/20171016/511cd197/attachment-0001.html>
More information about the mesa-dev
mailing list