CVE-2026-9928
Overview
Files Changed
src/compiler.gnisrc/compiler/translator/glsl/TranslatorESSL.cppsrc/compiler/translator/glsl/TranslatorGLSL.cppsrc/compiler/translator/hlsl/TranslatorHLSL.cppsrc/compiler/translator/tree_ops/AddDefaultReturnStatements.cppsrc/compiler/translator/tree_ops/AddDefaultReturnStatements.h
Patch
From 917bb0e1d008c28be3f9ae70c7b71272997c2f1a Mon Sep 17 00:00:00 2001
From: Shahbaz Youssefi <syoussefi@chromium.org>
Date: Wed, 22 Apr 2026 13:39:06 -0400
Subject: [PATCH] Translator: Fix AddDefaultReturnStatements vs empty function body
This change simultaneously applies this transform for the GLSL generator
as well to make the test pass on mesa.
Bug: chromium:501125002
Change-Id: I11640d494eee79387649140bde07868614e61206
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7786014
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Yuxin Hu <yuxinhu@google.com>
---
diff --git a/src/compiler.gni b/src/compiler.gni
index d96767c..29c9c8e 100644
--- a/src/compiler.gni
+++ b/src/compiler.gni
@@ -121,6 +121,8 @@
"src/compiler/translator/msl/TranslatorMSL.h",
"src/compiler/translator/null/TranslatorNULL.h",
"src/compiler/translator/spirv/TranslatorSPIRV.h",
+ "src/compiler/translator/tree_ops/AddDefaultReturnStatements.cpp",
+ "src/compiler/translator/tree_ops/AddDefaultReturnStatements.h",
"src/compiler/translator/tree_ops/ClampFragDepth.cpp",
"src/compiler/translator/tree_ops/ClampFragDepth.h",
"src/compiler/translator/tree_ops/ClampIndirectIndices.cpp",
@@ -287,8 +289,6 @@
"src/compiler/translator/hlsl/UtilsHLSL.h",
"src/compiler/translator/hlsl/blocklayoutHLSL.cpp",
"src/compiler/translator/hlsl/emulated_builtin_functions_hlsl_autogen.cpp",
- "src/compiler/translator/tree_ops/hlsl/AddDefaultReturnStatements.cpp",
- "src/compiler/translator/tree_ops/hlsl/AddDefaultReturnStatements.h",
"src/compiler/translator/tree_ops/hlsl/ArrayReturnValueToOutParameter.cpp",
"src/compiler/translator/tree_ops/hlsl/ArrayReturnValueToOutParameter.h",
"src/compiler/translator/tree_ops/hlsl/BreakVariableAliasingInInnerLoops.cpp",
diff --git a/src/compiler/translator/glsl/TranslatorESSL.cpp b/src/compiler/translator/glsl/TranslatorESSL.cpp
index dc0c72e..b059caf 100644
--- a/src/compiler/translator/glsl/TranslatorESSL.cpp
+++ b/src/compiler/translator/glsl/TranslatorESSL.cpp
@@ -12,6 +12,7 @@
#include "compiler/translator/StaticType.h"
#include "compiler/translator/glsl/BuiltInFunctionEmulatorGLSL.h"
#include "compiler/translator/glsl/OutputESSL.h"
+#include "compiler/translator/tree_ops/AddDefaultReturnStatements.h"
#include "compiler/translator/tree_ops/DeclarePerVertexBlocks.h"
#include "compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h"
#include "compiler/translator/tree_ops/RecordConstantPrecision.h"
@@ -104,6 +105,11 @@
return false;
}
+ if (!sh::AddDefaultReturnStatements(this, root))
+ {
+ return false;
+ }
+
// anglebug.com/42265954: The ESSL spec has a bug with images as function arguments. The
// recommended workaround is to inline functions that accept image arguments.
if (shaderVer >= 310 && !MonomorphizeUnsupportedFunctions(
diff --git a/src/compiler/translator/glsl/TranslatorGLSL.cpp b/src/compiler/translator/glsl/TranslatorGLSL.cpp
index 9c82efa..f63098a 100644
--- a/src/compiler/translator/glsl/TranslatorGLSL.cpp
+++ b/src/compiler/translator/glsl/TranslatorGLSL.cpp
@@ -12,6 +12,7 @@
#include "compiler/translator/glsl/ExtensionGLSL.h"
#include "compiler/translator/glsl/OutputGLSL.h"
#include "compiler/translator/glsl/VersionGLSL.h"
+#include "compiler/translator/tree_ops/AddDefaultReturnStatements.h"
#include "compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h"
#include "compiler/translator/tree_ops/PreTransformTextureCubeGradDerivatives.h"
#include "compiler/translator/tree_ops/RemoveDynamicIndexing.h"
@@ -97,6 +98,11 @@
if (!compileOptions.useIR)
{
+ if (!sh::AddDefaultReturnStatements(this, root))
+ {
+ return false;
+ }
+
// anglebug.com/42265954: The ESSL spec has a bug with images as function arguments. The
// recommended workaround is to inline functions that accept image arguments.
if (getShaderVersion() >= 310 &&
diff --git a/src/compiler/translator/hlsl/TranslatorHLSL.cpp b/src/compiler/translator/hlsl/TranslatorHLSL.cpp
index 9a061cc..f44552c 100644
--- a/src/compiler/translator/hlsl/TranslatorHLSL.cpp
+++ b/src/compiler/translator/hlsl/TranslatorHLSL.cpp
@@ -7,11 +7,11 @@
#include "compiler/translator/hlsl/TranslatorHLSL.h"
#include "compiler/translator/hlsl/OutputHLSL.h"
+#include "compiler/translator/tree_ops/AddDefaultReturnStatements.h"
#include "compiler/translator/tree_ops/RemoveDynamicIndexing.h"
#include "compiler/translator/tree_ops/RewriteTexelFetchOffset.h"
#include "compiler/translator/tree_ops/SimplifyLoopConditions.h"
#include "compiler/translator/tree_ops/SplitSequenceOperator.h"
-#include "compiler/translator/tree_ops/hlsl/AddDefaultReturnStatements.h"
#include "compiler/translator/tree_ops/hlsl/ArrayReturnValueToOutParameter.h"
#include "compiler/translator/tree_ops/hlsl/BreakVariableAliasingInInnerLoops.h"
#include "compiler/translator/tree_ops/hlsl/ExpandIntegerPowExpressions.h"
@@ -57,9 +57,12 @@
int maxDualSourceDrawBuffers =
resources.EXT_blend_func_extended ? resources.MaxDualSourceDrawBuffers : 0;
- if (!sh::AddDefaultReturnStatements(this, root))
+ if (!compileOptions.useIR)
{
- return false;
+ if (!sh::AddDefaultReturnStatements(this, root))
+ {
+ return false;
+ }
}
// Note that SimplifyLoopConditions needs to be run before any other AST transformations that
diff --git a/src/compiler/translator/tree_ops/AddDefaultReturnStatements.cpp b/src/compiler/translator/tree_ops/AddDefaultReturnStatements.cpp
new file mode 100644
index 0000000..92fa40b
--- /dev/null
+++ b/src/compiler/translator/tree_ops/AddDefaultReturnStatements.cpp
@@ -0,0 +1,71 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// AddDefaultReturnStatements.cpp: Add default return statements to functions that do not end in a
+// return.
+//
+
+#include "compiler/translator/tree_ops/AddDefaultReturnStatements.h"
+
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+bool EndsInReturn(TIntermBlock *body)
+{
+ if (body->getSequence()->empty())
+ {
+ return false;
+ }
+
+ TIntermNode *lastStatement = body->getSequence()->back();
+ if (lastStatement->getAsBlock())
+ {
+ return EndsInReturn(lastStatement->getAsBlock());
+ }
+
+ TIntermBranch *returnNode = lastStatement->getAsBranchNode();
+ return returnNode != nullptr && returnNode->getFlowOp() == EOpReturn;
+}
+
+bool NeedsReturnStatement(TIntermFunctionDefinition *node, TType *returnType)
+{
+ *returnType = node->getFunctionPrototype()->getType();
+ if (returnType->getBasicType() == EbtVoid)
+ {
+ return false;
+ }
+
+ TIntermBlock *bodyNode = node->getBody();
+ return !EndsInReturn(bodyNode);
+}
+
+} // anonymous namespace
+
+bool AddDefaultReturnStatements(TCompiler *compiler, TIntermBlock *root)
+{
+ TType returnType;
+ for (TIntermNode *node : *root->getSequence())
+ {
+ TIntermFunctionDefinition *definition = node->getAsFunctionDefinition();
+ if (definition != nullptr && NeedsReturnStatement(definition, &returnType))
+ {
+ TIntermBranch *branch = new TIntermBranch(EOpReturn, CreateZeroNode(returnType));
+
+ TIntermBlock *bodyNode = definition->getBody();
+ bodyNode->getSequence()->push_back(branch);
+ }
+ }
+
+ return compiler->validateAST(root);
+}
+
+} // namespace sh
diff --git a/src/compiler/translator/tree_ops/AddDefaultReturnStatements.h b/src/compiler/translator/tree_ops/AddDefaultReturnStatements.h
new file mode 100644
index 0000000..885b9a1
--- /dev/null
+++ b/src/compiler/translator/tree_ops/AddDefaultReturnStatements.h
@@ -0,0 +1,24 @@
Regression Test / PoC
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index 82c07b6..a6cef45 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -23121,6 +23121,27 @@
EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(127, 127, 127, 127), 1);
ASSERT_GL_NO_ERROR();
}
+
+// Regression test for a transformation bug where a function has |return| only in dead code.
+TEST_P(GLSLTest_ES3, EmptyBodyAfterPrunedIfWithReturn)
+{
+ constexpr char kFS[] = R"(#version 300 es
+precision mediump float;
+out vec4 color;
+
+int foo() {
+ if (false) { return 1; }
+}
+
+void main() {
+ color = vec4(float(foo()), 0, 1, 1);
+})";
+
+ ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
+ drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
+ ASSERT_GL_NO_ERROR();
+}
} // anonymous namespace
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND_ES31_AND_ES32(
Original Bug Report
Heap buffer underflow read with wild virtual call in [@ sh::AddDefaultReturnStatements] via empty function body in TranslatorHLSL
Report description
Heap buffer underflow read with wild virtual call in [@ sh::AddDefaultReturnStatements] via empty function body in TranslatorHLSL
Bug location
Where do you want to report your vulnerability?
Chrome VRP – Report security issues affecting the Chrome browser. See program rules
Which URL (or repository) have you found the vulnerability in?
https://chromium.googlesource.com/angle/angle
The problem
Please describe the technical details of the vulnerability
The parser sets mFunctionReturnsValue=true when it lexes “return 1;” inside the
if-block, but then addIfElse sees the constant false condition and prunes the entire block.
appendStatement skips the resulting nullptr, leaving the function body empty. The
addFunctionDefinition check passes because mFunctionReturnsValue is already true.
AddDefaultReturnStatements then calls bodyNode->getSequence()->back() without an empty check, reading out of bounds. The out of bounds read is then used a TIntermNode* with a virtual call to TIntermBranch *getAsBranchNode()
See attached for a shader compilation test that crashes.
Our bug for this is https://bugzilla.mozilla.org/show_bug.cgi?id=2029814. It has a bunch more detail that might only be relevant to Firefox but I can add you if you’d like.
Impact analysis
Potentially remote execution in the GPU process on Windows.
The cause
What version of Chrome have you found the security issue in?
Stable
Is the security issue related to a crash?
No, it is not related to a crash.
Choose the type of vulnerability
Remote Code Execution (RCE)
How would you like to be publicly acknowledged for your report?
Jeff Muizelaar - Mozilla