High CVSS 8.3 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in ANGLE
DescriptionUse after free in ANGLE
ComponentANGLE
Sub-componentD3D11
Bug ClassUAF
Tracker501563323
Fix commitba4afac4c523 (angle/angle) +112/-0
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-04-11 (per vendor advisory)
Fix landed2026-04-24 (public git)
Disclosed2026-05-27
Patch gap33 days — fix public in source before the advisory

Background

ANGLE D3D11 `Buffer11`
the D3D11 backend representation of a GL buffer (src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp). A single GL buffer is backed by several BufferStorage representations held in mBufferStorages[usage] (e.g. SYSTEM_MEMORY = index 0, STAGING = index 1, UNIFORM, …).
`mMappedStorage`
a raw BufferStorage* pointing at whichever storage currently backs an active CPU mapping. glUnmapBuffer later calls the virtual mMappedStorage->unmap().
`garbageCollection` / `checkForDeallocation`
periodic GC that deletes storages whose per-usage mIdleness[usage] exceeds mDeallocThresholds[usage] (default 8), unless that storage is the mLatestBufferStorage.
Priority inversion
mapping a SYSTEM_MEMORY buffer for read creates a STAGING storage for mMappedStorage, but mLatestBufferStorage stays on SYSTEM_MEMORY (STAGING’s priority index is not strictly higher), so GC considers the STAGING storage deletable while it is still the mapped one.
MiraclePtr exclusion
mMappedStorage is a raw C++ pointer outside Chromium’s BackupRefPtr, so a dangling value is directly exploitable.

Root Cause Analysis

The invariant is that the storage a buffer is currently mapped through (mMappedStorage) must never be freed while the mapping is live. Per the report, two flaws combined to break it: (1) mapping a read-only SYSTEM_MEMORY buffer creates a STAGING storage and points mMappedStorage at it, but leaves mLatestBufferStorage on SYSTEM_MEMORY; and (2) draw-time validation (ValidateDrawStates) checks that vertex/index buffers are not mapped but does not check UBOs. So a draw issued while a buffer is mapped and bound to many uniform blocks reaches StateManager11::syncUniformBuffersForShader, which calls Buffer11::getConstantBufferRange per binding; each call runs garbageCollection → checkForDeallocation(BUFFER_USAGE_STAGING), incrementing mIdleness[STAGING]. Once the program uses more than the threshold (8) of uniform-block bindings, mIdleness[STAGING] exceeds mDeallocThresholds[STAGING], and because mLatestBufferStorage (SYSTEM_MEMORY) != storage (STAGING), checkForDeallocation SafeDeletes the STAGING storage — leaving mMappedStorage dangling. The later glUnmapBuffer dereferences it via the virtual unmap(), a use-after-free in the GPU process.

The patch adds a defensive guard inside Buffer11::checkForDeallocation (Buffer11.cpp): right after fetching BufferStorage *&storage = mBufferStorages[usage];, it checks if (storage != nullptr && storage == mMappedStorage) and, if so, fails the operation with ANGLE_TRY_HR(..., E_FAIL, "Error deallocating mapped storage") instead of proceeding to delete it. This converts the UAF into a surfaced GL_INVALID_OPERATION at draw time. The commit message and an inline TODO(http://anglebug.com/505771894) are explicit that the real fix is to validate “buffer is mapped during draw” in the API layer (as vertex/index buffers already are) and later turn this check into an assert — so this change is an interim safety net, not the structural fix. The regression test SimpleUniformBufferTest.MappedUBOStagingGarbageCollection builds a 10-uniform-block program, maps a GL_DYNAMIC_DRAW buffer for read (forcing STAGING), draws, and asserts GL_INVALID_OPERATION where the old code would UAF on unmap. The advisory’s “Use-after-free” (CWE-416) matches the diff.

Key insight
A read-mapped buffer’s STAGING storage was the mMappedStorage but not the mLatestBufferStorage, and UBO mappings were never validated at draw time — so a draw with enough uniform-block bindings drove the STAGING storage’s idleness past the GC threshold and freed the still-mapped storage, leaving mMappedStorage dangling for glUnmapBuffer.

Attack Path

  1. Renderer (attacker) open a WebGL2/GLES3 context on Windows using the passthrough decoder (ANGLE D3D11, default).
  2. Force SYSTEM_MEMORY storage create a buffer with GL_DYNAMIC_DRAW/GL_STREAM_DRAW and data, so mLatestBufferStorage is SYSTEM_MEMORY.
  3. Bind to many UBO slots link a program using ≥9 uniform blocks and glBindBufferBase(GL_UNIFORM_BUFFER, 0..8, buffer).
  4. Map for read glMapBufferRange(..., GL_MAP_READ_BIT) allocates the STAGING storage and points mMappedStorage at it (while mLatestBufferStorage stays SYSTEM_MEMORY).
  5. Draw glDrawArrays; syncUniformBuffersForShader runs GC per binding; on the 9th the STAGING idleness exceeds the threshold and SafeDelete frees the mapped STAGING storage, dangling mMappedStorage.
  6. Groom (inferred) spray the GPU heap to reclaim the freed NativeStorage and forge its vtable.
  7. Primitive (inferred) glUnmapBuffer invokes the virtual mMappedStorage->unmap() on the freed/forged object — GPU-process code execution.

Impact Assessment

Runs in the GPU process, reachable from a WebGL2 renderer with no special privilege (default Windows/D3D11 path). The diff substantiates the use-after-free precondition — a draw can drive GC to free the currently mapped STAGING storage — and the fix makes that path fail with GL_INVALID_OPERATION (confirmed by the regression test). The heap reclamation and virtual-call-on-freed-object RCE are (inferred) from the report, not shown by this diff. Because mMappedStorage is a raw pointer excluded from MiraclePtr, the UAF is not downgraded to a safe crash. Gating: requires the D3D11 backend, a SYSTEM_MEMORY-backed read-mapped buffer, and a program with more uniform-block bindings than the dealloc threshold (default 8).

Changed Functions

FunctionChangeNotes
SimpleUniformBufferTest
src/tests/gl_tests/UniformBufferTest.cpp
modified

Files Changed

  • src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp
  • src/tests/gl_tests/UniformBufferTest.cpp

Audit Directions

  • UBO mapped-state validation
    the structural gap is that ValidateDrawStates/ValidateProgramDrawStates validate vertex/index buffers for being mapped but not UBOs (or SSBOs/atomic-counter buffers); audit all buffer-binding kinds consumed by draws for the same missing “is mapped” check.
  • `mLatestBufferStorage` priority ordering
    review the priority indices of all BufferStorage usages; any mapping whose storage is not the latest can be GC’d out from under mMappedStorage. Verify STAGING-vs-SYSTEM_MEMORY is the only such inversion.
  • GC during per-binding sync loops
    syncUniformBuffersForShader runs GC once per binding; audit other sync loops that call garbageCollection repeatedly within a single draw for threshold-crossing side effects on live objects.
  • Raw backend pointers
    mMappedStorage and peer raw pointers in the D3D11 backend are MiraclePtr-excluded; flag for raw_ptr migration to blunt exploitability of residual lifetime bugs.
From ba4afac4c523d50f5639014cc50f35052f125913 Mon Sep 17 00:00:00 2001
From: Antonio Maiorano <amaiorano@google.com>
Date: Thu, 23 Apr 2026 12:56:06 -0400
Subject: [PATCH] [d3d11] Fail on potential delete of mapped buffer during draw

Before this change, it was possible on D3D11 to map a buffer, then draw
in such a way that garbage collection attempts to delete the mapped
buffer, which would then result in a UAF on unmapping that buffer.

For now we detect this situation and return an invalid operation;
however, we should instead catch this in validation as per
http://anglebug.com/505771894.

Bug: chromium:501563323
Change-Id: I26f0a10564cc04ffd1eba31bcb542f0c5529a0ae
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7790252
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
---

diff --git a/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp
index d3ff511..1cbfd32 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp
@@ -622,6 +622,15 @@
     mIdleness[usage]++;
 
     BufferStorage *&storage = mBufferStorages[usage];
+
+    // TODO(http://anglebug.com/505771894): Add validation that a buffer is not mapped in calls that
+    // use it (draw, etc.). Once fixed, turn this into an assert.
+    if (storage != nullptr && storage == mMappedStorage)
+    {
+        ANGLE_TRY_HR(SafeGetImplAs<Context11>(context), E_FAIL,
+                     "Error deallocating mapped storage");
+    }
+
     if (storage != nullptr && mIdleness[usage] > mDeallocThresholds[usage])
     {
         BufferStorage *latestStorage = nullptr;
diff --git a/src/tests/gl_tests/UniformBufferTest.cpp b/src/tests/gl_tests/UniformBufferTest.cpp
index 36d5d79..45de0dd 100644
--- a/src/tests/gl_tests/UniformBufferTest.cpp
+++ b/src/tests/gl_tests/UniformBufferTest.cpp
@@ -5007,4 +5007,107 @@
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WebGL2UniformBufferTest);
 ANGLE_INSTANTIATE_TEST_ES3(WebGL2UniformBufferTest);
 
+class SimpleUniformBufferTest : public ANGLETest<>
+{
+  protected:
+    SimpleUniformBufferTest()
+    {
+        setWindowWidth(128);
+        setWindowHeight(128);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+    }
+};
+
+// Test that maps a buffer as staging, then performs a draw inducing a garbage collect of the
+// staging buffer. This would formerly UAF, but now returns an invalid operation.
+// TODO(http://anglebug.com/505771894): Update test once we validate that drawing with a mapped
+// buffer is invalid.
+TEST_P(SimpleUniformBufferTest, MappedUBOStagingGarbageCollection)
+{
+    // Only applies to D3D11 backend.
+    ANGLE_SKIP_TEST_IF(!IsD3D11());
+
+    // Create a program with 10 uniform blocks.
+    constexpr char kVS[] = R"(#version 300 es
+layout(std140) uniform block0 { vec4 data0; };
+layout(std140) uniform block1 { vec4 data1; };
+layout(std140) uniform block2 { vec4 data2; };
+layout(std140) uniform block3 { vec4 data3; };
+layout(std140) uniform block4 { vec4 data4; };
+layout(std140) uniform block5 { vec4 data5; };
+layout(std140) uniform block6 { vec4 data6; };
+layout(std140) uniform block7 { vec4 data7; };
+layout(std140) uniform block8 { vec4 data8; };
+layout(std140) uniform block9 { vec4 data9; };
+
+void main() {
+    vec4 total = vec4(0.0);
+    total += data0;
+    total += data1;
+    total += data2;
+    total += data3;
+    total += data4;
+    total += data5;
+    total += data6;
+    total += data7;
+    total += data8;
+    total += data9;
+    gl_Position = vec4(total.xyz, 1.0);
+}
+    )";
+
+    constexpr char kFS[] = R"(#version 300 es
+precision highp float;
+out vec4 color;
+void main() {
+    color = vec4(1.0);
+}
+)";
+
+    ANGLE_GL_PROGRAM(program, kVS, kFS);
+    glUseProgram(program);
+
+    // Create a buffer and bind it as a COPY_READ_BUFFER.
+    GLBuffer buffer;
+    glBindBuffer(GL_COPY_READ_BUFFER, buffer);
+
+    // Use GL_DYNAMIC_DRAW and provide data to ensure it uses SYSTEM_MEMORY storage
+    // and sets it as mLatestBufferStorage.
+    std::vector<uint8_t> data(1024, 0);
+    glBufferData(GL_COPY_READ_BUFFER, 1024, data.data(), GL_DYNAMIC_DRAW);
+
+    // Bind the buffer as a UBO to all 10 slots.
+    for (int i = 0; i < 10; ++i)
+    {
+        glBindBufferBase(GL_UNIFORM_BUFFER, i, buffer);
+    }
+
+    // Map the buffer for reading. Since it's SYSTEM_MEMORY and we are mapping for read,
+    // it should allocate a STAGING buffer.
+    void *ptr = glMapBufferRange(GL_COPY_READ_BUFFER, 0, 1024, GL_MAP_READ_BIT);
+    ASSERT_NE(ptr, nullptr);
+
+    // Now perform a draw call.
+    // This will trigger StateManager11::syncUniformBuffersForShader.
+    // It will iterate through the 10 slots.
+    // For each slot, it calls Buffer11::getConstantBufferRange -> getBufferStorage(UNIFORM) ->
+    // garbageCollection. garbageCollection(UNIFORM) calls checkForDeallocation(STAGING). On the 9th
+    // slot, STAGING's idleness will be 9, exceeding the threshold of 8. checkForDeallocation would
+    // then attempt to delete the STAGING storage because latestStorage (SYSTEM_MEMORY) != storage
+    // (STAGING), however, it currently detects this and returns failure.
+    glDrawArrays(GL_POINTS, 0, 1);
+
+    // Now unmap. This calls Buffer11::unmap() which uses mMappedStorage and would formerly UAF on
+    // accessing mMappedBuffer.
+    glUnmapBuffer(GL_COPY_READ_BUFFER);
+
+    ASSERT_GL_ERROR(GL_INVALID_OPERATION);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SimpleUniformBufferTest);
+ANGLE_INSTANTIATE_TEST_ES3(SimpleUniformBufferTest);
+
 }  // namespace
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/tests/gl_tests/UniformBufferTest.cpp b/src/tests/gl_tests/UniformBufferTest.cpp
index 36d5d79..45de0dd 100644
--- a/src/tests/gl_tests/UniformBufferTest.cpp
+++ b/src/tests/gl_tests/UniformBufferTest.cpp
@@ -5007,4 +5007,107 @@
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WebGL2UniformBufferTest);
 ANGLE_INSTANTIATE_TEST_ES3(WebGL2UniformBufferTest);
 
+class SimpleUniformBufferTest : public ANGLETest<>
+{
+  protected:
+    SimpleUniformBufferTest()
+    {
+        setWindowWidth(128);
+        setWindowHeight(128);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+    }
+};
+
+// Test that maps a buffer as staging, then performs a draw inducing a garbage collect of the
+// staging buffer. This would formerly UAF, but now returns an invalid operation.
+// TODO(http://anglebug.com/505771894): Update test once we validate that drawing with a mapped
+// buffer is invalid.
+TEST_P(SimpleUniformBufferTest, MappedUBOStagingGarbageCollection)
+{
+    // Only applies to D3D11 backend.
+    ANGLE_SKIP_TEST_IF(!IsD3D11());
+
+    // Create a program with 10 uniform blocks.
+    constexpr char kVS[] = R"(#version 300 es
+layout(std140) uniform block0 { vec4 data0; };
+layout(std140) uniform block1 { vec4 data1; };
+layout(std140) uniform block2 { vec4 data2; };
+layout(std140) uniform block3 { vec4 data3; };
+layout(std140) uniform block4 { vec4 data4; };
+layout(std140) uniform block5 { vec4 data5; };
+layout(std140) uniform block6 { vec4 data6; };
+layout(std140) uniform block7 { vec4 data7; };
+layout(std140) uniform block8 { vec4 data8; };
+layout(std140) uniform block9 { vec4 data9; };
+
+void main() {
+    vec4 total = vec4(0.0);
+    total += data0;
+    total += data1;
+    total += data2;
+    total += data3;
+    total += data4;
+    total += data5;
+    total += data6;
+    total += data7;
+    total += data8;
+    total += data9;
+    gl_Position = vec4(total.xyz, 1.0);
+}
+    )";
+
+    constexpr char kFS[] = R"(#version 300 es
+precision highp float;
+out vec4 color;
+void main() {
+    color = vec4(1.0);
+}
+)";
+
+    ANGLE_GL_PROGRAM(program, kVS, kFS);
+    glUseProgram(program);
+
+    // Create a buffer and bind it as a COPY_READ_BUFFER.
+    GLBuffer buffer;
+    glBindBuffer(GL_COPY_READ_BUFFER, buffer);
+
+    // Use GL_DYNAMIC_DRAW and provide data to ensure it uses SYSTEM_MEMORY storage
+    // and sets it as mLatestBufferStorage.
+    std::vector<uint8_t> data(1024, 0);
+    glBufferData(GL_COPY_READ_BUFFER, 1024, data.data(), GL_DYNAMIC_DRAW);
+
+    // Bind the buffer as a UBO to all 10 slots.
+    for (int i = 0; i < 10; ++i)
+    {
+        glBindBufferBase(GL_UNIFORM_BUFFER, i, buffer);
+    }
+
+    // Map the buffer for reading. Since it's SYSTEM_MEMORY and we are mapping for read,
+    // it should allocate a STAGING buffer.
+    void *ptr = glMapBufferRange(GL_COPY_READ_BUFFER, 0, 1024, GL_MAP_READ_BIT);
+    ASSERT_NE(ptr, nullptr);
+
+    // Now perform a draw call.
+    // This will trigger StateManager11::syncUniformBuffersForShader.
+    // It will iterate through the 10 slots.
+    // For each slot, it calls Buffer11::getConstantBufferRange -> getBufferStorage(UNIFORM) ->
+    // garbageCollection. garbageCollection(UNIFORM) calls checkForDeallocation(STAGING). On the 9th
+    // slot, STAGING's idleness will be 9, exceeding the threshold of 8. checkForDeallocation would
+    // then attempt to delete the STAGING storage because latestStorage (SYSTEM_MEMORY) != storage
+    // (STAGING), however, it currently detects this and returns failure.
+    glDrawArrays(GL_POINTS, 0, 1);
+
+    // Now unmap. This calls Buffer11::unmap() which uses mMappedStorage and would formerly UAF on
+    // accessing mMappedBuffer.
+    glUnmapBuffer(GL_COPY_READ_BUFFER);
+
+    ASSERT_GL_ERROR(GL_INVALID_OPERATION);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SimpleUniformBufferTest);
+ANGLE_INSTANTIATE_TEST_ES3(SimpleUniformBufferTest);
+
 }  // namespace
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Use-After-Free in ANGLE D3D11 Buffer11 via STAGING storage deallocation

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.

Overview: A Use-After-Free vulnerability exists in ANGLE’s D3D11 backend when a mapped buffer’s staging storage is prematurely garbage-collected. This occurs if a read-only mapped buffer is bound to multiple uniform blocks and drawn, causing its idleness counter to exceed the deallocation threshold. An attacker could potentially exploit this from a compromised renderer to execute arbitrary code in the GPU process.

Affected files:

  • third_party/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp
  • third_party/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
  • third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp

Estimated timestamp from git blame: 2025-05-14

Vulnerability Details

In ANGLE’s D3D11 backend, Buffer11 manages multiple storage representations for a single GL buffer (e.g., SYSTEM_MEMORY, STAGING, UNIFORM). To optimize memory, Buffer11::garbageCollection periodically deallocates unused storages.

A Use-After-Free (UAF) condition can occur because the raw pointer Buffer11::mMappedStorage can be left dangling if its underlying storage object is reclaimed by garbage collection while the buffer is mapped. This relies on two distinct logic flaws:

  1. Priority Inversion in Mapping: When a buffer is mapped with GL_MAP_READ_BIT and its mLatestBufferStorage is SYSTEM_MEMORY, Buffer11::mapRange creates a STAGING storage to provide CPU access. However, because BUFFER_USAGE_STAGING (index 1) does not have a strictly lower priority index than BUFFER_USAGE_SYSTEM_MEMORY (index 0), mLatestBufferStorage remains pointing to SYSTEM_MEMORY. mMappedStorage is set to the new STAGING storage.
  2. Lack of UBO Mapping Validation: ANGLE’s draw-time validation (ValidateDrawStates) correctly prevents drawing if vertex or index buffers are mapped, but fails to validate the mapped state of Uniform Buffers (UBOs).

If a draw call is issued while the buffer is mapped and bound to multiple uniform blocks, StateManager11::syncUniformBuffersForShader iterates through the uniform bindings. For each binding, it calls Buffer11::getConstantBufferRange, which ultimately triggers garbageCollection and checkForDeallocation(BUFFER_USAGE_STAGING).

Each iteration increments mIdleness[BUFFER_USAGE_STAGING]. If the shader uses more than 8 uniform block bindings (the default deallocation threshold), the idleness exceeds the threshold. Because mLatestBufferStorage is SYSTEM_MEMORY, latestStorage != storage evaluates to true, and SafeDelete(storage) destroys the STAGING object.

mMappedStorage is completely unaware of this deletion. A subsequent call to glUnmapBuffer will trigger the virtual function mMappedStorage->unmap(), resulting in a virtual call on a freed object.

Potential Reproduction Steps

Note: These are suggested steps based on static analysis. Our tooling does not yet execute code to provide a working proof-of-concept.

  1. From a compromised renderer process, establish a WebGL2/GLES3 context that uses the passthrough command decoder (default on Windows for ANGLE D3D11).
  2. Create a GL buffer and initialize it with GL_DYNAMIC_DRAW or GL_STREAM_DRAW. This sets the internal D3D usage to dynamic, forcing ANGLE to use SYSTEM_MEMORY as the mLatestBufferStorage.
  3. Compile a shader program that actively uses at least 9 uniform block bindings.
  4. Bind the buffer to all 9 uniform block binding points (glBindBufferBase(GL_UNIFORM_BUFFER, 0 through 8)).
  5. Map the buffer for reading via MapBufferRange with GL_MAP_READ_BIT. ANGLE allocates STAGING storage and points mMappedStorage to it.
  6. Issue a DrawArrays command. ANGLE fails to validate that the UBO is mapped, allowing the draw to proceed.
  7. The loop in syncUniformBuffersForShader fetches the storage for each of the 9 bindings, calling garbageCollection 9 times.
  8. On the 9th iteration, mIdleness[STAGING] reaches 9, exceeding the threshold of 8. The STAGING storage is SafeDeleted.
  9. (Optional) Spray the GPU process heap to reclaim the freed NativeStorage object and counterfeit its vtable.
  10. Unmap the buffer (glUnmapBuffer). The GPU process executes mMappedStorage->unmap(), dereferencing the attacker’s payload and achieving Remote Code Execution in the GPU process.

Suggested Fix

There are multiple ways to address this issue:

  1. Prevent Deallocation of Mapped Storage: Update Buffer11::checkForDeallocation to explicitly prevent deleting the storage if it is currently mapped:
    if (storage != nullptr && storage != mMappedStorage && mIdleness[usage] > mDeallocThresholds[usage]) {
        // ... existing deletion logic ...
    }
    
  2. Validate UBO Mapped State: Update ANGLE’s top-level API validation (e.g., ValidateProgramDrawStates) to return an error (like kBufferMapped) if any active Uniform Buffer Object is currently mapped, bringing UBOs in line with vertex and index buffer validation.
  3. Modernize Pointers: ANGLE relies extensively on raw pointers. Migrating ANGLE’s pointers (like mMappedStorage) to base::raw_ptr would mitigate the exploitability of this and similar UAFs by transforming them into safe, non-exploitable crashes via MiraclePtr/BackupRefPtr.

Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b


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