CVE-2026-5879
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
BufferDataTest1gbLimitsrc/tests/gl_tests/BufferDataTest.cpp |
modified |
Files Changed
include/platform/autogen/FeaturesGL_autogen.hinclude/platform/gl_features.jsonsrc/libANGLE/Caps.hsrc/libANGLE/ErrorStrings.hsrc/libANGLE/renderer/gl/renderergl_utils.cppsrc/libANGLE/validationES2.cppsrc/libANGLE/validationES3.cppsrc/libANGLE/validationESEXT.cppsrc/tests/gl_tests/BufferDataTest.cpp
Patch
From 42ae4b5eee733709f37b9f7a50d227a6bc90af70 Mon Sep 17 00:00:00 2001 From: Geoff Lang <[email protected]> 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 <[email protected]> Auto-Submit: Geoff Lang <[email protected]> Reviewed-by: Shahbaz Youssefi <[email protected]> --- 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.
Regression Test / PoC
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.
Original Bug Report
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.
RECOMMENDED PATCHES
--- 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