diff --git a/src/libANGLE/renderer/vulkan/vk_utils.cpp b/src/libANGLE/renderer/vulkan/vk_utils.cpp index 765b3e269e..22b9150ab6 100644 --- a/src/libANGLE/renderer/vulkan/vk_utils.cpp +++ b/src/libANGLE/renderer/vulkan/vk_utils.cpp @@ -1968,6 +1968,24 @@ void GetExtentsAndLayerCount(gl::TextureType textureType, vk::LevelIndex GetLevelIndex(gl::LevelIndex levelGL, gl::LevelIndex baseLevel) { + // SECURITY: |baseLevel| is the VkImage's mFirstAllocatedLevel. If the + // frontend's GL_TEXTURE_BASE_LEVEL has been changed since the image was + // allocated and the source texture was not re-synced (e.g. on the + // glCopy[Sub]TextureCHROMIUM path, which does not include + // DIRTY_OBJECT_TEXTURES), |levelGL| can be < |baseLevel|. Without this + // check the subtraction below underflows to ~0xFFFFFFFE and is forwarded + // verbatim to vkCmdCopyImage / vkCmdCopyImageToBuffer / image-view + // baseMipLevel, violating VUID-VkImageSubresourceLayers-mipLevel-07967 + // and reading out-of-bounds GPU memory on permissive drivers. Upgrade + // the debug-only ASSERT to a release crash so a compromised renderer can + // at worst DoS the GPU process rather than exfiltrate cross-origin GPU + // memory. + if (ANGLE_UNLIKELY(baseLevel.get() > levelGL.get())) + { + ERR() << "GetLevelIndex underflow: levelGL=" << levelGL.get() + << " < mFirstAllocatedLevel=" << baseLevel.get(); + ANGLE_CRASH(); + } ASSERT(baseLevel <= levelGL); return vk::LevelIndex(levelGL.get() - baseLevel.get()); }