CVE-2026-14398
Overview
Background
- `RefCountedEvent`
- ANGLE Vulkan wrapper that owns a
VkEventthrough a heap-allocatedmHandle(aRefCounted<EventAndLayout>or similar), used to coordinate GPU synchronization. - `mHandle`
- the heap pointer inside
RefCountedEventthat must be either a valid reference-counted event object ornullptr; an intermediate dangling value violates the object’s invariant. - `UNREACHABLE()`
- an ANGLE macro asserting a code path is impossible; in release builds it degrades to a no-op with undefined behavior rather than a hard abort, so the failure path silently continued.
- `SafeDelete`
- a helper that frees the pointed-to object and sets the pointer to
nullptrin one step, guaranteeing no dangling reference remains.
Root Cause Analysis
RefCountedEvent::init() allocates mHandle and then attempts to create the underlying VkEvent. On the failure path after VkEvent creation/allocation fails, the old code invoked UNREACHABLE() and immediately return false, without releasing or nulling the already-allocated mHandle. Because UNREACHABLE() is a no-op in release builds, execution fell through and left the RefCountedEvent in a half-initialized state with a dangling/invalid mHandle. Callers treated the false return as a recoverable condition (falling back to the pipelineBarrier path) but the object retained the stale pointer, so a later use, destruction, or reuse of that RefCountedEvent dereferenced freed or invalid memory.
The fix replaces UNREACHABLE() with ASSERT(false) and adds SafeDelete(mHandle) so the failed event becomes cleanly invalid (mHandle == nullptr), and the intended pipelineBarrier fallback runs without touching dangling memory.
false return does not by itself restore an object’s invariant; the failure path must also undo the partial allocation it made. Relying on UNREACHABLE() to guard a genuinely reachable error path meant the cleanup never happened in release builds, leaving a dangling mHandle.Attack Path
- Trigger event creation
A renderer sends GPU work that causes ANGLE to construct a
RefCountedEventand callRefCountedEvent::init(). - Force init failure
Under Vulkan resource pressure or an allocation/
vkCreateEventfailure,init()takes the failure path and returnsfalsewhile leavingmHandleallocated-but-invalid. - Dangling handle persists
The
RefCountedEventis retained with a stalemHandleinstead of a cleannullptrinvalid state. - Stale dereference
Later synchronization, reuse, or destruction of that
RefCountedEventdereferences the freed/invalidmHandle, producing a use-after-free in the GPU process. - Exploit the UAF With heap grooming controlling the freed slot, the dangling access can be steered toward memory corruption and control-flow influence in the GPU process.
Impact Assessment
init() failure path (e.g., resource exhaustion) but yields dangling-pointer access that can lead to memory corruption in the GPU process, a valuable target for sandbox-relevant escalation. The bug is release-build-specific because the UNREACHABLE() guard suppressed the crash that would otherwise surface in debug builds.Files Changed
src/libANGLE/renderer/vulkan/vk_ref_counted_event.cpp
Audit Directions
- `UNREACHABLE()` on reachable error pathsAudit ANGLE and other release builds for
UNREACHABLE()guarding paths that can actually fail (allocation,vkCreate*); these silently skip cleanup and leave partial state. - Partial-init cleanupReview
init()/create()functions that allocate a member before a fallible step and returnfalse; ensure every early return frees and nulls (SafeDelete) the allocated members, restoring the object’s invariant. - Invalid-object invariantsVerify that objects representing a “failed but retained” resource (like an invalid
RefCountedEventfeeding thepipelineBarrierfallback) holdnullptr, not dangling handles, wherever afalsereturn is treated as recoverable.
Patch
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;
Original Bug Report
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.cppthird_party/angle/src/libANGLE/renderer/vulkan/vk_ref_counted_event.hthird_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.cppthird_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
- Triggering the Poisoned State: An attacker uses WebGL to exhaust
VkEventhandles (a common limitation on some drivers) or creates memory pressure, causingvkCreateEventto fail during a synchronization operation. This leaves a recycledCommandBufferHelperwith a non-nullmHandlebut a zero reference count in its event array. - Incorrect Adoption: When the
CommandBufferHelperis reused, a subsequent image operation may check if an event is already valid for its stage. SincemHandleis non-null,RefCountedEvent::valid()returns true.ImageHelper::setCurrentRefCountedEventthen adopts this zombie event via copy assignment, which increments the reference count from 0 to 1. - Premature Free: When the adopting
ImageHelperlater releases its reference (e.g., during a layout transition), the reference count drops from 1 back to 0. On hardware drivers (whererecycleVkEventis typically disabled), this triggersSafeDelete(mHandle), freeing the memory. - Use-After-Free: The
CommandBufferHelperstill holds the now-freed pointer. Subsequent attempts to use that event stage on the same helper—either by anotherImageHelperor during command buffer submission—will result in a UAF write (refcount increment/decrement) or a UAF read (retrieving theVkEventhandle 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.