Overview

Critical
Severity
9.6
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in ANGLE
DescriptionUse after free in ANGLE
ComponentANGLE
Sub-componentGL
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker496807874
Fix commit311c502e88d9 (angle/angle) +283/-10
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoodluck
Reported2026-03-27 · bounty $25,000 (per vendor advisory)
Fix landed2026-07-10 (public git)
Disclosed2026-08-25
Patch gap46 days — fix public in source before the advisory

Background

ANGLE
Almost Native Graphics Layer Engine, Chrome’s translation layer that maps WebGL/OpenGL ES calls onto native drivers (here, the GL backend in src/libANGLE/renderer/gl).
PBO
Pixel Buffer Object, a GPU-side buffer bound to GL_PIXEL_UNPACK_BUFFER; when bound, the pixels argument to glTexSubImage2D is a byte offset into that buffer instead of a client pointer.
`TextureGL::setSubImage`
the GL-backend path that services glTexSubImage2D-style uploads, including the fast path that hands the entire region to the native driver in one call.
`splitLevel0PboFullSubImage2D`
a newly added PowerVR-only ANGLE feature flag that, when enabled, forces full-level-0 PBO uploads to a 2D texture to be split into two smaller driver calls.

Root Cause Analysis

On Imagination (PowerVR) drivers, a single glTexSubImage2D that uploads the entire level 0 of a 2D texture sourced from a bound PBO takes a driver-internal fast path that miscomputes the bounds of the transfer, reading or writing past the intended region of the mapped PBO or texture storage. Because the source of the copy is a PBO that ANGLE manages, the driver’s out-of-bounds access lands against the transfer buffer, producing a use-after-free / OOB condition inside driver-controlled memory. ANGLE previously issued exactly this full-image PBO upload as one call whenever the region matched the full mip level, directly triggering the buggy path.

The fix detects the full-image, level-0, PBO-backed, UseTexImage2D case and, when features.splitLevel0PboFullSubImage2D.enabled, reroutes it through setSubImageRowByRowWorkaround, which uploads everything except the final line/pixel and then the final line/pixel as a separate call. Splitting the upload keeps the driver off its buggy monolithic fast path, so no access exceeds the intended bounds. The reused helper was enhanced to handle height-1 uploads so that the trailing-remainder call is well-formed even for single-row images.

Key insight
The vulnerability is not in ANGLE’s own bounds logic but in a driver fast path that only fires for a single full-level-0 PBO upload; breaking that one upload into two smaller uploads that cover the same bytes is sufficient to keep the driver out of the miscomputing code path.

Attack Path

  1. Reach ANGLE from the web A page uses WebGL on a device with an Imagination/PowerVR GPU, so texture uploads route through ANGLE’s GL backend.
  2. Bind a PBO Script binds a pixel unpack buffer and issues texSubImage2D/texImage2D for level 0 of a 2D texture with the region covering the entire mip level.
  3. Trigger the fast path ANGLE forwards the full-image PBO upload as a single glTexSubImage2D, and the driver enters the buggy path that reads/writes past the transfer buffer.
  4. Corrupt driver memory The out-of-bounds access against the freed/mapped PBO-backed storage yields a use-after-free, which a crafted sequence of allocations can shape.
  5. Escalate Controlled corruption of driver/GPU-process memory is leveraged toward information disclosure or code execution in the GPU process.

Impact Assessment

A malicious web page could trigger a use-after-free in the Imagination GPU driver via ANGLE’s full-level-0 PBO upload path, corrupting memory in the GPU process. Depending on allocator state this could enable information disclosure or memory-corruption primitives leading to code execution within the sandboxed GPU process. The issue is confined to devices using PowerVR/Imagination drivers, matching the PowerVR-scoped feature flag.

Changed Functions

FunctionChangeNotes
Texture2DTestES3PBO
src/tests/gl_tests/TextureTest.cpp
modified
Texture2DTestES3Foveation
src/tests/gl_tests/TextureTest.cpp
modified

Files Changed

  • src/tests/gl_tests/TextureTest.cpp
  • src/libANGLE/renderer/gl/renderergl_utils_unittest.cpp
  • util/autogen/angle_features_autogen.h
  • src/libANGLE/renderer/gl/renderergl_utils.cpp
  • include/platform/autogen/FeaturesGL_autogen.h
  • util/autogen/angle_features_autogen.cpp
  • src/libANGLE/renderer/gl/TextureGL.cpp
  • src/libANGLE/renderer/gl/renderergl_utils.h
  • src/tests/angle_unittests.gni
  • include/platform/gl_features.json

Audit Directions

  • Driver fast-path avoidance
    Audit other ANGLE upload/download paths (texImage, copyTexSubImage, compressed uploads, 3D/array textures) where a single full-region PBO transfer is handed to the native driver, since analogous monolithic fast paths on PowerVR may miscompute bounds.
  • Boundary sizes in split helpers
    Review setSubImageRowByRowWorkaround and similar chunking helpers for correctness at edge dimensions (height-1, width-1, single-pixel), where remainder/last-line arithmetic and rowBytes computation are easiest to get wrong.
  • Feature-gate coverage
    Verify the isFullImage, level == 0, unpackBuffer, and nativegl::UseTexImage2D(getType()) conditions actually cover every way a full-level-0 2D PBO upload can reach the driver, so no aliasing entry point bypasses splitLevel0PboFullSubImage2D.
From 311c502e88d95d982295789f9d3cdc30e3d9bcf4 Mon Sep 17 00:00:00 2001
From: Ken Russell <kbr@chromium.org>
Date: Wed, 08 Jul 2026 14:08:48 -0700
Subject: [PATCH] GL: workaround: split PBO uploads to 2D textures' level 0.

To work around a bug in Imagination's drivers, if uploading the entire
level 0 of a 2D texture from a PBO, split the upload into two:
everything except the last line/pixel, and the last line/pixel. Reuse
setSubImageRowByRowWorkaround, and enhance it to support uploads of
height 1. Add PowerVR driver version support to workarounds, and add
unit test for parsing it. Add a new gl_test verifying the workaround.

Co-authored with Gemini with feedback from Imagination, Shahbaz
Youssefi and Amirali Abdolrashidi.

Bug: chromium:496807874
Change-Id: I598aeeb14fdaeaef2f0d96e566235e57582ee15f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8027554
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Amirali Abdolrashidi <abdolrashidi@google.com>
---
diff --git a/include/platform/gl_features.json b/include/platform/gl_features.json
index 49890e1..73fdf1d 100644
--- a/include/platform/gl_features.json
+++ b/include/platform/gl_features.json
@@ -925,6 +925,14 @@
                 "Disable the EXT_clear_texture extension due to driver bugs."
             ],
             "issue": "http://crbug.com/475877320"
+        },
+        {
+            "name": "split_level_0_pbo_full_sub_image_2D",
+            "category": "Workarounds",
+            "description": [
+                "Split full-image level 0 PBO uploads via TexSubImage2D into two calls to work around driver bugs."
+            ],
+            "issue": "http://crbug.com/496807874"
         }
     ]
 }
diff --git a/src/libANGLE/renderer/gl/TextureGL.cpp b/src/libANGLE/renderer/gl/TextureGL.cpp
index 47ebe88..b4fd17d 100644
--- a/src/libANGLE/renderer/gl/TextureGL.cpp
+++ b/src/libANGLE/renderer/gl/TextureGL.cpp
@@ -381,6 +381,38 @@
                .lumaWorkaround.enabled);
 
     stateManager->bindTexture(getType(), mTextureID);
+    const gl::ImageDesc &desc = mState.getImageDesc(index);
+    const bool isFullImage    = area.x == 0 && area.y == 0 && area.width == desc.size.width &&
+                                area.height == desc.size.height;
+
+    if (features.splitLevel0PboFullSubImage2D.enabled && level == 0 && unpackBuffer &&
+        nativegl::UseTexImage2D(getType()) && isFullImage && (area.width > 1 || area.height > 1))
+    {
+        GLuint rowBytes   = 0;
+        GLuint imageBytes = 0;
+        GLuint skipBytes  = 0;
+        ANGLE_CHECK_GL_MATH(contextGL, originalInternalFormatInfo.computeRowDepthSkipBytes(
+                                           type, area.width, area.height, unpack, /*is3D=*/false,
+                                           &rowBytes, &imageBytes, &skipBytes));
+
+        size_t maxBytesUploadedPerChunk = 0;
+        if (area.height > 1)
+        {
+            maxBytesUploadedPerChunk = static_cast<size_t>(area.height - 1) * rowBytes;
+        }
+        else
+        {
+            ASSERT(area.height == 1 && area.width > 1);
+            GLuint pixelBytes        = originalInternalFormatInfo.computePixelBytes(type);
+            maxBytesUploadedPerChunk = static_cast<size_t>(area.width - 1) * pixelBytes;
+        }
+
+        ANGLE_TRY(setSubImageRowByRowWorkaround(context, target, level, area, format, type, unpack,
+                                                unpackBuffer, maxBytesUploadedPerChunk, pixels));
+        contextGL->markWorkSubmitted();
+        return angle::Result::Continue;
+    }
+
     if (features.unpackOverlappingRowsSeparatelyUnpackBuffer.enabled && unpackBuffer &&
         unpack.rowLength != 0 && unpack.rowLength < area.width)
     {
@@ -508,15 +540,35 @@
     else
     {
         ASSERT(nativegl::UseTexImage2D(getType()));
-        for (GLint row = 0; row < area.height; row += rowsPerChunk)
+        if (area.height == 1 && maxBytesUploadedPerChunk > 0 && maxBytesUploadedPerChunk < rowBytes)
         {
-            GLint height             = std::min(rowsPerChunk, area.height - row);
-            GLint byteOffset         = row * rowBytes;
-            const GLubyte *rowPixels = ANGLE_UNSAFE_TODO(pixelsWithSkip + byteOffset);
-            ANGLE_GL_TRY(context, functions->texSubImage2D(
-                                      ToGLenum(target), static_cast<GLint>(level), area.x,
-                                      row + area.y, area.width, height, texSubImageFormat.format,
-                                      texSubImageFormat.type, rowPixels));
+            GLuint pixelBytes    = glFormat.computePixelBytes(type);
+            GLint pixelsPerChunk = std::min(
+                std::max(static_cast<GLint>(maxBytesUploadedPerChunk / pixelBytes), 1), area.width);
+            for (GLint col = 0; col < area.width; col += pixelsPerChunk)
+            {
+                GLint width              = std::min(pixelsPerChunk, area.width - col);
+                GLint byteOffset         = col * pixelBytes;
+                const GLubyte *colPixels = ANGLE_UNSAFE_TODO(pixelsWithSkip + byteOffset);
+                ANGLE_GL_TRY(context, functions->texSubImage2D(
+                                          ToGLenum(target), static_cast<GLint>(level), area.x + col,
+                                          area.y, width, 1, texSubImageFormat.format,
+                                          texSubImageFormat.type, colPixels));
+            }
+        }
+        else
+        {
+            for (GLint row = 0; row < area.height; row += rowsPerChunk)
+            {
+                GLint height             = std::min(rowsPerChunk, area.height - row);
+                GLint byteOffset         = row * rowBytes;
+                const GLubyte *rowPixels = ANGLE_UNSAFE_TODO(pixelsWithSkip + byteOffset);
+                ANGLE_GL_TRY(context,
+                             functions->texSubImage2D(ToGLenum(target), static_cast<GLint>(level),
+                                                      area.x, row + area.y, area.width, height,
+                                                      texSubImageFormat.format,
+                                                      texSubImageFormat.type, rowPixels));
+            }
         }
     }
     return angle::Result::Continue;
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index c527432..ef2fc80 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -35,6 +35,7 @@
 
 #include <EGL/eglext.h>
 #include <algorithm>
+#include <cctype>
 #include <sstream>
 
 using angle::CheckedNumeric;
@@ -222,6 +223,59 @@
 }
 }  // namespace
 
+// Example GL_VENDOR, GL_RENDERER and GL_VERSION strings:
+//   GL_VENDOR:   "Imagination Technologies"
+//   GL_RENDERER: "PowerVR D-Series DXT-48-1536"
+//   GL_VERSION:  "OpenGL ES 3.2 build 25.3@6908880"
+//   GL_VENDOR:   "Imagination Technologies"
+//   GL_RENDERER: "PowerVR D-Series DXT-48-1536"
+//   GL_VERSION:  "OpenGL ES 3.2 build 26.1@7000000"
+bool GetPowerVRDriverVersion(const std::string &vendorString,
+                             const std::string &rendererString,
+                             const std::string &versionString,
+                             std::array<int, 2> *versionOut)
+{
+    ASSERT(versionOut);
+    (*versionOut)[0] = 0;
+    (*versionOut)[1] = 0;
+
+    if (vendorString.find("Imagination") == std::string::npos)
+    {
+        return false;
+    }
+
+    if (rendererString.find("PowerVR") == std::string::npos)
+    {
+        return false;
+    }
+
+    size_t atPos = versionString.find('@');
+    if (atPos == std::string::npos)
+    {
+        return false;
+    }
+
+    size_t startPos = atPos;
+    while (startPos > 0 && !std::isspace(static_cast<unsigned char>(versionString[startPos - 1])))
+    {
+        --startPos;
+    }
+
+    // SAFETY: this offset is computed safely above.
+    std::istringstream stream(ANGLE_UNSAFE_BUFFERS(&versionString[startPos]));
+    int major = 0;
+    int minor = 0;
+    char dot  = 0;
+    if (!(stream >> major >> dot >> minor) || dot != '.')
+    {
+        return false;
+    }
+
+    (*versionOut)[0] = major;
+    (*versionOut)[1] = minor;
+    return true;
+}
+
 SwapControlData::SwapControlData()
     : targetSwapInterval(0), maxSwapInterval(-1), currentSwapInterval(-1)
 {}
@@ -2350,6 +2404,13 @@
     ANGLE_FEATURE_CONDITION(features, unpackOverlappingRowsSeparatelyUnpackBuffer, isNvidia);
     ANGLE_FEATURE_CONDITION(features, packOverlappingRowsSeparatelyPackBuffer, isNvidia);
 
+    std::array<int, 2> powerVRVersion = {0, 0};
+    bool isPowerVRDriver =
+        GetPowerVRDriverVersion(GetVendorString(functions), GetRendererString(functions),
+                                GetVersionString(functions), &powerVRVersion);
+    ANGLE_FEATURE_CONDITION(features, splitLevel0PboFullSubImage2D,
+                            isPowerVRDriver && powerVRVersion < (std::array<int, 2>{26, 2}));
+
     ANGLE_FEATURE_CONDITION(features, initializeCurrentVertexAttributes, isNvidia);
 
     ANGLE_FEATURE_CONDITION(features, unpackLastRowSeparatelyForPaddingInclusion,
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.h b/src/libANGLE/renderer/gl/renderergl_utils.h
index c01151c..4b37e2d 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.h
+++ b/src/libANGLE/renderer/gl/renderergl_utils.h
@@ -19,6 +19,7 @@
 #include "libANGLE/renderer/driver_utils.h"
 #include "libANGLE/renderer/gl/functionsgl_typedefs.h"
 
+#include <array>
 #include <string>
 #include <vector>
 
@@ -188,6 +189,11 @@
 std::string GetVendorString(const FunctionsGL *functions);
 std::string GetVersionString(const FunctionsGL *functions);
 
+bool GetPowerVRDriverVersion(const std::string &vendorString,
+                             const std::string &rendererString,
+                             const std::string &versionString,
+                             std::array<int, 2> *versionOut);
+
 }  // namespace rx
 
 #endif  // LIBANGLE_RENDERER_GL_RENDERERGLUTILS_H_
diff --git a/src/libANGLE/renderer/gl/renderergl_utils_unittest.cpp b/src/libANGLE/renderer/gl/renderergl_utils_unittest.cpp
new file mode 100644
index 0000000..27ab6e4
--- /dev/null
+++ b/src/libANGLE/renderer/gl/renderergl_utils_unittest.cpp
@@ -0,0 +1,55 @@
+//
+// Copyright 2026 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.
+//
+
+#include "gtest/gtest.h"
+
+#include "libANGLE/renderer/gl/renderergl_utils.h"
+
+#include <array>
+
+namespace rx
+{
+namespace
+{
+
+// Test parsing of PowerVR driver version strings.
+TEST(RendererGLUtilsTest, GetPowerVRDriverVersion)
+{
+    std::array<int, 2> version = {0, 0};
+
+    // Valid PowerVR driver version strings
+    EXPECT_TRUE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                        "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_EQ(version[0], 25);
+    EXPECT_EQ(version[1], 3);
+
+    EXPECT_TRUE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                        "OpenGL ES 3.2 build 26.1@7000000", &version));
+    EXPECT_EQ(version[0], 26);
+    EXPECT_EQ(version[1], 1);
+
+    // Negative tests
+    EXPECT_FALSE(GetPowerVRDriverVersion("", "", "", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("", "PowerVR D-Series DXT-48-1536",
+                                         "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "",
+                                         "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "Some Other Renderer",
+                                         "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                         "", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Intel", "Mesa DRI Intel(R) HD Graphics 4000 (IVB GT2)",
+                                         "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                         "without build version", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                         "build invalid@version", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                         "build 26@7000000", &version));
+}
+
+}  // namespace
+}  // namespace rx
diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp
index 06bad2d..139728f 100644
--- a/src/tests/gl_tests/TextureTest.cpp
+++ b/src/tests/gl_tests/TextureTest.cpp
@@ -534,6 +534,9 @@
     Texture2DTestES3RobustInit() : Texture2DTestES3() { setRobustResourceInit(true); }
 };
 
+class Texture2DTestES3PBO : public Texture2DTestES3
+{};
+
 class Texture2DTestES3Foveation : public Texture2DTestES3
 {
   protected:
@@ -10789,6 +10792,79 @@
     EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
 }
 
+// Test that full level 0 PBO uploads with height > 1 correctly upload the last line when split.
+TEST_P(Texture2DTestES3PBO, SplitUploadLastLine)
+{
+    constexpr GLuint kWidth  = 8;
+    constexpr GLuint kHeight = 8;
+
+    std::vector<GLColor> pixelData(kWidth * kHeight, GLColor::green);
+    for (GLuint x = 0; x < kWidth; ++x)
+    {
+        pixelData[(kHeight - 1) * kWidth + x] = GLColor::red;
+    }
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    GLTexture texture;
+    glBindTexture(GL_TEXTURE_2D, texture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+    EXPECT_GL_NO_ERROR();
+
+    GLBuffer pbo;
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
+    glBufferData(GL_PIXEL_UNPACK_BUFFER, pixelData.size() * sizeof(GLColor), pixelData.data(),
+                 GL_STATIC_DRAW);
+
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    EXPECT_GL_NO_ERROR();
+
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+    EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight - 1, GLColor::green);
+    EXPECT_PIXEL_RECT_EQ(0, kHeight - 1, kWidth, 1, GLColor::red);
+}
+
+// Test that full level 0 PBO uploads with height == 1 correctly upload the last pixel when split.
+TEST_P(Texture2DTestES3PBO, SplitUploadLastPixel)
+{
+    constexpr GLuint kWidth  = 8;
+    constexpr GLuint kHeight = 1;
+
+    std::vector<GLColor> pixelData(kWidth * kHeight, GLColor::blue);
+    pixelData[kWidth - 1] = GLColor::yellow;
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    GLTexture texture;
+    glBindTexture(GL_TEXTURE_2D, texture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+    EXPECT_GL_NO_ERROR();
+
+    GLBuffer pbo;
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
+    glBufferData(GL_PIXEL_UNPACK_BUFFER, pixelData.size() * sizeof(GLColor), pixelData.data(),
+                 GL_STATIC_DRAW);
+
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    EXPECT_GL_NO_ERROR();
+
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+    EXPECT_PIXEL_RECT_EQ(0, 0, kWidth - 1, 1, GLColor::blue);
+    EXPECT_PIXEL_RECT_EQ(kWidth - 1, 0, 1, 1, GLColor::yellow);
+}
+
 // Test that compressed textures ignore the pixel unpack state.
 // (https://crbug.com/40057837)
 TEST_P(Texture2DTestES3, PixelUnpackStateTexImage)
@@ -21363,6 +21439,11 @@
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture2DTestES3RobustInit);
 ANGLE_INSTANTIATE_TEST_ES3(Texture2DTestES3RobustInit);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture2DTestES3PBO);
+ANGLE_INSTANTIATE_TEST_ES3_AND(Texture2DTestES3PBO,
+                               ES3_OPENGL().enable(Feature::SplitLevel0PboFullSubImage2D),
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/libANGLE/renderer/gl/renderergl_utils_unittest.cpp b/src/libANGLE/renderer/gl/renderergl_utils_unittest.cpp
new file mode 100644
index 0000000..27ab6e4
--- /dev/null
+++ b/src/libANGLE/renderer/gl/renderergl_utils_unittest.cpp
@@ -0,0 +1,55 @@
+//
+// Copyright 2026 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.
+//
+
+#include "gtest/gtest.h"
+
+#include "libANGLE/renderer/gl/renderergl_utils.h"
+
+#include <array>
+
+namespace rx
+{
+namespace
+{
+
+// Test parsing of PowerVR driver version strings.
+TEST(RendererGLUtilsTest, GetPowerVRDriverVersion)
+{
+    std::array<int, 2> version = {0, 0};
+
+    // Valid PowerVR driver version strings
+    EXPECT_TRUE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                        "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_EQ(version[0], 25);
+    EXPECT_EQ(version[1], 3);
+
+    EXPECT_TRUE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                        "OpenGL ES 3.2 build 26.1@7000000", &version));
+    EXPECT_EQ(version[0], 26);
+    EXPECT_EQ(version[1], 1);
+
+    // Negative tests
+    EXPECT_FALSE(GetPowerVRDriverVersion("", "", "", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("", "PowerVR D-Series DXT-48-1536",
+                                         "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "",
+                                         "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "Some Other Renderer",
+                                         "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                         "", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Intel", "Mesa DRI Intel(R) HD Graphics 4000 (IVB GT2)",
+                                         "OpenGL ES 3.2 build 25.3@6908880", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                         "without build version", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                         "build invalid@version", &version));
+    EXPECT_FALSE(GetPowerVRDriverVersion("Imagination Technologies", "PowerVR D-Series DXT-48-1536",
+                                         "build 26@7000000", &version));
+}
+
+}  // namespace
+}  // namespace rx
diff --git a/src/tests/angle_unittests.gni b/src/tests/angle_unittests.gni
index 2020854..124657c 100644
--- a/src/tests/angle_unittests.gni
+++ b/src/tests/angle_unittests.gni
@@ -133,8 +133,10 @@
   "compiler_tests/UnrollFlatten_test.cpp",
 ]
 
-angle_unittests_gl_sources =
-    [ "../libANGLE/renderer/gl/DisplayGL_unittest.cpp" ]
+angle_unittests_gl_sources = [
+  "../libANGLE/renderer/gl/DisplayGL_unittest.cpp",
+  "../libANGLE/renderer/gl/renderergl_utils_unittest.cpp",
+]
 
 angle_unittests_msl_sources = [
   "../common/apple/ObjCPtr_test.mm",
diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp
index 06bad2d..139728f 100644
--- a/src/tests/gl_tests/TextureTest.cpp
+++ b/src/tests/gl_tests/TextureTest.cpp
@@ -534,6 +534,9 @@
     Texture2DTestES3RobustInit() : Texture2DTestES3() { setRobustResourceInit(true); }
 };
 
+class Texture2DTestES3PBO : public Texture2DTestES3
+{};
+
 class Texture2DTestES3Foveation : public Texture2DTestES3
 {
   protected:
@@ -10789,6 +10792,79 @@
     EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
 }
 
+// Test that full level 0 PBO uploads with height > 1 correctly upload the last line when split.
+TEST_P(Texture2DTestES3PBO, SplitUploadLastLine)
+{
+    constexpr GLuint kWidth  = 8;
+    constexpr GLuint kHeight = 8;
+
+    std::vector<GLColor> pixelData(kWidth * kHeight, GLColor::green);
+    for (GLuint x = 0; x < kWidth; ++x)
+    {
+        pixelData[(kHeight - 1) * kWidth + x] = GLColor::red;
+    }
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    GLTexture texture;
+    glBindTexture(GL_TEXTURE_2D, texture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+    EXPECT_GL_NO_ERROR();
+
+    GLBuffer pbo;
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
+    glBufferData(GL_PIXEL_UNPACK_BUFFER, pixelData.size() * sizeof(GLColor), pixelData.data(),
+                 GL_STATIC_DRAW);
+
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    EXPECT_GL_NO_ERROR();
+
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+    EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight - 1, GLColor::green);
+    EXPECT_PIXEL_RECT_EQ(0, kHeight - 1, kWidth, 1, GLColor::red);
+}
+
+// Test that full level 0 PBO uploads with height == 1 correctly upload the last pixel when split.
+TEST_P(Texture2DTestES3PBO, SplitUploadLastPixel)
+{
+    constexpr GLuint kWidth  = 8;
+    constexpr GLuint kHeight = 1;
+
+    std::vector<GLColor> pixelData(kWidth * kHeight, GLColor::blue);
+    pixelData[kWidth - 1] = GLColor::yellow;
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    GLTexture texture;
+    glBindTexture(GL_TEXTURE_2D, texture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+    EXPECT_GL_NO_ERROR();
+
+    GLBuffer pbo;
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
+    glBufferData(GL_PIXEL_UNPACK_BUFFER, pixelData.size() * sizeof(GLColor), pixelData.data(),
+                 GL_STATIC_DRAW);
+
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    EXPECT_GL_NO_ERROR();
+
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+    EXPECT_PIXEL_RECT_EQ(0, 0, kWidth - 1, 1, GLColor::blue);
+    EXPECT_PIXEL_RECT_EQ(kWidth - 1, 0, 1, 1, GLColor::yellow);
+}
+
 // Test that compressed textures ignore the pixel unpack state.
 // (https://crbug.com/40057837)
 TEST_P(Texture2DTestES3, PixelUnpackStateTexImage)
@@ -21363,6 +21439,11 @@
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture2DTestES3RobustInit);
 ANGLE_INSTANTIATE_TEST_ES3(Texture2DTestES3RobustInit);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture2DTestES3PBO);
+ANGLE_INSTANTIATE_TEST_ES3_AND(Texture2DTestES3PBO,
+                               ES3_OPENGL().enable(Feature::SplitLevel0PboFullSubImage2D),
+                               ES3_OPENGLES().enable(Feature::SplitLevel0PboFullSubImage2D));
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture2DTestES3Foveation);
 ANGLE_INSTANTIATE_TEST_ES3_AND(
     Texture2DTestES3Foveation,
Loading diff…

Original Bug Report

reported by ha...@gmail.com

Use After Free in TQMQueueTransfer

Security Bug

Important: Please do not change the component of this bug manually.

Please READ THIS FAQ before filing a bug: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md

Please see the following link for instructions on filing security bugs: https://www.chromium.org/Home/chromium-security/reporting-security-bugs

Reports may be eligible for reward payments under the Chrome VRP: https://g.co/chrome/vrp

NOTE: Security bugs are normally made public once a fix has been widely deployed.


VULNERABILITY DETAILS

his indicates that the TQ context object internally holds a stale mapping pointer. When the texture is respec (redefined in size/format),

the surface mapping inside TQ is not updated correctly, and subsequent texture data operations using the stale mapping result in SEGV.

Chrome Version: [146.0.7680.119] + [stable]

Operating System: [Android pixel 10 latest patch]

REPRODUCTION CASE

open poc.html and locat | grep DEBUG

FOR CRASHES, PLEASE INCLUDE THE FOLLOWING ADDITIONAL INFORMATION

Type of crash: [gpu.]

Crash State:

Symbolizing stack using ABI=arm64
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7b9bb61108 in tid 15173 (CrGpuMain), pid 15147 (ileged_process0)
Build fingerprint: 'google/frankel/frankel:16/CP1A.260305.018/14887507:user/release-keys'
Revision: 'MP1.0'
pid: 15147, tid: 15173, name: CrGpuMain  >>> org.chromium.chrome:privileged_process0 <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000007b9bb61108 (read)

Stack Trace:
  RELADDR   FUNCTION                                                                          FILE:LINE
  000000000004060c  TQMQueueTransfer+2620) (BuildId: 17a0557644ca324bb2113edbfbfbd1e6                 /vendor/lib64/libIMGegl.so
  0000000000120590  TQCopyDataFromSurface+768) (BuildId: cefd59f52838946b0e646aaf2bb04c76             /vendor/lib64/egl/libGLESv2_powervr.so
  00000000001589f8  glCopyTexSubImage2D+1256) (BuildId: cefd59f52838946b0e646aaf2bb04c76              /vendor/lib64/egl/libGLESv2_powervr.so
  0000000003677128  rx::TextureGL::copySubImage(gl::Context const*, gl::ImageIndex const&, angle::Offset<int> const&, gl::RectangleImpl<int> const&, gl::Framebuffer*)  ../../third_party/angle/src/libANGLE/renderer/gl/TextureGL.cpp:0:0
  00000000035aa8ac  gl::Texture::copySubImage(gl::Context*, gl::ImageIndex const&, angle::Offset<int> const&, gl::RectangleImpl<int> const&, gl::Framebuffer*)  ../../third_party/angle/src/libANGLE/Texture.cpp:1613:25
  000000000352aea4  gl::Context::copyTexSubImage2D(gl::TextureTarget, int, int, int, int, int, int, int)  ../../third_party/angle/src/libANGLE/Context.cpp:5192:32
  0000000008399250  GL_CopyTexSubImage2D                                                              ../../third_party/angle/src/libGLESv2/entry_points_gles_2_0_autogen.cpp:1142:22
  0000000009a05278  gpu::gles2::GLES2DecoderPassthroughImpl::DoCopyTexSubImage2D(unsigned int, int, int, int, int, int, int, int)  ../../gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc:882:10
  0000000009a1c87c  gpu::gles2::GLES2DecoderPassthroughImpl::HandleCopyTexSubImage2D(unsigned int, void const volatile*)  ../../gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc:533:7
  00000000099fb52c  gpu::error::Error gpu::gles2::GLES2DecoderPassthroughImpl::DoCommandsImpl<false>(unsigned int, void const volatile*, int, int*)  ../../gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc:742:20
  00000000042bd920  gpu::CommandBufferService::Flush(int, gpu::AsyncAPIInterface*)                    ../../gpu/command_buffer/service/command_buffer_service.cc:267:35
  0000000009aca028  gpu::CommandBufferStub::OnAsyncFlush(int, unsigned int, std::__Cr::vector<gpu::SyncToken, std::__Cr::allocator<gpu::SyncToken>> const&)  ../../gpu/ipc/service/command_buffer_stub.cc:504:22
  0000000009ac9cc0  gpu::CommandBufferStub::ExecuteDeferredRequest(gpu::mojom::DeferredCommandBufferRequestParams&, gpu::FenceSyncReleaseDelegate*)  ../../gpu/ipc/service/command_buffer_stub.cc:173:7
  0000000009acfb50  gpu::GpuChannel::ExecuteDeferredRequest(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*)  ../../gpu/ipc/service/gpu_channel.cc:833:13
  0000000009ad29e0  void base::internal::DecayedFunctorTraits<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>::Invoke<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel> const&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*>(void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel> const&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&, gpu::FenceSyncReleaseDelegate*&&)  ../../base/functional/bind_internal.h:740:12
  v------>  void base::internal::InvokeHelper<true, base::internal::FunctorTraits<void (gpu::GpuChannel::*&&)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>, void, 0ul, 1ul>::MakeItSo<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), std::__Cr::tuple<base::WeakPtr<gpu::GpuChannel>, mojo::StructPtr<gpu::mojom::DeferredRequestParams>>, gpu::FenceSyncReleaseDelegate*>(void (gpu::GpuChannel::*&&)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), std::__Cr::tuple<base::WeakPtr<gpu::GpuChannel>, mojo::StructPtr<gpu::mojom::DeferredRequestParams>>&&, gpu::FenceSyncReleaseDelegate*&&)  ../../base/functional/bind_internal.h:956:5
  v------>  void base::internal::Invoker<base::internal::FunctorTraits<void (gpu::GpuChannel::*&&)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>, base::internal::BindState<true, true, false, void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>, mojo::StructPtr<gpu::mojom::DeferredRequestParams>>, void (gpu::FenceSyncReleaseDelegate*)>::RunImpl<void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), std::__Cr::tuple<base::WeakPtr<gpu::GpuChannel>, mojo::StructPtr<gpu::mojom::DeferredRequestParams>>, 0ul, 1ul>(void (gpu::GpuChannel::*&&)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), std::__Cr::tuple<base::WeakPtr<gpu::GpuChannel>, mojo::StructPtr<gpu::mojom::DeferredRequestParams>>&&, std::__Cr::integer_sequence<unsigned long, 0ul, 1ul>, gpu::FenceSyncReleaseDelegate*&&)  ../../base/functional/bind_internal.h:1069:14
  0000000009ad2964  base::internal::Invoker<base::internal::FunctorTraits<void (gpu::GpuChannel::*&&)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>&&, mojo::StructPtr<gpu::mojom::DeferredRequestParams>&&>, base::internal::BindState<true, true, false, void (gpu::GpuChannel::*)(mojo::StructPtr<gpu::mojom::DeferredRequestParams>, gpu::FenceSyncReleaseDelegate*), base::WeakPtr<gpu::GpuChannel>, mojo::StructPtr<gpu::mojom::DeferredRequestParams>>, void (gpu::FenceSyncReleaseDelegate*)>::RunOnce(base::internal::BindStateBase*, gpu::FenceSyncReleaseDelegate*)  ../../base/functional/bind_internal.h:982:12
  v------>  base::OnceCallback<void (media::DemuxerStream*)>::Run(media::DemuxerStream*) &&   ../../base/functional/callback.h:155:12
  v------>  void base::internal::DecayedFunctorTraits<base::OnceCallback<void (media::DemuxerStream*)>, media::DemuxerStream*&&>::Invoke<base::OnceCallback<void (media::DemuxerStream*)>, media::DemuxerStream*>(base::OnceCallback<void (media::DemuxerStream*)>&&, media::DemuxerStream*&&)  ../../base/functional/bind_internal.h:815:49
  v------>  void base::internal::InvokeHelper<false, base::internal::FunctorTraits<base::OnceCallback<void (media::DemuxerStream*)>&&, media::DemuxerStream*&&>, void, 0ul>::MakeItSo<base::OnceCallback<void (media::DemuxerStream*)>, std::__Cr::tuple<base::internal::UnretainedWrapper<media::DemuxerStream, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>>(base::OnceCallback<void (media::DemuxerStream*)>&&, std::__Cr::tuple<base::internal::UnretainedWrapper<media::DemuxerStream, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>&&)  ../../base/functional/bind_internal.h:932:12
  v------>  void base::internal::Invoker<base::internal::FunctorTraits<base::OnceCallback<void (media::DemuxerStream*)>&&, media::DemuxerStream*&&>, base::internal::BindState<false, true, true, base::OnceCallback<void (media::DemuxerStream*)>, base::internal::UnretainedWrapper<media::DemuxerStream, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, void ()>::RunImpl<base::OnceCallback<void (media::DemuxerStream*)>, std::__Cr::tuple<base::internal::UnretainedWrapper<media::DemuxerStream, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>, 0ul>(base::OnceCallback<void (media::DemuxerStream*)>&&, std::__Cr::tuple<base::internal::UnretainedWrapper<media::DemuxerStream, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)0>>&&, std::__Cr::integer_sequence<unsigned long, 0ul>)  ../../base/functional/bind_internal.h:1069:14
  0000000003bb79a4  base::internal::Invoker<base::internal::FunctorTraits<base::OnceCallback<void (BrowserWindowInterface*)>&&, base::raw_ptr<BrowserWindowInterface, (partition_alloc::internal::RawPtrTraits)1>&&>, base::internal::BindState<false, true, true, base::OnceCallback<void (BrowserWindowInterface*)>, base::internal::UnretainedWrapper<BrowserWindowInterface, base::unretained_traits::MayNotDangle, (partition_alloc::internal::RawPtrTraits)1>>, void ()>::RunOnce(base::internal::BindStateBase*)  ../../base/functional/bind_internal.h:982:12
  v------>  base::OnceCallback<void ()>::Run() &&                                             ../../base/functional/callback.h:155:12
  00000000042c3384  gpu::Scheduler::ExecuteSequence(base::IdType<gpu::SyncPointOrderData, unsigned int, 0u, 1u>)  ../../gpu/command_buffer/service/scheduler.cc:707:29
  00000000042c2a7c  gpu::Scheduler::RunNextTask()                                                     ../../gpu/command_buffer/service/scheduler.cc:625:3
  v------>  base::OnceCallback<void ()>::Run() &&                                             ../../base/functional/callback.h:155:12
  000000000703a1fc  base::TaskAnnotator::RunTaskImpl(base::PendingTask&)                              ../../base/task/common/task_annotator.cc:229:34
  v------>  void base::TaskAnnotator::RunTask<base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*)::$_3>(perfetto::StaticString, base::PendingTask&, base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*)::$_3&&)  ../../base/task/common/task_annotator.h:112:5
  0000000007056a3c  base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*)  ../../base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:475:23
  0000000007056640  base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork()   ../../base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:346:40
  0000000006fead28  base::MessagePumpDefault::Run(base::MessagePump::Delegate*)                       ../../base/message_loop/message_pump_default.cc:42:55
  0000000007057100  base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool, base::TimeDelta)  ../../base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:650:12
  00000000070182cc  base::RunLoop::Run(base::Location const&)                                         ../../base/run_loop.cc:135:14
  000000000ce4b014  content::GpuMain(content::MainFunctionParams)                                     ../../content/gpu/gpu_main.cc:485:14
  0000000006fc2288  content::RunOtherNamedProcessTypeMain(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char>> const&, content::MainFunctionParams, content::ContentMainDelegate*)  ../../content/app/content_main_runner_impl.cc:762:14
  0000000006fc30d8  content::ContentMainRunnerImpl::Run()                                             ../../content/app/content_main_runner_impl.cc:1152:10
  0000000006fc0acc  content::RunContentProcess(content::ContentMainParams, content::ContentMainRunner*)  ../../content/app/content_main.cc:358:36
  0000000006fc1ac8  content::StartContentMain(bool)                                                   ../../content/app/android/content_main_android.cc:54:10
  00000000002c2300  art_quick_generic_jni_trampoline+144) (BuildId: 61c7a211c01ef3c0068b4fbe31051050  /apex/com.android.art/lib64/libart.so
  00000000002ab260  art_quick_invoke_static_stub+640) (BuildId: 61c7a211c01ef3c0068b4fbe31051050      /apex/com.android.art/lib64/libart.so
  0000000000585c88  bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+1984) (BuildId: 61c7a211c01ef3c0068b4fbe31051050  /apex/com.android.art/lib64/libart.so
  000000000048ff6c  void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+468) (BuildId: 61c7a211c01ef3c0068b4fbe31051050  /apex/com.android.art/lib64/libart.so
  00000000003d1718  ExecuteSwitchImplAsm+8) (BuildId: 61c7a211c01ef3c0068b4fbe31051050                /apex/com.android.art/lib64/libart.so
  000000000029005c  offset 0x4085000) (xi1.run+0                                                      /data/app/~~ayCs0G1cEE9vDupqqYSiOA==/org.chromium.chrome-OG-qSl8JAd4v0pHWHiahqA==/base.apk/libmonochrome.so
  00000000003d136c  art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) (.__uniq.112435418011751916792819755956732575238.llvm.15381326084910962254)+364) (BuildId: 61c7a211c01ef3c0068b4fbe31051050  /apex/com.android.art/lib64/libart.so
  00000000003d0aa0  artQuickToInterpreterBridge+1020) (BuildId: 61c7a211c01ef3c0068b4fbe31051050      /apex/com.android.art/lib64/libart.so
  00000000002c2438  art_quick_to_interpreter_bridge+88) (BuildId: 61c7a211c01ef3c0068b4fbe31051050    /apex/com.android.art/lib64/libart.so
  00000000000049a8  offset 0x2000000) (java.lang.Thread.run+136                                       [anon_shmem:dalvik-jit-code-cache]
  00000000002aaf94  art_quick_invoke_stub+612) (BuildId: 61c7a211c01ef3c0068b4fbe31051050             /apex/com.android.art/lib64/libart.so
  00000000002709b0  art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+220) (BuildId: 61c7a211c01ef3c0068b4fbe31051050  /apex/com.android.art/lib64/libart.so
  00000000004bdfc8  art::Thread::CreateCallback(void*)+1184) (BuildId: 61c7a211c01ef3c0068b4fbe31051050  /apex/com.android.art/lib64/libart.so
  00000000004bdb18  art::Thread::CreateCallbackWithUffdGc(void*)+8) (BuildId: 61c7a211c01ef3c0068b4fbe31051050  /apex/com.android.art/lib64/libart.so
  000000000008a914  __pthread_start(void*) (.__uniq.67847048707805468364044055584648682506)+180) (BuildId: 8d65ea529c21c79c019713e50adb6675  /apex/com.android.runtime/lib64/bionic/libc.so
  000000000007b5a4  __start_thread+68) (BuildId: 8d65ea529c21c79c019713e50adb6675                     /apex/com.android.runtime/lib64/bionic/libc.so

CREDIT INFORMATION

Reporter credit: Goodluck

Please categorize manually; bot-based categorization is inaccurate.

View on issue tracker