CVE-2026-79127
Overview
Files Changed
src/libANGLE/renderer/vulkan/ContextVk.cppsrc/libANGLE/renderer/vulkan/vk_helpers.cpp
Patch
From 7d65fc5ed3e3610c9156b9b9856eebeff57ff21d Mon Sep 17 00:00:00 2001 From: wangra <[email protected]> Date: Sun, 19 Jul 2026 14:55:07 -0400 Subject: [PATCH] Vulkan: Reset DynamicBuffer state on allocation failure - Reset DynamicBuffer state on map failure and harden allocateFromCurrentBuffer in release builds. - Clean up ContextVk active render pass tracking on error to prevent command buffer state desynchronization. Bug: b/517045394 Change-Id: Ic39e6e8d4b7d9bb49a5f5ff114ad37ae0bb827ad Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8118297 Reviewed-by: Charlie Lao <[email protected]> Reviewed-by: Shahbaz Youssefi <[email protected]> Commit-Queue: Shahbaz Youssefi <[email protected]> --- diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp index 3684745..1154257 100644 --- a/src/libANGLE/renderer/vulkan/ContextVk.cpp +++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp @@ -6987,6 +6987,7 @@ { mLastFlushedQueueSerial = mRenderPassCommands->getQueueSerial(); mRenderPassCommands->abandon(this, &collector); + mRenderPassCommandBuffer = nullptr; } collector.releaseCommandBuffers(); diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp index 46a7e0e..c80590c 100644 --- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp +++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp @@ -2972,7 +2972,7 @@ { // Allocate the buffer ASSERT(!mBuffer); - mBuffer = std::make_unique<BufferHelper>(); + RendererScoped<BufferHelper> buffer(context->getRenderer()); VkBufferCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -2983,11 +2983,19 @@ createInfo.queueFamilyIndexCount = 0; createInfo.pQueueFamilyIndices = nullptr; - return mBuffer->init(context, createInfo, mMemoryPropertyFlags); + ANGLE_TRY(buffer.get().init(context, createInfo, mMemoryPropertyFlags)); + + mBuffer = std::make_unique<BufferHelper>(buffer.release()); + return angle::Result::Continue; } bool DynamicBuffer::allocateFromCurrentBuffer(size_t sizeInBytes, BufferHelper **bufferHelperOut) { + if (mBuffer == nullptr) + { + return false; + } + mNextAllocationOffset = roundUp<uint32_t>(mNextAllocationOffset, static_cast<uint32_t>(mAlignment));
Original Bug Report
Potential wild write in GPU process due to un-reset stale offset on vkMapMemory failure
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential logic error in ANGLE’s Vulkan backend can cause DynamicBuffer to retain a stale mNextAllocationOffset and a partially-initialized mBuffer pointer if vkMapMemory fails. Because this failure translates to GL_INVALID_OPERATION rather than context loss, subsequent allocations can succeed using the stale state. This can lead to a wild memory write in the GPU process when downstream code attempts to copy uniform or vertex data.
Affected files:
third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.cppthird_party/angle/src/libANGLE/renderer/vulkan/Suballocation.cppthird_party/angle/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp
Estimated timestamp from git blame: 2020-12-01
Root Cause Analysis
In third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.cpp, DynamicBuffer::allocateNewBuffer assigns mBuffer before invoking BufferHelper::init:
angle::Result DynamicBuffer::allocateNewBuffer(ErrorContext *context)
{
...
ASSERT(!mBuffer);
mBuffer = std::make_unique<BufferHelper>();
...
return mBuffer->init(context, createInfo, mMemoryPropertyFlags);
}
If mBuffer->init fails (for example, if vkMapMemory returns VK_ERROR_MEMORY_MAP_FAILED), the failure propagates immediately inside DynamicBuffer::allocate:
if (mBufferFreeList.empty() || ...)
{
ANGLE_TRY(allocateNewBuffer(context));
}
...
mNextAllocationOffset = 0; // Bypassed on failure
Because the allocation failure propagates early, the following critical states are not rolled back:
mBufferremains pointing to the partially-initialized, unmappedBufferHelperinstance.mNextAllocationOffsetis not reset to0, retaining its previous stale, high value.
Survival of the GL Context
When vkMapMemory fails with VK_ERROR_MEMORY_MAP_FAILED, it maps to GL_INVALID_OPERATION via DefaultGLErrorCode in ContextVk.cpp:
GLenum DefaultGLErrorCode(VkResult result) {
switch (result) {
...
default:
return GL_INVALID_OPERATION; // VK_ERROR_MEMORY_MAP_FAILED falls here
}
}
Since ANGLE only triggers context loss on GL_OUT_OF_MEMORY or GL_CONTEXT_LOST, the WebGL context survives, records GL_INVALID_OPERATION, and continues processing GLES command buffers.
Downstream Wild Write in Release Builds
Upon a subsequent draw call requiring a smaller allocation, allocateFromCurrentBuffer checks if the requested size fits within the stale bounds:
bool DynamicBuffer::allocateFromCurrentBuffer(size_t sizeInBytes, BufferHelper **bufferHelperOut) {
mNextAllocationOffset = roundUp<uint32_t>(mNextAllocationOffset, mAlignment);
...
if (!checkedNextWriteOffset.IsValid() || checkedNextWriteOffset.ValueOrDie() > mSize)
return false;
...
mBuffer->setSuballocationOffsetAndSize(mNextAllocationOffset, sizeToAllocate);
*bufferHelperOut = mBuffer.get();
}
Since ASSERT(mBuffer->getMappedMemory()) is compiled out in release builds, the function returns the half-initialized BufferHelper. When downstream code (such as default uniform copy in ProgramExecutableVk.cpp or vertex streamed buffer copy) writes data to the buffer, it calls getMappedMemory(), which evaluates to (spec-undefined mMappedMemory) + stale_offset:
uint8_t *bufferData = defaultUniformBuffer->getMappedMemory();
memcpy(&bufferData[offsets[shaderType]], uniformData.data(), uniformData.size()); // WILD WRITE
Potential Steps to Trigger (Hypothetical)
Note: These steps are based on static code analysis and have not been executed on a running system.
- From a compromised renderer, create a WebGL context backed by ANGLE/Vulkan.
- Issue drawing commands with default uniforms to advance
mDefaultUniformStorage.mNextAllocationOffsetnear its limit (~56 KB out of 64 KB). - Artificially exhaust host virtual-address (VA) space to force the next
vkMapMemorycall to returnVK_ERROR_MEMORY_MAP_FAILEDduring a new buffer allocation. - Submit a draw call that triggers the new allocation. The allocation fails, but because it is handled as
GL_INVALID_OPERATION, the context survives and leavesmBufferandmNextAllocationOffsetun-reset. - Release VA pressure.
- Issue a smaller draw command where the uniform requirements fit within the stale buffer boundaries (
stale_offset + size <= mSize).allocateFromCurrentBuffersucceeds, returning the unmapped buffer helper. - A
memcpycopy of uniform data writes to an invalid address in the GPU process.
Suggested Fix
In DynamicBuffer::allocate (inside third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.cpp), ensure that if allocateNewBuffer fails, mBuffer is explicitly reset to nullptr and mNextAllocationOffset is cleared or rolled back to a safe state to prevent subsequent draw calls from utilizing stale states.
Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.