Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in ANGLE
DescriptionUse after free in ANGLE
ComponentANGLE
Bug ClassUAF
Tracker512995785
Fix commitca3a627b7270 (angle/angle) +3/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Files Changed

  • src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp
From ca3a627b72709b6fd4b56a1517ff1a926f7c3fca Mon Sep 17 00:00:00 2001
From: Charlie Lao <[email protected]>
Date: Wed, 13 May 2026 17:18:28 -0700
Subject: [PATCH] Vulkan: Ensure we clean up mHandle when Event::init() fail

Otherwise we may leave a dangling mHandle in RefCountedEvent and cause
potential UAF later.

Bug: b/512995785
Change-Id: Id9148c7d05fbca4abd9eda0e7f1c73edc372c224
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7846935
Reviewed-by: Yuxin Hu <[email protected]>
Reviewed-by: Shahbaz Youssefi <[email protected]>
Commit-Queue: Charlie Lao <[email protected]>
---

diff --git a/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp b/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp
index 1948b6c..3d855ae 100644
--- a/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp
@@ -60,7 +60,9 @@
                 // that many VkEvents under normal situation. If we failed to allocate, there is a
                 // high chance that we may have a leak somewhere. This macro should help us catch
                 // such potential bugs in the bots if that happens.
-                UNREACHABLE();
+                ASSERT(false);
+                // Ensure memory is freed and pointer is nulled
+                SafeDelete(mHandle);
                 // If still fail to create, we just return. An invalid event will trigger
                 // pipelineBarrier code path
                 return false;
Loading diff…

Original Bug Report

reported by [email protected]

Potential Use-After-Free in ANGLE Vulkan via RefCountedEvent initialization 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 without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A potential Use-After-Free (UAF) vulnerability exists in ANGLE’s Vulkan backend when vkCreateEvent fails during synchronization initialization. A leaked heap-allocated handle persists in recycled command buffers, leading to premature destruction and subsequent dangling pointer access.

Affected files:

  • third_party/angle/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp
  • third_party/angle/src/libANGLE/renderer/vulkan/vk_ref_counted_event.h
  • third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.cpp
  • third_party/angle/src/libANGLE/renderer/vulkan/vk_renderer.cpp

Estimated timestamp from git blame: 2024-05-10

Summary

A Use-After-Free (UAF) vulnerability has been identified in ANGLE’s Vulkan backend, specifically within the lifecycle management of RefCountedEvent. The issue stems from an inconsistent state created when vkCreateEvent fails, allowing a heap-allocated pointer to survive command buffer recycling. This can lead to memory corruption when the stale pointer is later reused and prematurely freed.

Root Cause Analysis

In third_party/angle/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp, the RefCountedEvent::init() function handles the allocation and initialization of Vulkan events. When a VkEvent fails to initialize (e.g., due to driver handle exhaustion), the function returns false but fails to clean up the newly allocated mHandle member:

// third_party/angle/src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp:43
mHandle = new RefCounted<EventAndStage>; // Refcount starts at 0
// ... initialization attempts ...
if (result != VK_SUCCESS)
{
    UNREACHABLE();
    return false; // mHandle remains non-null, pointing to a RefCounted object with refcount 0
}

The calling function, RefCountedEventArray::initEventAtStage(), receives the false return and does not set the corresponding bit in its mBitMask. Because the CommandBufferHelper recycling logic relies on this bitmask to identify and release active events, the “zombie” mHandle pointer remains in the mEvents array when the helper is returned to a pool.

Potential Exploitation Path

  1. Triggering the Poisoned State: An attacker uses WebGL to exhaust VkEvent handles (a common limitation on some drivers) or creates memory pressure, causing vkCreateEvent to fail during a synchronization operation. This leaves a recycled CommandBufferHelper with a non-null mHandle but a zero reference count in its event array.
  2. Incorrect Adoption: When the CommandBufferHelper is reused, a subsequent image operation may check if an event is already valid for its stage. Since mHandle is non-null, RefCountedEvent::valid() returns true. ImageHelper::setCurrentRefCountedEvent then adopts this zombie event via copy assignment, which increments the reference count from 0 to 1.
  3. Premature Free: When the adopting ImageHelper later releases its reference (e.g., during a layout transition), the reference count drops from 1 back to 0. On hardware drivers (where recycleVkEvent is typically disabled), this triggers SafeDelete(mHandle), freeing the memory.
  4. Use-After-Free: The CommandBufferHelper still holds the now-freed pointer. Subsequent attempts to use that event stage on the same helper—either by another ImageHelper or during command buffer submission—will result in a UAF write (refcount increment/decrement) or a UAF read (retrieving the VkEvent handle from freed memory).

In ANGLE, mHandle is a bare pointer to a RefCounted object, meaning this vulnerability is not mitigated by MiraclePtr. On Android, where the GPU process is often unsandboxed, this could potentially lead to platform-level arbitrary code execution.

Suggested Fix

Ensure that mHandle is properly deleted and set to nullptr if initialization fails in RefCountedEvent::init():

if (result != VK_SUCCESS)
{
    UNREACHABLE();
    SafeDelete(mHandle); // Ensure memory is freed and pointer is nulled
    return false;
}

Reproducibility Note

These steps are based on a source code analysis of the Vulkan backend synchronization logic. Tooling constraints currently prevent the execution of a proof-of-concept to confirm reachability under specific driver conditions.

Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e


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.

View on issue tracker