Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in ANGLE
DescriptionInsufficient validation of untrusted input in ANGLE
ComponentANGLE
Bug ClassLogic Error
Tracker40073848
Fix commit42ae4b5eee73 (angle/angle) +88/-0
CISA KEVNot listed
Creditedparkminchan, working for SSD Labs Korea
Disclosed2026-04-07

Changed Functions

FunctionChangeNotes
BufferDataTest1gbLimit
src/tests/gl_tests/BufferDataTest.cpp
modified

Files Changed

  • include/platform/autogen/FeaturesGL_autogen.h
  • include/platform/gl_features.json
  • src/libANGLE/Caps.h
  • src/libANGLE/ErrorStrings.h
  • src/libANGLE/renderer/gl/renderergl_utils.cpp
  • src/libANGLE/validationES2.cpp
  • src/libANGLE/validationES3.cpp
  • src/libANGLE/validationESEXT.cpp
  • src/tests/gl_tests/BufferDataTest.cpp
From 42ae4b5eee733709f37b9f7a50d227a6bc90af70 Mon Sep 17 00:00:00 2001
From: Geoff Lang <geofflang@chromium.org>
Date: Wed, 18 Feb 2026 12:09:39 -0500
Subject: [PATCH] GL: Limit buffer size to 1gb on Intel Macs.

These drivers cannot handle allocations this large so add a limitation
which generates GL_INVALID_OPERATION.

Bug: chromium:40073848
Change-Id: Ibeffb7507e72e4fb5e2a23ed9b873c726c351361
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7589937
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Auto-Submit: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
---

diff --git a/include/platform/autogen/FeaturesGL_autogen.h b/include/platform/autogen/FeaturesGL_autogen.h
index c0c00f1..f1aa71d 100644
--- a/include/platform/autogen/FeaturesGL_autogen.h
+++ b/include/platform/autogen/FeaturesGL_autogen.h
@@ -668,6 +668,12 @@
         &members,
     };
 
+    FeatureInfo limitMaxBufferSizeTo1gb = {
+        "limitMaxBufferSizeTo1gb",
+        FeatureCategory::OpenGLWorkarounds,
+        &members,
+    };
+
 };
 
 inline FeaturesGL::FeaturesGL()  = default;
diff --git a/include/platform/gl_features.json b/include/platform/gl_features.json
index a78b0d4..6d3c820 100644
--- a/include/platform/gl_features.json
+++ b/include/platform/gl_features.json
@@ -868,6 +868,14 @@
                 "Validate GL_MAX_*_UNIFORM_BLOCKS at compile time instead of link time to work around compiler bugs."
             ],
             "issue": "http://crbug.com/475877320"
+        },
+        {
+            "name": "limit_max_buffer_size_to_1gb",
+            "category": "Workarounds",
+            "description": [
+                "Some drivers have internal failures when attempting to allocate and initialize buffers larger than 1gb."
+            ],
+            "issue": "http://crbug.com/475877320"
         }
     ]
 }
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index af19f52..9f05536 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -172,6 +172,10 @@
     // GL_ANGLE_base_vertex_base_instance is emulated and should only be exposed to WebGL. Emulated
     // by default in shared renderer code.
     bool baseInstanceBaseVertexEmulated = true;
+
+    // Size limit for buffers. GL_INVALID_OPERATION should be generated if trying to allocate a
+    // buffer larger than this limit.
+    GLsizeiptr bufferSizeLimit = std::numeric_limits<GLsizeiptr>::max();
 };
 
 struct TypePrecision
diff --git a/src/libANGLE/ErrorStrings.h b/src/libANGLE/ErrorStrings.h
index 22a8ed0..16755fc 100644
--- a/src/libANGLE/ErrorStrings.h
+++ b/src/libANGLE/ErrorStrings.h
@@ -68,6 +68,7 @@
 inline constexpr const char *kBufferNotMapped = "Buffer is not mapped.";
 inline constexpr const char *kBufferNotUpdatable = "Buffer is not updatable.";
 inline constexpr const char *kBufferOffsetOverflow = "Buffer offset overflow.";
+inline constexpr const char *kBufferSizeLimitation = "Buffer size is too large (artificial limitation).";
 inline constexpr const char *kBufferTextureNotAllowed = "Buffer textures are not allowed.";
 inline constexpr const char *kCannotPopDefaultDebugGroup = "Cannot pop the default debug group.";
 inline constexpr const char *kClientBufferInvalid = "Size must not exceed the size of clientbuffer.";
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index 29abdf4..06dc8b3 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -2221,6 +2221,11 @@
         // Restore previous state
         functions->blendColor(oldColor[0], oldColor[1], oldColor[2], oldColor[3]);
     }
+
+    if (features.limitMaxBufferSizeTo1gb.enabled)
+    {
+        limitations->bufferSizeLimit = 1 << 30;
+    }
 }
 
 bool GetSystemInfoVendorIDAndDeviceID(const FunctionsGL *functions,
@@ -2755,6 +2760,9 @@
     // IMG GL drivers crash while compiling shaders with more than the limit of uniform blocks.
     ANGLE_FEATURE_CONDITION(features, validateMaxPerStageUniformBlocksAtCompileTime,
                             IsPowerVR(vendor));
+
+    // Mac Intel drivers are unable to allocate buffers larger than ~1gb
+    ANGLE_FEATURE_CONDITION(features, limitMaxBufferSizeTo1gb, isApple && isIntel);
 }
 
 void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features)
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index adaa73e..2b6a85e 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -3636,6 +3636,13 @@
         return false;
     }
 
+    const Limitations &limitations = context->getLimitations();
+    if (size > limitations.bufferSizeLimit)
+    {
+        ANGLE_VALIDATION_ERROR(GL_INVALID_OPERATION, kBufferSizeLimitation);
+        return false;
+    }
+
     switch (usage)
     {
         case BufferUsage::StreamDraw:
diff --git a/src/libANGLE/validationES3.cpp b/src/libANGLE/validationES3.cpp
index 173841a..170eeb2 100644
--- a/src/libANGLE/validationES3.cpp
+++ b/src/libANGLE/validationES3.cpp
@@ -3303,6 +3303,13 @@
         return false;
     }
 
+    const Limitations &limitations = context->getLimitations();
+    if (size > limitations.bufferSizeLimit)
+    {
+        ANGLE_VALIDATION_ERROR(GL_INVALID_OPERATION, kBufferSizeLimitation);
+        return false;
+    }
+
     if (checkedReadSum.ValueOrDie() > readBuffer->getSize() ||
         checkedWriteSum.ValueOrDie() > writeBuffer->getSize())
     {
diff --git a/src/libANGLE/validationESEXT.cpp b/src/libANGLE/validationESEXT.cpp
index b9849fd..5e7055e 100644
--- a/src/libANGLE/validationESEXT.cpp
+++ b/src/libANGLE/validationESEXT.cpp
@@ -2681,6 +2681,13 @@
         return false;
     }
 
+    const Limitations &limitations = context->getLimitations();
+    if (size > limitations.bufferSizeLimit)
+    {
+        ANGLE_VALIDATION_ERROR(GL_INVALID_OPERATION, kBufferSizeLimitation);
+        return false;
+    }
+
     constexpr GLbitfield kAllUsageFlags =
         (GL_DYNAMIC_STORAGE_BIT_EXT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT |
          GL_MAP_PERSISTENT_BIT_EXT | GL_MAP_COHERENT_BIT_EXT | GL_CLIENT_STORAGE_BIT_EXT);
@@ -2800,6 +2807,13 @@
         return false;
     }
 
+    const Limitations &limitations = context->getLimitations();
+    if (size > limitations.bufferSizeLimit)
+    {
+        ANGLE_VALIDATION_ERROR(GL_INVALID_VALUE, kBufferSizeLimitation);
+        return false;
+    }
+
     return true;
 }
 
diff --git a/src/tests/gl_tests/BufferDataTest.cpp b/src/tests/gl_tests/BufferDataTest.cpp
index f9960a4..97a8fba 100644
--- a/src/tests/gl_tests/BufferDataTest.cpp
+++ b/src/tests/gl_tests/BufferDataTest.cpp
@@ -2413,6 +2413,33 @@
     glBindBuffer(GL_COPY_READ_BUFFER, 0);
 }
 
+class BufferDataTest1gbLimit : public BufferDataTest
+{};
+
+// Allocating >1gb should generate an INVALID_OPERATION when LimitMaxBufferSizeTo1gb is enabled.
+TEST_P(BufferDataTest1gbLimit, ErrorGeneratedOnLargeAllocation)
+{
+    GLBuffer buffer;
+    glBindBuffer(GL_ARRAY_BUFFER, buffer);
+    EXPECT_GL_NO_ERROR();
+
+    // First make sure a small allocation works
+    glBufferData(GL_ARRAY_BUFFER, (1 << 10) + 1, nullptr, GL_STATIC_DRAW);
+    EXPECT_GL_NO_ERROR();
+
+    // >1gb should fail.
+    glBufferData(GL_ARRAY_BUFFER, (1 << 30) + 1, nullptr, GL_STATIC_DRAW);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // glCopyBufferSubData can't be tested because a source buffer > 1gb cannot be created.
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/tests/gl_tests/BufferDataTest.cpp b/src/tests/gl_tests/BufferDataTest.cpp
index f9960a4..97a8fba 100644
--- a/src/tests/gl_tests/BufferDataTest.cpp
+++ b/src/tests/gl_tests/BufferDataTest.cpp
@@ -2413,6 +2413,33 @@
     glBindBuffer(GL_COPY_READ_BUFFER, 0);
 }
 
+class BufferDataTest1gbLimit : public BufferDataTest
+{};
+
+// Allocating >1gb should generate an INVALID_OPERATION when LimitMaxBufferSizeTo1gb is enabled.
+TEST_P(BufferDataTest1gbLimit, ErrorGeneratedOnLargeAllocation)
+{
+    GLBuffer buffer;
+    glBindBuffer(GL_ARRAY_BUFFER, buffer);
+    EXPECT_GL_NO_ERROR();
+
+    // First make sure a small allocation works
+    glBufferData(GL_ARRAY_BUFFER, (1 << 10) + 1, nullptr, GL_STATIC_DRAW);
+    EXPECT_GL_NO_ERROR();
+
+    // >1gb should fail.
+    glBufferData(GL_ARRAY_BUFFER, (1 << 30) + 1, nullptr, GL_STATIC_DRAW);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // glCopyBufferSubData can't be tested because a source buffer > 1gb cannot be created.
+
+    if (EnsureGLExtensionEnabled("GL_EXT_buffer_storage"))
+    {
+        glBufferStorageEXT(GL_ARRAY_BUFFER, (1 << 30) + 1, nullptr, 0);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+    }
+}
+
 ANGLE_INSTANTIATE_TEST_ES2(BufferDataTest);
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BufferSubDataTest);
@@ -2437,6 +2464,10 @@
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BufferStorageTestES3Threaded);
 ANGLE_INSTANTIATE_TEST_ES3(BufferStorageTestES3Threaded);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BufferDataTest1gbLimit);
+ANGLE_INSTANTIATE_TEST(BufferDataTest1gbLimit,
+                       ES2_OPENGL().enable(Feature::LimitMaxBufferSizeTo1gb),
+                       ES3_OPENGL().enable(Feature::LimitMaxBufferSizeTo1gb));
 #ifdef _WIN64
 
 // Test a bug where an integer overflow bug could trigger a crash in D3D.
Loading diff…

Original Bug Report

reported by no...@ssd-disclosure.com

ArrayBuffer OOR/W in glBufferData_Exec

Steps to reproduce the problem:
Insufficient input validation for large size ArrayBuffer results in an Out of Bound (R/W) vulnerability in the glBufferData_Exec function of the GL driver.

Problem Description:

ROOT CAUSE ANALYSIS

* Due to various characteristics, it will not be possible to reproduce in ClusterFuzzer.
* Because it occurs within the GL Driver, accurate root cause analysis is impossible.
* Since this vulnerability is reproduced when a page is loaded more than three times, it may be related to GPU task scheduling.

angle::Result Buffer::bufferDataImpl(Context \*context,  
                                     BufferBinding target,  
                                     const void \*data,  
                                     GLsizeiptr size,  
                                     BufferUsage usage,  
                                     GLbitfield flags)  
{  
	...  
	  
    if (context && context->isRobustResourceInitEnabled() && !data && size > 0)  
    {  
        angle::MemoryBuffer \*scratchBuffer = nullptr;  
        ANGLE_CHECK_GL_ALLOC(  
            context, context->getZeroFilledBuffer(static_cast<size_t>(size), &scratchBuffer));  
        dataForImpl = scratchBuffer->data();  
    }  
  
	...  
}  

If the size of the ArrayBuffer is close to the maximum value of the GLsizeiptr(signed long int), an integer overflow occurs.

bool ScratchBuffer::getImpl(size_t requestedSize,  
                            MemoryBuffer \*\*memoryBufferOut,  
                            Optional<uint8_t> initValue)  
{  
    ...  
  
    if (mScratchMemory.size() < requestedSize)  
    {  
        if (!mScratchMemory.resize(requestedSize))  
        {  
            return false;  
        }  
        mResetCounter = mLifetime;  
        if (initValue.valid())  
        {  
            mScratchMemory.fill(initValue.value());  
        }  
    }  
      
    ...  
}  

The buffer is allocated by the size converted from the size in which the integer overflow has occurred to size_t.

angle::Result BufferGL::setData(const gl::Context \*context,  
                                gl::BufferBinding target,  
                                const void \*data,  
                                size_t size,  
                                gl::BufferUsage usage)  
{  
	...  
	ANGLE_GL_TRY(context, functions->bufferData(gl::ToGLenum(DestBufferOperationTarget), size, data,  
                                                ToGLenum(usage)));  
	...  
}  

When handling with data in bufferData functions of GL driver, the actual size of data and size parameters are different, resulting in out of bound r/w memory corruptions.

--- a/third_party/angle/src/libANGLE/Buffer.cpp  
+++ b/third_party/angle/src/libANGLE/Buffer.cpp  
@@ -140,7 +140,7 @@  
         dataForImpl = scratchBuffer->data();  
     }  
   
-    if (mImpl->setDataWithUsageFlags(context, target, nullptr, dataForImpl, size, usage, flags) ==  
+    if (mImpl->setDataWithUsageFlags(context, target, nullptr, dataForImpl, static_cast<size_t>(size), usage, flags) ==  
         angle::Result::Stop)  
     {  
         // If setData fails, the buffer contents are undefined. Set a zero size to indicate that.  

When calling the setData function, if the size parameter is also converted to size_t, memory corruptions caused by different sizes from the data can be prevented.


VERSION

* Chrome Version: 116.0.5845.0 (Developer Build) (x86_64)
/ 118.0.5970.0 (Developer Build) (x86_64)
* Operating System: macOS Monterey Version 12.6.8 (21G725)
* Hardware: Intel Core i5, 8GB RAM
* GPU: Intel Iris Graphics 540 1536 MB
(**I have attached the contents of the chrome://gpu page.**)

REPRODUCE CASE

You can simply reproduce by opening the attached poc.html in Chromium.

./Chromium --no-sandbox poc.html

CREDIT

parkminchan, working for SSD Labs Korea.

Additional Comments:

**Chrome version: ** 117.0.0.0 **Channel: ** Stable

OS: Mac OS

View on issue tracker