[Mesa-dev] [PATCH 07/10] anv: Add sizeless anv_bo_cache_import()

Jason Ekstrand jason at jlekstrand.net
Tue Oct 17 18:12:45 UTC 2017


On Tue, Oct 17, 2017 at 10:52 AM, Chad Versace <chadversary at chromium.org>
wrote:

> On Mon 16 Oct 2017, Jason Ekstrand wrote:
> > On Mon, Oct 16, 2017 at 11:55 AM, Chad Versace <[1]
> 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.
>
> I don't believe the caller can check the size securely. See my comments
> below.
>
> >
> >     +{
> >     +   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 !=.
>
> Should I replace < with != now?
>

No, that's a functional change; this is a refactor.


> >
> >
> >     +      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.
>
> Right. anv_bo_cache_release() locks/unlocks the mutex while
> anv_bo_cache_import_with_size() holds the lock. Oops.
>
> One solution, which you won't like, is to add a new function
> anv_bo_cache_release_locked() and call that within
> anv_bo_cache_import_with_size().
>
> > 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.
>
> I believe it's unsafe to complete the import then release it if the size
> is wrong. I explained that in the comment above that begins "To
> successfully prevent that attack...".
>
> >
> >
> >     +      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, &[2]
> new_impl.bo);
> >     +         result = anv_bo_cache_import_with_size(device,
> &device->bo_cache,
> >     fd,
> >     +                                                4096, &[3]
> new_impl.bo);
> >               if (result != VK_SUCCESS)
> >                  return result;
> >
> >     --
> >     2.13.0
> >
> >     _______________________________________________
> >     mesa-dev mailing list
> >     [4]mesa-dev at lists.freedesktop.org
> >     [5]https://lists.freedesktop.org/mailman/listinfo/mesa-dev
> >
> >
> >
> > References:
> >
> > [1] mailto:chadversary at chromium.org
> > [2] http://new_impl.bo/
> > [3] http://new_impl.bo/
> > [4] mailto:mesa-dev at lists.freedesktop.org
> > [5] https://lists.freedesktop.org/mailman/listinfo/mesa-dev
>
> > _______________________________________________
> > 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/20171017/a56b4abc/attachment-0001.html>


More information about the mesa-dev mailing list