CVE-2026-10994
Overview
Files Changed
src/libANGLE/renderer/gl/BlitGL.cppsrc/tests/gl_tests/RobustResourceInitTest.cpp
Patch
From 9f099a5d233f7c311d0f3d3651365e0979fcba91 Mon Sep 17 00:00:00 2001
From: Geoff Lang <geofflang@chromium.org>
Date: Wed, 22 Apr 2026 13:28:09 -0400
Subject: [PATCH] GL: Set stencil write mask before clearing for robust init
Fixed: chromium:504820809
Change-Id: I45f2746e3e4e239d9bf72219330484349d441723
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7785196
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
---
diff --git a/src/libANGLE/renderer/gl/BlitGL.cpp b/src/libANGLE/renderer/gl/BlitGL.cpp
index 0fec270..8d590ae 100644
--- a/src/libANGLE/renderer/gl/BlitGL.cpp
+++ b/src/libANGLE/renderer/gl/BlitGL.cpp
@@ -157,6 +157,7 @@
}
if (stencilClear)
{
+ stateManager->setStencilFrontWritemask(static_cast<GLuint>(-1));
stateManager->setClearStencil(0);
*outClearMask |= GL_STENCIL_BUFFER_BIT;
}
diff --git a/src/tests/gl_tests/RobustResourceInitTest.cpp b/src/tests/gl_tests/RobustResourceInitTest.cpp
index 340bdcd..1e05ed0 100644
--- a/src/tests/gl_tests/RobustResourceInitTest.cpp
+++ b/src/tests/gl_tests/RobustResourceInitTest.cpp
@@ -2214,6 +2214,10 @@
};
maskedStencilClear(clearFunc);
+
+ // Run the test twice. This is a regression test for state synchronization leaking into the
+ // clear operation.
+ maskedStencilClear(clearFunc);
}
// Test that clearing a masked stencil buffer doesn't mark it clean, with ClearBufferi.
Regression Test / PoC
diff --git a/src/tests/gl_tests/RobustResourceInitTest.cpp b/src/tests/gl_tests/RobustResourceInitTest.cpp
index 340bdcd..1e05ed0 100644
--- a/src/tests/gl_tests/RobustResourceInitTest.cpp
+++ b/src/tests/gl_tests/RobustResourceInitTest.cpp
@@ -2214,6 +2214,10 @@
};
maskedStencilClear(clearFunc);
+
+ // Run the test twice. This is a regression test for state synchronization leaking into the
+ // clear operation.
+ maskedStencilClear(clearFunc);
}
// Test that clearing a masked stencil buffer doesn't mark it clean, with ClearBufferi.
Original Bug Report
WebGL can disclose stale stencil contents because ANGLE robust framebuffer initialization honors attacker-controlled stencilMask(0)
Security Bug
VULNERABILITY DETAILS
ANGLE’s OpenGL robust-resource-initialization path can preserve stale stencil contents because the
internal clear helper in BlitGL::SetClearState() sets glClearStencil(0) but never restores the
front/back stencil write masks to all-ones before the later glClear(GL_STENCIL_BUFFER_BIT).
On the GL backend, FramebufferGL::ensureAttachmentsInitialized() routes first-use attachment
initialization through BlitGL::clearFramebuffer(). If attacker-controlled WebGL state has already
latched stencilMask(0) into the underlying GL state machine, the internal stencil clear can be
suppressed while ANGLE still marks the attachment initialized. A later stencil-tested draw can then
turn the stale stencil bits into script-readable color output.
The sink is still present in current source:
if (stencilClear)
{
stateManager->setClearStencil(0);
*outClearMask |= GL_STENCIL_BUFFER_BIT;
}
Source references:
third_party/angle/src/libANGLE/context_private_call.inl.hthird_party/angle/src/libANGLE/State.cppthird_party/angle/src/libANGLE/renderer/gl/StateManagerGL.cppthird_party/angle/src/libANGLE/Framebuffer.cppthird_party/angle/src/libANGLE/renderer/gl/FramebufferGL.cppthird_party/angle/src/libANGLE/renderer/gl/BlitGL.cpp
The attached browser PoC primes a fresh DEPTH24_STENCIL8 renderbuffer with stencil value 1,
deletes it, syncs stencilMask(0) on a separate initialized framebuffer, then allocates a new
depth-stencil attachment and triggers robust initialization on first use. It finally uses
stencil-tested drawing plus readPixels() to detect non-zero stale stencil state.
On the latest Chromium main tip I synced for this validation pass, the browser leak is immediate.
Importantly, on this Linux/NVIDIA system it reproduces without any backend-forcing flags such as
--use-gl=angle, --use-angle=gl, or --ignore-gpu-blocklist; Chromium’s default startup path
still selects ANGLE (NVIDIA Corporation, NVIDIA L4/PCIe/SSE2, OpenGL 4.5.0) for WebGL on this
machine. The first attempted victim allocation already produced sum(red)=4177920, which is the
full 128 x 128 x 255 frame, meaning the primed all-ones stencil pattern survived robust
initialization intact.
VERSION
Chrome Version: Chromium 149.0.7804.0 + dev (local ASan build from commit edf6e4e6f9d6427ecb954106f5b889f94c30de88)
Operating System: Ubuntu 22.04.5 LTS x86_64
GPU / GL renderer: ANGLE (NVIDIA Corporation, NVIDIA L4/PCIe/SSE2, OpenGL 4.5.0) on desktop Linux
REPRODUCTION CASE
Attachments:
poc.htmlstencil_browser_default_no_backend_flags_14978040.log
Numbered repro steps:
- Use a Chromium build configured to run WebGL through ANGLE’s GL backend on a real GPU-backed X server.
- Serve the attached PoC locally:
python3 -m http.server 8000
- Run Chromium directly. The following command includes only convenience flags for profile/log hygiene; it does not force a specific GL backend:
out/asan/chrome \
--user-data-dir=/tmp/chrome-stencil-poc \
--no-first-run \
--disable-background-networking \
--disable-default-apps \
--disable-sync \
--metrics-recording-only \
--enable-logging=stderr \
'http://127.0.0.1:8000/poc.html?iters=2000&logEvery=50&test=nonzero'
Observed result:
[poc] UNMASKED_RENDERER_WEBGL=ANGLE (NVIDIA Corporation, NVIDIA L4/PCIe/SSE2, OpenGL 4.5.0)
[poc] sync FBO prepared
[poc] LEAK observed iteration=0 sum(red)=4177920
The attached poc.html also supports the exact stencilFunc(EQUAL, 1, 0xFF) mode from the
minimal write-up, but the attached browser run uses the broader NOTEQUAL, 0 detection mode to maximize reliability when validating reused stencil contents.
Type of crash: N/A (stale stencil disclosure; no crash required)
Crash State:
N/A
Client ID (if relevant): N/A
CREDIT INFORMATION
Reporter credit: Mufeed VH from Winfunc Research (winfunc.com)