blob: 51a4e642c61c6baa731b71f6161611376ee1b199 [file] [log] [blame]
//
// 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.
//
// BaseInstanceOverflowTest: Reproduces integer overflow in vertex buffer streaming path.
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
class BaseInstanceOverflowTest : public ANGLETest<>
{
protected:
BaseInstanceOverflowTest()
{
setWindowWidth(1);
setWindowHeight(1);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
// Regression Test (crbug.com/489791424)
// Reproduces integer overflow in vertex buffer streaming path.
// The bug occurs when baseInstance is large, causing a wrap-around in reservation
// while the copy path uses 64-bit math.
TEST_P(BaseInstanceOverflowTest, BaseInstanceOverflow)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_base_vertex_base_instance"));
// We need a dynamic vertex attribute to trigger the streaming path.
// In D3D11, using an unaligned offset forces the streaming path.
GLBuffer buffer;
glBindBuffer(GL_ARRAY_BUFFER, buffer);
std::vector<GLfloat> data(1000, 1.0f);
glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), data.data(), GL_DYNAMIC_DRAW);
GLProgram program;
program.makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glUseProgram(program);
GLint posLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
glEnableVertexAttribArray(posLoc);
// Unaligned offset (1 byte) forces streaming path in D3D11.
glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 16, reinterpret_cast<void *>(1));
glVertexAttribDivisor(posLoc, 1);
// Trigger overflow by using a large baseInstance.
// elementCount calculation in Renderer11::getVertexSpaceRequired:
// (instances + baseInstance) / divisor
// If instances = 300, baseInstance = 0xFFFFFF00, divisor = 1:
// 300 + 0xFFFFFF00 = 0x10000002C.
// 32-bit truncation results in 0x2C = 44 elements reserved.
// However, StreamingVertexBufferInterface::storeDynamicAttribute uses 64-bit size_t:
// adjustedCount = 300 + 0xFFFFFF00 = 4,294,967,340 elements copied.
GLuint baseInstance = 0xFFFFFF00;
GLsizei instanceCount = 300;
// This call is expected to crash the GPU process without the fix.
// With the fix, the large baseInstance is caught by validation in VertexDataManager (D3D11)
// or ContextGL (OpenGL) because it exceeds the source buffer's bounds, returning
// GL_INVALID_OPERATION (D3D11) or avoiding the draw (OpenGL fallback).
glDrawArraysInstancedBaseInstanceANGLE(GL_TRIANGLES, 0, 3, instanceCount, baseInstance);
if (isD3D11Renderer())
{
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
else
{
EXPECT_GL_NO_ERROR();
}
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BaseInstanceOverflowTest);
ANGLE_INSTANTIATE_TEST(BaseInstanceOverflowTest,
ES3_D3D11().enable(Feature::AlwaysEnableEmulatedMultidrawExtensions),
ES3_OPENGL().enable(Feature::AlwaysEnableEmulatedMultidrawExtensions),
ES3_OPENGLES().enable(Feature::AlwaysEnableEmulatedMultidrawExtensions),
ES3_VULKAN().enable(Feature::AlwaysEnableEmulatedMultidrawExtensions),
ES3_METAL().enable(Feature::AlwaysEnableEmulatedMultidrawExtensions));