Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in ANGLE
DescriptionUse after free in ANGLE
ComponentANGLE
Bug ClassUAF
Tracker500560234
Fix commit464cb960322f (angle/angle) +2/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Files Changed

  • src/libANGLE/renderer/vulkan/ContextVk.cpp
From 464cb960322f4bef1a189b7d1b525349eea4ca13 Mon Sep 17 00:00:00 2001
From: Shahbaz Youssefi <syoussefi@chromium.org>
Date: Thu, 04 Jun 2026 11:07:32 -0400
Subject: [PATCH] Vulkan: Don't ignore `finish()` result on destroy

If `finish()` fails, treat it as a device loss before cleaning up.  This
is a theoretical fix for unlikely scenarios such as OOM during context
destruction.

Bug: chromium:500560234
Change-Id: I579b929afcfdca96a9b933d6b86806af5beb434a
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7903240
Reviewed-by: Charlie Lao <cclao@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
---

diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index d7b9bde..a9aef50 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -1251,10 +1251,10 @@
     mIncompleteTextures.onDestroy(context);
 
     // Flush and complete current outstanding work before destruction.
-    (void)finishImpl(QueueSubmitReason::ContextDestruction);
+    const angle::Result finishResult = finishImpl(QueueSubmitReason::ContextDestruction);
 
     // The finish call could also generate device loss.
-    if (mRenderer->isDeviceLost())
+    if (mRenderer->isDeviceLost() || finishResult != angle::Result::Continue)
     {
         mRenderer->handleDeviceLost();
     }
Loading diff…

Original Bug Report

reported by vm...@google.com

Driver-side UAF in ANGLE Vulkan via unchecked ContextVk::onDestroy OOM errors

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 without the security team.

Overview: A logic flaw in ANGLE’s Vulkan backend allows GPU context destruction to proceed without proper synchronization if an Out-Of-Memory error occurs. Because ContextVk::onDestroy discards the error returned by finishImpl and the device is not marked as lost, Vulkan objects are destroyed while the GPU is still actively using them. This leads to a potential driver-side Use-After-Free in the GPU process, which bypasses MiraclePtr and could allow a sandbox escape.

Affected files:

  • third_party/angle/src/libANGLE/renderer/vulkan/ContextVk.cpp
  • third_party/angle/src/libANGLE/renderer/vulkan/CommandQueue.cpp
  • third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.cpp
  • third_party/angle/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
  • third_party/angle/src/libANGLE/renderer/vulkan/UtilsVk.cpp

Estimated timestamp from git blame: 2025-12-15

Summary

There is a potential Use-After-Free (UAF) vulnerability in ANGLE’s Vulkan backend during context destruction. If an Out-Of-Memory (OOM) error occurs while flushing the remaining commands during teardown, the synchronization step is skipped. Since the failure is discarded and the device is not marked as “lost”, ANGLE immediately destroys Vulkan resources (like VkQueryPool) that the GPU is still actively writing to, leading to a driver-level memory corruption.

Technical Details

  1. In ContextVk.cpp, ContextVk::onDestroy initiates teardown by calling (void)finishImpl(QueueSubmitReason::ContextDestruction). The (void) cast explicitly discards any returned errors.
  2. finishImpl attempts to flush commands (flushAndSubmitCommands) and wait for the GPU to finish using context resources (mRenderer->finishResourceUse).
  3. If a Vulkan call within these operations fails due to an OOM (e.g., VK_ERROR_OUT_OF_HOST_MEMORY), the ANGLE_VK_TRY macro triggers ContextVk::handleError and returns angle::Result::Stop.
  4. ContextVk::handleError logs the OOM error but crucially does not call handleDeviceLost() or set the mDeviceLost flag, because the error is not explicitly VK_ERROR_DEVICE_LOST.
  5. The angle::Result::Stop error propagates back up but is discarded by the (void) cast in onDestroy.
  6. onDestroy then checks if (mRenderer->isDeviceLost()). Since the OOM error did not set this flag, the check evaluates to false, bypassing mRenderer->handleDeviceLost(). (If executed, this fallback would have called mQueueMap.waitAllQueuesIdle() to safely sync the GPU).
  7. In release builds, the subsequent ASSERT(mRenderer->hasResourceUseFinished(...)) is a no-op.
  8. Without waiting for the GPU, onDestroy iterates and destroys Vulkan objects like VkQueryPool and VkCommandPool via their respective driver calls (vkDestroyQueryPool, etc.).
  9. The Vulkan driver frees the host-memory allocations backing these objects while the asynchronous GPU operations from prior flushes are still running.

Impact

This causes a UAF in the GPU process. Because the memory backing Vulkan objects like query pools is managed by the proprietary Vulkan driver rather than Chromium’s PartitionAlloc, mitigations like MiraclePtr (BackupRefPtr) do not apply. An attacker could exploit this memory corruption to achieve Remote Code Execution (RCE) in the GPU process, effectively escaping the Renderer sandbox.

Potential Trigger Steps

Note: These are suggested steps to trigger the vulnerability. Our tooling agent does not currently have the capability to execute code to provide a working Proof of Concept.

  1. Setup: From a malicious webpage, initialize a WebGL2 context (using the ANGLE Vulkan backend).
  2. Begin Query: Start a WebGL occlusion query (gl.beginQuery) to allocate tracking structures in a VkQueryPool.
  3. Queue Heavy Workload: Dispatch a massive draw call (e.g., millions of vertices) while the query is active to ensure the GPU will be busy for an extended period, and call gl.flush().
  4. Induce Memory Pressure: Rapidly allocate large textures or buffers via WebGL to exhaust available GPU/host memory, ensuring subsequent Vulkan allocations will fail with an OOM error.
  5. Trigger UAF: Destroy the WebGL context (e.g., by navigating away). finishImpl will hit an OOM, skip the GPU wait, and the host will free the VkQueryPool. The GPU, still executing the heavy draw call, will eventually write its query results into the now-freed host memory.

Suggested Fix

Do not silently discard the result of finishImpl in ContextVk::onDestroy. If finishImpl returns an error (such as angle::Result::Stop), ensure that the renderer explicitly forces a GPU synchronization (e.g., via mQueueMap.waitAllQueuesIdle()) before destroying any Vulkan objects. Alternatively, update ContextVk::handleError to treat teardown-time OOMs as a fatal device loss to ensure the fallback sync path is always taken.

Evaluated with Chrome root at commit: 137d451a126685dd5010e6609db9f6d4a78d8234


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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.

View on issue tracker