diff options
author | zmo@chromium.org <zmo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-24 22:59:31 +0000 |
---|---|---|
committer | zmo@chromium.org <zmo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-24 22:59:31 +0000 |
commit | db2833886f19cd4a687b5159e066e89d889ad21a (patch) | |
tree | d8a19291ec7886d680b452f6a198bc9f5c6973b9 /gpu | |
parent | f4e972d7809c0383fb61593f0376fc13454c031a (diff) | |
download | chromium_src-db2833886f19cd4a687b5159e066e89d889ad21a.zip chromium_src-db2833886f19cd4a687b5159e066e89d889ad21a.tar.gz chromium_src-db2833886f19cd4a687b5159e066e89d889ad21a.tar.bz2 |
Disallow sizes and offsets that can't fit into a 32-bit integer in GL.
So we have consistent behaviors on various systems.
BUG=363869
TEST=gpu_unittests, http://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/draw-elements-out-of-bounds.html on 64 bit systems.
R=piman@chromium.org, kbr@chromium.org
Review URL: https://codereview.chromium.org/246403012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266021 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu')
-rw-r--r-- | gpu/command_buffer/client/gles2_implementation.cc | 60 | ||||
-rw-r--r-- | gpu/command_buffer/client/gles2_implementation.h | 8 | ||||
-rw-r--r-- | gpu/command_buffer/client/gles2_implementation_unittest.cc | 106 | ||||
-rw-r--r-- | gpu/command_buffer/common/gles2_cmd_utils.h | 8 |
4 files changed, 172 insertions, 10 deletions
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index 6b64d82..3fe999a 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc @@ -865,6 +865,10 @@ void GLES2Implementation::DrawElements( if (count == 0) { return; } + if (vertex_array_object_manager_->bound_element_array_buffer() != 0 && + !ValidateOffset("glDrawElements", reinterpret_cast<GLintptr>(indices))) { + return; + } GLuint offset = 0; bool simulated = false; if (!vertex_array_object_manager_->SetupSimulatedIndexAndClientSideBuffers( @@ -1266,7 +1270,6 @@ void GLES2Implementation::PixelStorei(GLenum pname, GLint param) { CheckGLError(); } - void GLES2Implementation::VertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) { @@ -1288,10 +1291,18 @@ void GLES2Implementation::VertexAttribPointer( #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) if (bound_array_buffer_id_ != 0) { // Only report NON client side buffers to the service. + if (!ValidateOffset("glVertexAttribPointer", + reinterpret_cast<GLintptr>(ptr))) { + return; + } helper_->VertexAttribPointer(index, size, type, normalized, stride, ToGLuint(ptr)); } #else // !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) + if (!ValidateOffset("glVertexAttribPointer", + reinterpret_cast<GLintptr>(ptr))) { + return; + } helper_->VertexAttribPointer(index, size, type, normalized, stride, ToGLuint(ptr)); #endif // !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) @@ -1381,10 +1392,8 @@ void GLES2Implementation::ShaderSource( void GLES2Implementation::BufferDataHelper( GLenum target, GLsizeiptr size, const void* data, GLenum usage) { - if (size < 0) { - SetGLError(GL_INVALID_VALUE, "glBufferData", "size < 0"); + if (!ValidateSize("glBufferData", size)) return; - } GLuint buffer_id; if (GetBoundPixelTransferBuffer(target, "glBufferData", &buffer_id)) { @@ -1455,8 +1464,8 @@ void GLES2Implementation::BufferSubDataHelper( return; } - if (size < 0) { - SetGLError(GL_INVALID_VALUE, "glBufferSubData", "size < 0"); + if (!ValidateSize("glBufferSubData", size) || + !ValidateOffset("glBufferSubData", offset)) { return; } @@ -1893,9 +1902,9 @@ void GLES2Implementation::TexSubImage2D( } static GLint ComputeNumRowsThatFitInBuffer( - GLsizeiptr padded_row_size, GLsizeiptr unpadded_row_size, + uint32 padded_row_size, uint32 unpadded_row_size, unsigned int size) { - DCHECK_GE(unpadded_row_size, 0); + DCHECK_GE(unpadded_row_size, 0u); if (padded_row_size == 0) { return 1; } @@ -2916,10 +2925,11 @@ void* GLES2Implementation::MapBufferSubDataCHROMIUM( "glMapBufferSubDataCHROMIUM", access, "access"); return NULL; } - if (offset < 0 || size < 0) { - SetGLError(GL_INVALID_VALUE, "glMapBufferSubDataCHROMIUM", "bad range"); + if (!ValidateSize("glMapBufferSubDataCHROMIUM", size) || + !ValidateOffset("glMapBufferSubDataCHROMIUM", offset)) { return NULL; } + int32 shm_id; unsigned int shm_offset; void* mem = mapped_memory_->Alloc(size, &shm_id, &shm_offset); @@ -3129,6 +3139,7 @@ void GLES2Implementation::GetMultipleIntegervCHROMIUM( " " << i << ": " << GLES2Util::GetStringGLState(pnames[i])); } }); + DCHECK(size >= 0 && FitInt32NonNegative<GLsizeiptr>(size)); GetMultipleIntegervState state(pnames, count, results, size); if (!GetMultipleIntegervSetup(&state)) { @@ -3536,6 +3547,11 @@ void GLES2Implementation::DrawElementsInstancedANGLE( if (primcount == 0) { return; } + if (vertex_array_object_manager_->bound_element_array_buffer() != 0 && + !ValidateOffset("glDrawElementsInstancedANGLE", + reinterpret_cast<GLintptr>(indices))) { + return; + } GLuint offset = 0; bool simulated = false; if (!vertex_array_object_manager_->SetupSimulatedIndexAndClientSideBuffers( @@ -4097,6 +4113,30 @@ void GLES2Implementation::GetImageParameterivCHROMIUM( CheckGLError(); } +bool GLES2Implementation::ValidateSize(const char* func, GLsizeiptr size) { + if (size < 0) { + SetGLError(GL_INVALID_VALUE, func, "size < 0"); + return false; + } + if (!FitInt32NonNegative<GLsizeiptr>(size)) { + SetGLError(GL_INVALID_OPERATION, func, "size more than 32-bit"); + return false; + } + return true; +} + +bool GLES2Implementation::ValidateOffset(const char* func, GLintptr offset) { + if (offset < 0) { + SetGLError(GL_INVALID_VALUE, func, "offset < 0"); + return false; + } + if (!FitInt32NonNegative<GLintptr>(offset)) { + SetGLError(GL_INVALID_OPERATION, func, "offset more than 32-bit"); + return false; + } + return true; +} + // Include the auto-generated part of this file. We split this because it means // we can easily edit the non-auto generated parts right here in this file // instead of having to edit some template or the code generator. diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h index eaf0d6f..45fb270 100644 --- a/gpu/command_buffer/client/gles2_implementation.h +++ b/gpu/command_buffer/client/gles2_implementation.h @@ -597,6 +597,14 @@ class GLES2_IMPL_EXPORT GLES2Implementation void OnSwapBuffersComplete(); + // Validate if an offset is valid, i.e., non-negative and fit into 32-bit. + // If not, generate an approriate error, and return false. + bool ValidateOffset(const char* func, GLintptr offset); + + // Validate if a size is valid, i.e., non-negative and fit into 32-bit. + // If not, generate an approriate error, and return false. + bool ValidateSize(const char* func, GLsizeiptr offset); + // Remove the transfer buffer from the buffer tracker. For buffers used // asynchronously the memory is free:ed if the upload has completed. For // other buffers, the memory is either free:ed immediately or free:ed pending diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc index cb7f6bd..9224424 100644 --- a/gpu/command_buffer/client/gles2_implementation_unittest.cc +++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc @@ -598,6 +598,10 @@ class GLES2ImplementationTest : public testing::Test { return gl_->GetError(); } + const std::string& GetLastError() { + return gl_->GetLastError(); + } + bool GetBucketContents(uint32 bucket_id, std::vector<int8>* data) { return gl_->GetBucketContents(bucket_id, data); } @@ -3133,6 +3137,108 @@ TEST_F(GLES2ImplementationTest, ProduceTextureCHROMIUM) { EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } +TEST_F(GLES2ImplementationTest, LimitSizeAndOffsetTo32Bit) { + GLsizeiptr size; + GLintptr offset; + if (sizeof(size) <= 4 || sizeof(offset) <= 4) + return; + // The below two casts should be no-op, as we return early if + // it's 32-bit system. + int64 value64 = 0x100000000; + size = static_cast<GLsizeiptr>(value64); + offset = static_cast<GLintptr>(value64); + + const char kSizeOverflowMessage[] = "size more than 32-bit"; + const char kOffsetOverflowMessage[] = "offset more than 32-bit"; + + const GLfloat buf[] = { 1.0, 1.0, 1.0, 1.0 }; + const GLubyte indices[] = { 0 }; + + const GLuint kClientArrayBufferId = 0x789; + const GLuint kClientElementArrayBufferId = 0x790; + gl_->BindBuffer(GL_ARRAY_BUFFER, kClientArrayBufferId); + gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, kClientElementArrayBufferId); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + + // Call BufferData() should succeed with legal paramaters. + gl_->BufferData(GL_ARRAY_BUFFER, sizeof(buf), buf, GL_DYNAMIC_DRAW); + gl_->BufferData( + GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + + // BufferData: size + gl_->BufferData(GL_ARRAY_BUFFER, size, buf, GL_DYNAMIC_DRAW); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + EXPECT_STREQ(kSizeOverflowMessage, GetLastError().c_str()); + + // Call BufferSubData() should succeed with legal paramaters. + gl_->BufferSubData(GL_ARRAY_BUFFER, 0, sizeof(buf[0]), buf); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + + // BufferSubData: offset + gl_->BufferSubData(GL_ARRAY_BUFFER, offset, 1, buf); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); + + // BufferSubData: size + EXPECT_EQ(GL_NO_ERROR, CheckError()); + gl_->BufferSubData(GL_ARRAY_BUFFER, 0, size, buf); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + EXPECT_STREQ(kSizeOverflowMessage, GetLastError().c_str()); + + // Call MapBufferSubDataCHROMIUM() should succeed with legal paramaters. + EXPECT_TRUE(NULL != gl_->MapBufferSubDataCHROMIUM( + GL_ARRAY_BUFFER, 0, 1, GL_WRITE_ONLY)); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + + // MapBufferSubDataCHROMIUM: offset + EXPECT_TRUE(NULL == gl_->MapBufferSubDataCHROMIUM( + GL_ARRAY_BUFFER, offset, 1, GL_WRITE_ONLY)); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); + + // MapBufferSubDataCHROMIUM: size + EXPECT_EQ(GL_NO_ERROR, CheckError()); + EXPECT_TRUE(NULL == gl_->MapBufferSubDataCHROMIUM( + GL_ARRAY_BUFFER, 0, size, GL_WRITE_ONLY)); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + EXPECT_STREQ(kSizeOverflowMessage, GetLastError().c_str()); + + // Call DrawElements() should succeed with legal paramaters. + gl_->DrawElements(GL_POINTS, 1, GL_UNSIGNED_BYTE, NULL); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + + // DrawElements: offset + gl_->DrawElements( + GL_POINTS, 1, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset)); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); + + // Call DrawElementsInstancedANGLE() should succeed with legal paramaters. + gl_->DrawElementsInstancedANGLE(GL_POINTS, 1, GL_UNSIGNED_BYTE, NULL, 1); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + + // DrawElementsInstancedANGLE: offset + gl_->DrawElementsInstancedANGLE( + GL_POINTS, 1, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset), 1); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); + + // Call VertexAttribPointer() should succeed with legal paramaters. + const GLuint kAttribIndex = 1; + const GLsizei kStride = 4; + gl_->VertexAttribPointer( + kAttribIndex, 1, GL_FLOAT, GL_FALSE, kStride, NULL); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + + // VertexAttribPointer: offset + gl_->VertexAttribPointer( + kAttribIndex, 1, GL_FLOAT, GL_FALSE, kStride, + reinterpret_cast<void*>(offset)); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); +} + TEST_F(GLES2ImplementationManualInitTest, LoseContextOnOOM) { ContextInitOptions init_options; init_options.lose_context_when_out_of_memory = true; diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h index 0df1f31..52d93a3 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.h +++ b/gpu/command_buffer/common/gles2_cmd_utils.h @@ -56,6 +56,14 @@ inline bool SafeAddInt32(int32 a, int32 b, int32* dst) { return safe; } +// Return false if |value| is more than a 32 bit integer can represent. +template<typename T> +inline bool FitInt32NonNegative(T value) { + const int32 max = std::numeric_limits<int32>::max(); + return (std::numeric_limits<T>::max() <= max || + value <= static_cast<T>(max)); +} + // Utilties for GLES2 support. class GLES2_UTILS_EXPORT GLES2Util { public: |