CVE-2026-113200
Overview
Background
- Fragment outputs narrower than vec4
- unwritten components are undefined.
- ExpandFragmentOutputsToVec4
- the new transform padding outputs to gvec4 with zeros.
- Hardened contexts
- where undefined values are a security concern.
Root Cause Analysis
The commit “Always expand non-vec4 fragment outputs to vec4” makes ANGLE expand any fragment shader output narrower than 4 components (e.g. float/vec2/vec3 outputs) to a full gvec4, filling the missing components with 0, on hardened contexts for all backends. The commit message states the problem: “The alpha channel is undefined if unwritten, which leads to undefined values produced when blending.” Previously this was only a per-driver workaround (expand_fragment_outputs_to_vec4); the fix promotes it to a general transform (new ExpandFragmentOutputsToVec4 C++ tree-op and expand_fragment_outputs_to_vec4 Rust IR transform) run whenever expandFragmentOutputsToVec4 is set and the shader is ESSL 3.00+.
The root cause is therefore use of undefined/uninitialized fragment-output components: an unwritten alpha (or other missing component) carries an undefined value that is then consumed by blending and read back, which can expose uninitialized GPU register/memory contents. This CVE (bug chromium:563350676) shares the fix; the advisory labels it “use after free,” but the diff is a shader-output expansion transform with no allocation, free, or pointer-lifetime change anywhere. There is no UAF in this patch. Reclassified to UninitializedMemory (undefined/uninitialized fragment-output components). (If a genuine UAF were intended by this CVE, it is not in the mapped commit — but the commit explicitly lists bug 563350676, so the fix is the output-expansion change.)
Attack Path
- ENTRY WebGL fragment shader (ESSL 3.00+) with a non-vec4 output, hardened context.
- CONTROLLED INPUT Leave a component unwritten (undefined).
- MISSING CHECK Missing components are not zero-filled before use.
- IMPACT Undefined values are consumed by blending / read back — uninitialized-data exposure, not a use-after-free.
Impact Assessment
Files Changed
src/compiler/translator/ir/src/transform.rssrc/libANGLE/renderer/gl/ShaderGL.cppsrc/libANGLE/renderer/gl/renderergl_utils.cppinclude/platform/autogen/FeaturesGL_autogen.hutil/autogen/angle_features_autogen.cppsrc/compiler.gnisrc/tests/angle_end2end_tests_expectations.txtsrc/compiler/translator/Compiler.cppsrc/compiler/translator/glsl/TranslatorGLSL.cppsrc/compiler/translator/ir/src/transform/expand_fragment_outputs_to_vec4.rssrc/compiler/translator/ir/src/ir.rssrc/tests/gl_tests/WebGLCompatibilityTest.cppsrc/compiler/translator/ir/src/transform/emulate_multi_draw.rssrc/compiler/translator/glsl/TranslatorESSL.cppsrc/compiler/translator/ir/BUILD.gnsrc/libANGLE/Shader.cpputil/autogen/angle_features_autogen.hsrc/compiler/translator/tree_ops/glsl/ExpandFragmentOutputsToVec4.hsrc/compiler/translator/tree_ops/ExpandFragmentOutputsToVec4.cppsrc/compiler/translator/ir/src/compile.rssrc/compiler/translator/ir/src/compile.cppsrc/tests/gl_tests/GLSLTest.cppsrc/compiler/translator/tree_ops/ExpandFragmentOutputsToVec4.hinclude/platform/gl_features.jsonsrc/compiler/translator/ir/src/transform/emulate_instanced_multiview.rs
Audit Directions
- Verify the advisory↔commit natureThe “UAF” label conflicts with the mapped commit; confirm no separate use-after-free fix exists under bug 563350676.
- Undefined output componentsAs for the sibling CVEs, audit unwritten shader outputs consumed downstream.
Original Bug Report
Information leak of GPU register residue via sub-vec4 fragment outputs in ANGLE GL backend
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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: ANGLE’s GL/GLES backend has a translator pass, ExpandFragmentOutputsToVec4, whose purpose is to zero-fill the undeclared channels of sub-vec4 fragment shader outputs so indeterminate GPU register contents cannot reach color attachments. Its only enabling site gates it to isAMD && isMesa as a driver workaround, so on every other GL/GLES driver — Linux desktop defaults (NVIDIA proprietary, Intel Mesa, AMD proprietary) and Android’s default ANGLE-on-GLES path plus the validating-decoder fallback — a plain WebGL2 page declaring out vec2 c deposits undefined data into the .zw channels of an RGBA attachment and reads it back with gl.readPixels(). The WebGL hardening that does run everywhere (initOutputVariables) zero-initializes only the declared 2-component variable and cannot touch the undeclared hardware channels. ANGLE fixed the identical mechanism unconditionally on Metal, citing “indeterminate GPU register residue leaking into color attachments”.
Affected files:
third_party/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp:2725-2726— sole enabler of the hardening, gatedisAMD && isMesathird_party/angle/src/libANGLE/renderer/gl/ShaderGL.cpp:131-136,274-277— hardened contexts get only declared-arityinitOutputVariables; expansion only via the vendor-gated featurethird_party/angle/src/compiler/translator/tree_ops/glsl/ExpandFragmentOutputsToVec4.cpp— the sanitization pass itself (zero-fills missing channels)third_party/angle/src/compiler/translator/glsl/TranslatorGLSL.cpp:141-147,third_party/angle/src/compiler/translator/glsl/TranslatorESSL.cpp:131-137— pass wired into both GL-family translators, but only under the compile optiongpu/command_buffer/service/shader_translator.cc:123-135— Android validating-decoder fallback setsinitOutputVariablesbut has no reference to the expansion option
Estimated timestamp from git blame: 2026-05-01
Summary
WebGL2 allows fragment shaders to declare sub-vec4 outputs (out vec2 c) and draw into 4-channel attachments; per OpenGL ES 3.2 §14.2.3, the unwritten channels are undefined (“Any fragment colors which are not written by the fragment shader are undefined”). ANGLE hardens every WebGL context (Context.cpp:729: mHardenedContext(mWebGLContext || ...)), and on hardened contexts ShaderGL.cpp:131-136 sets initOutputVariables — but TCompiler::initializeOutputVariables (Compiler.cpp:1674-1751) emits only c = vec2(0) at declared arity, leaving the .zw hardware output-register channels untouched.
The pass that actually closes the gap, ExpandFragmentOutputsToVec4, rewrites the output as a real vec4 and explicitly zero-fills the missing channels. It is enabled by exactly one site in the tree, as a driver-bug workaround (gl_features.json category “Workarounds”, crbug.com/491732188 / crbug.com/498828605):
// third_party/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp:2725-2726
// http://crbug.com/498828605
ANGLE_FEATURE_CONDITION(features, expandFragmentOutputsToVec4, isAMD && isMesa);
Every !(isAMD && isMesa) GL/GLES configuration is therefore unhardened, including Intel Mesa (isMesa but not isAMD), NVIDIA and AMD proprietary on Linux, and all Android GLES vendors (Adreno, Mali, PowerVR, Xclipse). No other layer compensates: draw validation checks output presence and component type but not arity (validationES.cpp:445-471), robust resource initialization clears attachments only at creation (the leak is deposited by the draw itself), and no arity-derived color-mask logic exists in the GL backend.
This was demonstrated end-to-end from plain web content in a stock Chromium build on Mesa llvmpipe (an isMesa && !isAMD configuration, i.e. in the vulnerable population):
- Structural: the web-exposed
WEBGL_debug_shadersextension shows the translated shader ANGLE hands the native driver still declaresout vec2on both the desktop-GL backend (TranslatorGLSL, Linux default) and the native-GLES backend (TranslatorESSL, the Android-default path); the only sanitization present is the declared-arity zero-init. - Value: after clearing an RGBA8 (and RGBA32F) FBO to a sentinel, a single
out vec2draw overwrote the never-written.ba/.zwchannels of all 4096 pixels, andreadPixels()returned them — undefined-channel data reaches web content. - Control: the same page with
--enable-angle-features=expandFragmentOutputsToVec4on the same driver flips the translated source to vec4 with explicit.z = 0; .w = 0fill, confirming the vendor gate is the only difference between vulnerable and hardened.
One caveat is hardware-dependent and could not be sampled in a GPU-less environment: what the undefined channels actually contain per driver. On llvmpipe they deterministically resolve to zero (benign on that software driver). Evidence that real hardware emits register residue rather than benign constants: ANGLE fixed this exact mechanism unconditionally on Metal with the comment “to avoid indeterminate GPU register residue leaking into color attachments” (compiler/translator/msl/ModifyStruct.cpp:885-887), and the AMD+Mesa workaround exists because unwritten channels produced observable garbage there (the pass’s blend-focused test suite reflects the same symptom). Which of NVIDIA/Intel/Adreno/Mali emit residue, and whether that residue crosses GPU-context (hence origin) boundaries, must be confirmed on hardware.
Root Cause Analysis
- WebGL contexts are hardened, but hardening only zero-inits declared arity:
ShaderGL.cpp:131-136setsoptions->initOutputVariablesfor every hardened context;Compiler.cpp:1674-1751initializes the declared variables only (c = vec2(0)), never the undeclared.zwhardware channels of the color-output register. - The pass that would close the gap is vendor-gated:
renderergl_utils.cpp:2725-2726enablesexpandFragmentOutputsToVec4only forisAMD && isMesa;ShaderGL.cpp:274-277copies the feature into the compile option. A tree-wide search confirms this is the sole enabler — nothing undergpu/configorgpu/command_bufferreferences it. - Both translators honor only the option:
TranslatorGLSL.cpp:141-147andTranslatorESSL.cpp:131-137runExpandFragmentOutputsToVec4solely undercompileOptions.expandFragmentOutputsToVec4, so on all other drivers the native driver receives a fragment shader writing only.xyof color output 0:// translated output observed via WEBGL_debug_shaders, default flags (vulnerable) #version 450 out vec2 _uc; void main(){ (_uc = vec2(0.0, 0.0)); (_uc = vec2(0.25, 0.5)); } - No downstream layer rejects or masks the arity mismatch:
validationES.cpp:445-460checks only that an active output variable exists per draw buffer;validationES.cpp:462-471compares float/int/uint type masks, not component counts.out vec2→ RGBA8 draws are legal GL and validate cleanly; the output merger then writes all four attachment channels from the 4-wide output register. - Android validating-decoder fallback has the same gap:
gpu/command_buffer/service/shader_translator.cc:123-135setsinitOutputVariablesfor itsSH_ESSL_OUTPUTtranslators but has no expansion option, so devices blocklisted from the passthrough decoder are equally exposed. - The Metal backend fixed this same mechanism unconditionally:
compiler/translator/msl/ModifyStruct.cpp:885-896zero-initializes saturated output components for all compilations, with no vendor gate — the GL/GLES backend is the unfixed sibling.
Reproduction
- On a GL-backend configuration where
!(isAMD && isMesa)— e.g. Linux desktop Chrome defaults on NVIDIA/Intel-Mesa/AMD-proprietary, Android Chrome on the ANGLE GLES backend, or any Mesa-llvmpipe environment — open a page that creates a WebGL2 context. - Create an FBO with an RGBA8 attachment (or RGBA32F with
EXT_color_buffer_floatfor raw 32-bit channel bits) and clear it to a sentinel color. - Compile and draw a fullscreen triangle with fragment shader
#version 300 es\nprecision mediump float;\nout vec2 c;\nvoid main(){ c = vec2(0.25, 0.5); }— compilation, linking, and draw validation all succeed. - Call
gl.readPixels()and inspect bytes 2–3 (.ba) of each pixel. Optionally dump the translated shader viaWEBGL_debug_shadersto confirm the output is still declaredvec2. - Expected: a correct implementation writes deterministic zeros to the unwritten channels (as the pass does where enabled, and as the Metal backend does unconditionally).
Observed (executed in a stock build on Mesa llvmpipe, both default flags and --use-angle=gles): the translated shader still declares out vec2; the draw overwrote the sentinel in the never-written .ba channels of all 4096 pixels and readPixels() returned them. On this software driver the deposited values resolve to zeros; per GLES 3.2 §14.2.3 the contents are undefined and driver-dependent, so on hardware register files they may carry residue of prior GPU work — this half is stated as hardware-dependent, not demonstrated.
Severity
medium — Read-only information disclosure reachable from any WebGL2 page with no user interaction and no compromised renderer, across broad default populations (Linux desktop GL, Android GLES); no memory corruption. The sanitization gap and the web-readable deposit of undefined channel data are code- and POC-confirmed; what keeps this at medium rather than high (“GPU-process info leak”) is that the disclosed entity is GPU hardware register state rather than GPU-process address-space memory, and cross-origin provenance of the residue is unproven per hardware family — the one locally testable driver yields benign zeros. Triage may reasonably weigh a promotion to high on Android, where the GPU process is unsandboxed and shared across all origins and register residue could in principle originate from other origins’ GPU work; the identical-mechanism Metal fix was triaged medium, and consistency with that precedent is applied here. Prior art: the June-2026 “Uninitialized Use in ANGLE” CVE wave (e.g. CVE-2026-11087, fixed in 149.0.7827.53) and CVE-2026-8020 (Uninitialized Use in GPU, Chrome on Android) show this class is consistently security-triaged.
Suggested Fix Direction
Key the expansion on context trust level instead of driver vendor: in ShaderGL.cpp, set options->expandFragmentOutputsToVec4 = true for fragment shaders whenever the context is hardened (next to the existing initOutputVariables hardening), or fold the expansion into the initOutputVariables contract in TCompiler. The validating-decoder path (gpu/command_buffer/service/shader_translator.cc) needs the same option for parity on Android. A vendor-independent patch of this shape was build- and behavior-verified (hardened output on both backends, rendering unaffected). Note: a separately filed report covers a copy-back/shadowing defect inside ExpandFragmentOutputsToVec4 itself on the one configuration where it currently runs (AMD+Mesa); both need fixing for attachment-channel sanitization to be effective across the GL/GLES backend.
POC: ANGLE GL/GLES — ExpandFragmentOutputsToVec4 WebGL hardening gated to AMD+Mesa; all other GL drivers leave undefined sub-vec4 fragment output channels readable via WebGL2 readPixels
Strategy: web-content Bug Type: uninitialized-data information disclosure (CWE-908) — no memory corruption, so no ASan report is expected; proof is by observation oracles from plain web content (zero code modification).
ANGLE’s ExpandFragmentOutputsToVec4 translator pass exists to zero-fill the
undeclared components of sub-vec4 fragment shader outputs so indeterminate
register contents cannot reach color attachments. Its only enabling site is
ANGLE_FEATURE_CONDITION(features, expandFragmentOutputsToVec4, isAMD && isMesa)
(third_party/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp:2726).
On every other GL/GLES driver, a WebGL2 shader declaring out vec2 c hands the
native driver a 2-component output; the output merger still writes all four
attachment channels from the 4-wide output register, and readPixels() returns
the two channels the shader never wrote.
What the POC does
poc.html + poc.js (plain web content, WebGL2 only) run three oracles:
- Structural oracle — dumps the exact GLSL ANGLE handed to the native
driver via the web-exposed
WEBGL_debug_shadersextension. Vulnerable configuration: output still declaredvec2(the pass did not run) — the only sanitization visible isinitOutputVariables’ declared-arity-only zero-init. Hardened configuration: output expanded tovec4with explicit.z = 0.0; .w = 0.0fill. - Value oracle — clears an RGBA8 (and RGBA32F) FBO to a sentinel
(
0x11,0x22,0x33,0x44), draws one fullscreen triangle without vec2 c; c = vec2(0.25, 0.5);, reads back, and inspects the.b/.achannels the shader never assigned. Any.ba != sentinelproves the draw deposited data into channels the shader never wrote. - Residue probes — MRT sibling-value trials, cross-draw secret priming, a register-pressure shader variant, and a blending amplifier, to sample what the undefined channels actually contain on the local driver.
Prerequisites
- Chromium build (ASAN build used here:
out/asan/chrome). - X display; the GL driver in this environment is Mesa llvmpipe
(
isMesa && !isAMD), which is squarely in the vulnerable population — the same gate outcome as Intel Mesa, NVIDIA proprietary, AMD proprietary, Adreno, Mali, etc.
Reproduction
cd /poc-artifacts && python3 server.py & # serves poc.html on :8080
# Run 1 - DEFAULT flags (vulnerable configuration; ANGLE GL backend / TranslatorGLSL)
/chromium_src/src/out/asan/chrome \
--no-first-run --no-default-browser-check --ignore-gpu-blocklist \
--no-proxy-server --user-data-dir=/tmp/chrome-poc-default \
'http://localhost:8080/poc.html?run=default'
# Run 2 - ANGLE native-GLES backend (TranslatorESSL, the Android-default path)
# same vulnerable outcome:
# ... --use-gl=angle --use-angle=gles 'http://localhost:8080/poc.html?run=gles-llvmpipe'
# Run 3 - CONTROL: force-enable the (normally AMD+Mesa-only) feature to show
# what the hardening does when it runs:
# ... --enable-angle-features=expandFragmentOutputsToVec4 \
# 'http://localhost:8080/poc.html?run=hardened'
Each run POSTs its full findings back to server.py, which writes
results-<run>.json (the files committed here are from actual runs). The page
also renders the same JSON.
--no-proxy-server works around this sandbox’s CLI proxy env;
--ignore-gpu-blocklist keeps the real GPU process on the (software) GL
driver; neither affects the shader-translation behavior under test.
Expected Result
Vulnerable (default flags, and --use-angle=gles):
results-default.json / results-gles-llvmpipe.json
translatedSource.verdict = "VULNERABLE: output still declared vec2 - ExpandFragmentOutputsToVec4 did NOT run"experiments.rgba8.afterDraw.baStillSentinel = 0(all 4096 pixels’.baoverwritten by a shader that never wrote those channels)
Hardened (feature force-enabled, or with bandaid-fix.patch applied):
results-hardened.json, results-bandaid-fixed*.json
translatedSource.verdict = "HARDENED: output expanded to vec4 (zero-fill pass ran)"
Output (from the committed runs)
Renderer (both backends confirmed ANGLE-on-Mesa-llvmpipe, i.e. isMesa && !isAMD):
default: ANGLE (Mesa/X.org, llvmpipe (LLVM 15.0.6 256 bits), OpenGL 4.5)
gles-llvmpipe: ANGLE (Mesa/X.org, llvmpipe (LLVM 15.0.6 256 bits), OpenGL ES 3.2)
Translated fragment shader ANGLE handed to the native driver — default run
(vulnerable). Note the output is still vec2, and the only hardening applied
(initOutputVariables, first assignment) covers the declared components only:
#version 450
out vec2 _uc;
void main(){
(_uc = vec2(0.0, 0.0));
(_uc = vec2(0.25, 0.5));
}
Same page, --use-angle=gles (TranslatorESSL — the Android-default backend):
#version 300 es
out mediump vec2 _uc;
void main(){ (_uc = vec2(0.0, 0.0)); (_uc = vec2(0.25, 0.5)); }
Same page, control run with --enable-angle-features=expandFragmentOutputsToVec4
— this is the sanitization every WebGL context should get, and the ONLY
difference is the isAMD && isMesa feature gate:
#version 450
vec2 sbbb;
out vec4 _uc;
void main(){
(sbbb = vec2(0.0, 0.0));
(sbbb = vec2(0.25, 0.5));
{
(_uc.xy = sbbb);
(_uc.z = 0.0);
(_uc.w = 0.0);
}
}
Value oracle (default run, results-default.json): the sentinel-cleared
attachment read back [17,34,51,68,...] before the draw; after the vec2-output
draw all 4096 pixels read [64,128,0,0] — .rg are the shader’s values,
.ba were overwritten by the draw (sentinel destroyed) with data the
shader never wrote:
"afterDraw": { "pixels": 4096, "rgWrittenCorrectly": 4096,
"baStillSentinel": 0, "baZero": 4096, "baOtherValue": 0 }
What the undefined channels contain on this driver
On this specific software driver (Mesa 22.3.6 llvmpipe), the undefined
channels deterministically resolve to zero bits (RGBA8 .ba = 0x00,0x00;
RGBA32F .zw = 0x00000000), in all probes (simple/complex shaders, MRT
sibling secrets, cross-draw priming, blend amplifier). So on llvmpipe the
unsanitized deposit is benign in content. The security-relevant facts the POC
proves end-to-end from web content are:
- ANGLE’s sanitization pass does NOT run for WebGL contexts on any
!(isAMD && isMesa)driver (structural oracle, both GL and GLES backends); - the native driver’s output merger DOES write the never-written channels
into the attachment, and
readPixels()returns them (value oracle); - what fills those channels is left entirely to the driver — per
GLES 3.2 §14.2.3 it is undefined. Which drivers emit register residue
rather than benign constants is hardware-dependent and cannot be sampled in
this GPU-less environment; ANGLE’s own tree documents residue on real
hardware (the unconditional Metal fix for the same mechanism cites
“indeterminate GPU register residue leaking into color attachments”,
compiler/translator/msl/ModifyStruct.cpp:885-887, and the AMD+Mesa workaround exists because unwritten channels produced observable garbage there).
Environment notes: Mesa softpipe was also tried as a second local driver but cannot host a Chromium GPU process at all (desktop GL capped at 3.3 / ES context creation fails), so llvmpipe is the only locally testable driver. Windows (D3D11) and macOS (Metal) defaults do not use this backend and are out of scope of this finding.
Bandaid Fix
bandaid-fix.patch (to third_party/angle/src/libANGLE/renderer/gl/ShaderGL.cpp)
keys the expansion on context trust level instead of driver vendor: set
options->expandFragmentOutputsToVec4 = true for fragment shaders on all
hardened (WebGL) contexts, next to the existing initOutputVariables
hardening.
Verified: with the patch applied (autoninja -C out/asan chrome, ANGLE is
statically linked into libgl_init.so), the same POC page with default
flags reports HARDENED on both backends — output expanded to vec4 with
.z/.w zero-filled — and rendering is unaffected (rgWrittenCorrectly: 4096/4096). See results-bandaid-fixed.json and
results-bandaid-fixed-gles.json. The validating-decoder path
(gpu/command_buffer/service/shader_translator.cc) needs the same option for
parity on Android; the patch covers the ANGLE (passthrough) path demonstrated
here.
Files
| File | Description |
|---|---|
poc.html, poc.js |
The POC page (plain web content) |
server.py |
Serves the page, stores POSTed results |
results-default.json |
Stock build, default flags — vulnerable (GL backend) |
results-gles-llvmpipe.json |
Stock build, --use-angle=gles — vulnerable (GLES backend) |
results-hardened.json |
Stock build + --enable-angle-features=expandFragmentOutputsToVec4 — control |
results-bandaid-fixed.json |
Patched build, default flags — hardened |
results-bandaid-fixed-gles.json |
Patched build, --use-angle=gles — hardened |
bandaid-fix.patch |
Vendor-independent hardening fix (verified) |
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.
- http://crbug.com/498828605
- http://localhost:8080/poc.html?run=default
- http://localhost:8080/poc.html?run=gles-llvmpipe
- http://localhost:8080/poc.html?run=hardened
- https://crbug.com/491732188
- https://crbug.com/498828605
- https://source.chromium.org/chromium/chromium/src/+/main:compiler/translator/msl/ModifyStruct.cpp;l=885
- https://source.chromium.org/chromium/chromium/src/+/main:gpu/command_buffer/service/shader_translator.cc
- https://source.chromium.org/chromium/chromium/src/+/main:gpu/command_buffer/service/shader_translator.cc;l=123
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/compiler/translator/glsl/TranslatorESSL.cpp;l=131
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/compiler/translator/glsl/TranslatorGLSL.cpp;l=141
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/compiler/translator/tree_ops/glsl/ExpandFragmentOutputsToVec4.cpp
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/libANGLE/renderer/gl/ShaderGL.cpp
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/libANGLE/renderer/gl/ShaderGL.cpp;l=131
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp;l=2725
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp;l=2726