Commit d2f0c74c by Jamie Madill Committed by Commit Bot

Use safe math in ValidateCopyBufferSubData.

This should fix any potential out of bounds reads/writes. BUG=chromium:660854 Change-Id: Iffa00e4551d7362115cbf023a09b1d0e15f724c8 Reviewed-on: https://chromium-review.googlesource.com/405816 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent b990b55e
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
#include "libANGLE/validationES3.h" #include "libANGLE/validationES3.h"
#include "base/numerics/safe_conversions.h"
#include "common/mathutil.h"
#include "common/utilities.h"
#include "libANGLE/validationES.h" #include "libANGLE/validationES.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Texture.h" #include "libANGLE/Texture.h"
...@@ -16,9 +19,6 @@ ...@@ -16,9 +19,6 @@
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
#include "common/mathutil.h"
#include "common/utilities.h"
using namespace angle; using namespace angle;
namespace gl namespace gl
...@@ -1948,13 +1948,14 @@ bool ValidateCopyBufferSubData(ValidationContext *context, ...@@ -1948,13 +1948,14 @@ bool ValidateCopyBufferSubData(ValidationContext *context,
{ {
if (context->getClientMajorVersion() < 3) if (context->getClientMajorVersion() < 3)
{ {
context->handleError(Error(GL_INVALID_OPERATION)); context->handleError(
Error(GL_INVALID_OPERATION, "CopyBufferSubData requires ES 3 or greater"));
return false; return false;
} }
if (!ValidBufferTarget(context, readTarget) || !ValidBufferTarget(context, writeTarget)) if (!ValidBufferTarget(context, readTarget) || !ValidBufferTarget(context, writeTarget))
{ {
context->handleError(Error(GL_INVALID_ENUM)); context->handleError(Error(GL_INVALID_ENUM, "Invalid buffer target"));
return false; return false;
} }
...@@ -1963,31 +1964,68 @@ bool ValidateCopyBufferSubData(ValidationContext *context, ...@@ -1963,31 +1964,68 @@ bool ValidateCopyBufferSubData(ValidationContext *context,
if (!readBuffer || !writeBuffer) if (!readBuffer || !writeBuffer)
{ {
context->handleError(Error(GL_INVALID_OPERATION)); context->handleError(Error(GL_INVALID_OPERATION, "No buffer bound to target"));
return false; return false;
} }
// Verify that readBuffer and writeBuffer are not currently mapped // Verify that readBuffer and writeBuffer are not currently mapped
if (readBuffer->isMapped() || writeBuffer->isMapped()) if (readBuffer->isMapped() || writeBuffer->isMapped())
{ {
context->handleError(Error(GL_INVALID_OPERATION)); context->handleError(
Error(GL_INVALID_OPERATION, "Cannot call CopyBufferSubData on a mapped buffer"));
return false; return false;
} }
if (readOffset < 0 || writeOffset < 0 || size < 0 || CheckedNumeric<GLintptr> checkedReadOffset(readOffset);
static_cast<unsigned int>(readOffset + size) > readBuffer->getSize() || CheckedNumeric<GLintptr> checkedWriteOffset(writeOffset);
static_cast<unsigned int>(writeOffset + size) > writeBuffer->getSize()) CheckedNumeric<GLintptr> checkedSize(size);
auto checkedReadSum = checkedReadOffset + checkedSize;
auto checkedWriteSum = checkedWriteOffset + checkedSize;
if (!checkedReadSum.IsValid() || !checkedWriteSum.IsValid() ||
!IsValueInRangeForNumericType<GLintptr>(readBuffer->getSize()) ||
!IsValueInRangeForNumericType<GLintptr>(writeBuffer->getSize()))
{ {
context->handleError(Error(GL_INVALID_VALUE)); context->handleError(
Error(GL_INVALID_VALUE, "Integer overflow when validating copy offsets."));
return false; return false;
} }
if (readBuffer == writeBuffer && std::abs(readOffset - writeOffset) < size) if (readOffset < 0 || writeOffset < 0 || size < 0)
{ {
context->handleError(Error(GL_INVALID_VALUE)); context->handleError(
Error(GL_INVALID_VALUE, "readOffset, writeOffset and size must all be non-negative"));
return false; return false;
} }
if (checkedReadSum.ValueOrDie() > readBuffer->getSize() ||
checkedWriteSum.ValueOrDie() > writeBuffer->getSize())
{
context->handleError(
Error(GL_INVALID_VALUE, "Buffer offset overflow in CopyBufferSubData"));
return false;
}
if (readBuffer == writeBuffer)
{
auto checkedOffsetDiff = (checkedReadOffset - checkedWriteOffset).Abs();
if (!checkedOffsetDiff.IsValid())
{
// This shold not be possible.
UNREACHABLE();
context->handleError(
Error(GL_INVALID_VALUE, "Integer overflow when validating same buffer copy."));
return false;
}
if (checkedOffsetDiff.ValueOrDie() < size)
{
context->handleError(Error(GL_INVALID_VALUE));
return false;
}
}
return true; return true;
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
// //
#include "test_utils/ANGLETest.h" #include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include <stdint.h> #include <stdint.h>
...@@ -447,24 +448,8 @@ class BufferDataOverflowTest : public ANGLETest ...@@ -447,24 +448,8 @@ class BufferDataOverflowTest : public ANGLETest
{ {
protected: protected:
BufferDataOverflowTest() BufferDataOverflowTest()
: mProgram(0)
{ {
} }
~BufferDataOverflowTest()
{
if (!mBuffers.empty())
{
glDeleteBuffers(static_cast<GLsizei>(mBuffers.size()), &mBuffers[0]);
}
if (mProgram != 0u)
{
glDeleteProgram(mProgram);
}
}
std::vector<GLuint> mBuffers;
GLuint mProgram;
}; };
// See description above. // See description above.
...@@ -472,9 +457,9 @@ TEST_P(BufferDataOverflowTest, VertexBufferIntegerOverflow) ...@@ -472,9 +457,9 @@ TEST_P(BufferDataOverflowTest, VertexBufferIntegerOverflow)
{ {
// These values are special, to trigger the rounding bug. // These values are special, to trigger the rounding bug.
unsigned int numItems = 0x7FFFFFE; unsigned int numItems = 0x7FFFFFE;
GLsizei bufferCnt = 8; constexpr GLsizei bufferCnt = 8;
mBuffers.resize(bufferCnt); std::vector<GLBuffer> buffers(bufferCnt);
std::stringstream vertexShaderStr; std::stringstream vertexShaderStr;
...@@ -502,30 +487,27 @@ TEST_P(BufferDataOverflowTest, VertexBufferIntegerOverflow) ...@@ -502,30 +487,27 @@ TEST_P(BufferDataOverflowTest, VertexBufferIntegerOverflow)
" gl_FragColor = vec4(v_attrib, 0, 0, 1);\n" " gl_FragColor = vec4(v_attrib, 0, 0, 1);\n"
"}"; "}";
mProgram = CompileProgram(vertexShaderStr.str(), fragmentShader); ANGLE_GL_PROGRAM(program, vertexShaderStr.str(), fragmentShader);
ASSERT_NE(0u, mProgram); glUseProgram(program.get());
glUseProgram(mProgram);
glGenBuffers(bufferCnt, &mBuffers[0]);
std::vector<GLfloat> data(numItems, 1.0f); std::vector<GLfloat> data(numItems, 1.0f);
for (GLsizei bufferIndex = 0; bufferIndex < bufferCnt; ++bufferIndex) for (GLsizei bufferIndex = 0; bufferIndex < bufferCnt; ++bufferIndex)
{ {
glBindBuffer(GL_ARRAY_BUFFER, mBuffers[bufferIndex]); glBindBuffer(GL_ARRAY_BUFFER, buffers[bufferIndex].get());
glBufferData(GL_ARRAY_BUFFER, numItems * sizeof(float), &data[0], GL_DYNAMIC_DRAW); glBufferData(GL_ARRAY_BUFFER, numItems * sizeof(float), &data[0], GL_DYNAMIC_DRAW);
std::stringstream attribNameStr; std::stringstream attribNameStr;
attribNameStr << "attrib" << bufferIndex; attribNameStr << "attrib" << bufferIndex;
GLint attribLocation = glGetAttribLocation(mProgram, attribNameStr.str().c_str()); GLint attribLocation = glGetAttribLocation(program.get(), attribNameStr.str().c_str());
ASSERT_NE(-1, attribLocation); ASSERT_NE(-1, attribLocation);
glVertexAttribPointer(attribLocation, 1, GL_FLOAT, GL_FALSE, 4, nullptr); glVertexAttribPointer(attribLocation, 1, GL_FLOAT, GL_FALSE, 4, nullptr);
glEnableVertexAttribArray(attribLocation); glEnableVertexAttribArray(attribLocation);
} }
GLint positionLocation = glGetAttribLocation(mProgram, "position"); GLint positionLocation = glGetAttribLocation(program.get(), "position");
ASSERT_NE(-1, positionLocation); ASSERT_NE(-1, positionLocation);
glDisableVertexAttribArray(positionLocation); glDisableVertexAttribArray(positionLocation);
glVertexAttrib2f(positionLocation, 1.0f, 1.0f); glVertexAttrib2f(positionLocation, 1.0f, 1.0f);
...@@ -535,6 +517,28 @@ TEST_P(BufferDataOverflowTest, VertexBufferIntegerOverflow) ...@@ -535,6 +517,28 @@ TEST_P(BufferDataOverflowTest, VertexBufferIntegerOverflow)
EXPECT_GL_ERROR(GL_OUT_OF_MEMORY); EXPECT_GL_ERROR(GL_OUT_OF_MEMORY);
} }
// Tests a security bug in our CopyBufferSubData validation (integer overflow).
TEST_P(BufferDataOverflowTest, CopySubDataValidation)
{
GLBuffer readBuffer, writeBuffer;
glBindBuffer(GL_COPY_READ_BUFFER, readBuffer.get());
glBindBuffer(GL_COPY_WRITE_BUFFER, writeBuffer.get());
constexpr int bufSize = 100;
glBufferData(GL_COPY_READ_BUFFER, bufSize, nullptr, GL_STATIC_DRAW);
glBufferData(GL_COPY_WRITE_BUFFER, bufSize, nullptr, GL_STATIC_DRAW);
GLintptr big = std::numeric_limits<GLintptr>::max() - bufSize + 90;
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, big, 0, 50);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, big, 50);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
ANGLE_INSTANTIATE_TEST(BufferDataOverflowTest, ES3_D3D11()); ANGLE_INSTANTIATE_TEST(BufferDataOverflowTest, ES3_D3D11());
#endif // _WIN64 #endif // _WIN64
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment