Commit 2b97681b by Jamie Madill

Extract validation from VertexDataManager.cpp to the API.

We can check for buffer overflow at draw validation time, before processing any vertex data. BUG=angle:571 Change-Id: I4f49629b98c17ca28e25baed74cad4ae5341b20f Reviewed-on: https://chromium-review.googlesource.com/210647Tested-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShannon Woods <shannonwoods@chromium.org>
parent 1fecbc85
......@@ -50,6 +50,7 @@ class Buffer : public RefCountObject
void markTransformFeedbackUsage();
rx::IndexRangeCache *getIndexRangeCache() { return &mIndexRangeCache; }
const rx::IndexRangeCache *getIndexRangeCache() const { return &mIndexRangeCache; }
private:
DISALLOW_COPY_AND_ASSIGN(Buffer);
......
......@@ -1761,7 +1761,9 @@ void Context::drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instan
}
}
void Context::drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instances)
void Context::drawElements(GLenum mode, GLsizei count, GLenum type,
const GLvoid *indices, GLsizei instances,
const rx::RangeUI &indexRange)
{
ASSERT(mState.getCurrentProgramId() != 0);
......@@ -1795,6 +1797,7 @@ void Context::drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid
VertexArray *vao = mState.getVertexArray();
rx::TranslatedIndexData indexInfo;
indexInfo.indexRange = indexRange;
GLenum err = mRenderer->applyIndexBuffer(indices, vao->getElementArrayBuffer(), count, mode, type, &indexInfo);
if (err != GL_NO_ERROR)
{
......
......@@ -194,7 +194,9 @@ class Context
void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei *bufSize, void* pixels);
void drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instances);
void drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instances);
void drawElements(GLenum mode, GLsizei count, GLenum type,
const GLvoid *indices, GLsizei instances,
const rx::RangeUI &indexRange);
void sync(bool block); // flush/finish
void recordError(const Error &error);
......
......@@ -1294,7 +1294,7 @@ void __stdcall glDrawArrays(GLenum mode, GLint first, GLsizei count)
if (context)
{
if (!ValidateDrawArrays(context, mode, first, count))
if (!ValidateDrawArrays(context, mode, first, count, 0))
{
return;
}
......@@ -1329,12 +1329,13 @@ void __stdcall glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLv
if (context)
{
if (!ValidateDrawElements(context, mode, count, type, indices))
rx::RangeUI indexRange;
if (!ValidateDrawElements(context, mode, count, type, indices, 0, &indexRange))
{
return;
}
context->drawElements(mode, count, type, indices, 0);
context->drawElements(mode, count, type, indices, 0, indexRange);
}
}
......@@ -1347,12 +1348,13 @@ void __stdcall glDrawElementsInstancedANGLE(GLenum mode, GLsizei count, GLenum t
if (context)
{
if (!ValidateDrawElementsInstanced(context, mode, count, type, indices, primcount))
rx::RangeUI indexRange;
if (!ValidateDrawElementsInstanced(context, mode, count, type, indices, primcount, &indexRange))
{
return;
}
context->drawElements(mode, count, type, indices, primcount);
context->drawElements(mode, count, type, indices, primcount, indexRange);
}
}
......
......@@ -16,6 +16,37 @@
namespace rx
{
template <class IndexType>
static RangeUI ComputeTypedRange(const IndexType *indices, GLsizei count)
{
unsigned int minIndex = indices[0];
unsigned int maxIndex = indices[0];
for (GLsizei i = 1; i < count; i++)
{
if (minIndex > indices[i]) minIndex = indices[i];
if (maxIndex < indices[i]) maxIndex = indices[i];
}
return RangeUI(minIndex, maxIndex);
}
RangeUI IndexRangeCache::ComputeRange(GLenum type, const GLvoid *indices, GLsizei count)
{
switch (type)
{
case GL_UNSIGNED_BYTE:
return ComputeTypedRange(static_cast<const GLubyte*>(indices), count);
case GL_UNSIGNED_INT:
return ComputeTypedRange(static_cast<const GLuint*>(indices), count);
case GL_UNSIGNED_SHORT:
return ComputeTypedRange(static_cast<const GLushort*>(indices), count);
default:
UNREACHABLE();
return RangeUI();
}
}
void IndexRangeCache::addRange(GLenum type, unsigned int offset, GLsizei count, const RangeUI &range,
unsigned int streamOffset)
{
......
......@@ -28,6 +28,8 @@ class IndexRangeCache
void invalidateRange(unsigned int offset, unsigned int size);
void clear();
static RangeUI ComputeRange(GLenum type, const GLvoid *indices, GLsizei count);
private:
struct IndexRange
{
......
......@@ -20,42 +20,7 @@
namespace rx
{
IndexDataManager::IndexDataManager(Renderer *renderer) : mRenderer(renderer)
{
mStreamingBufferShort = new StreamingIndexBufferInterface(mRenderer);
if (!mStreamingBufferShort->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_SHORT))
{
delete mStreamingBufferShort;
mStreamingBufferShort = NULL;
}
mStreamingBufferInt = new StreamingIndexBufferInterface(mRenderer);
if (!mStreamingBufferInt->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_INT))
{
delete mStreamingBufferInt;
mStreamingBufferInt = NULL;
}
if (!mStreamingBufferShort)
{
// Make sure both buffers are deleted.
delete mStreamingBufferInt;
mStreamingBufferInt = NULL;
ERR("Failed to allocate the streaming index buffer(s).");
}
mCountingBuffer = NULL;
}
IndexDataManager::~IndexDataManager()
{
delete mStreamingBufferShort;
delete mStreamingBufferInt;
delete mCountingBuffer;
}
static void convertIndices(GLenum sourceType, GLenum destinationType, const void *input, GLsizei count, void *output)
static void ConvertIndices(GLenum sourceType, GLenum destinationType, const void *input, GLsizei count, void *output)
{
if (sourceType == GL_UNSIGNED_BYTE)
{
......@@ -94,35 +59,36 @@ static void convertIndices(GLenum sourceType, GLenum destinationType, const void
else UNREACHABLE();
}
template <class IndexType>
static RangeUI computeRange(const IndexType *indices, GLsizei count)
IndexDataManager::IndexDataManager(Renderer *renderer)
: mRenderer(renderer)
{
unsigned int minIndex = indices[0];
unsigned int maxIndex = indices[0];
mStreamingBufferShort = new StreamingIndexBufferInterface(mRenderer);
if (!mStreamingBufferShort->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_SHORT))
{
SafeDelete(mStreamingBufferShort);
}
mStreamingBufferInt = new StreamingIndexBufferInterface(mRenderer);
if (!mStreamingBufferInt->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_INT))
{
SafeDelete(mStreamingBufferInt);
}
for (GLsizei i = 1; i < count; i++)
if (!mStreamingBufferShort)
{
if (minIndex > indices[i]) minIndex = indices[i];
if (maxIndex < indices[i]) maxIndex = indices[i];
// Make sure both buffers are deleted.
SafeDelete(mStreamingBufferInt);
ERR("Failed to allocate the streaming index buffer(s).");
}
return RangeUI(minIndex, maxIndex);
mCountingBuffer = NULL;
}
static RangeUI computeRange(GLenum type, const GLvoid *indices, GLsizei count)
IndexDataManager::~IndexDataManager()
{
switch (type)
{
case GL_UNSIGNED_BYTE:
return computeRange(static_cast<const GLubyte*>(indices), count);
case GL_UNSIGNED_INT:
return computeRange(static_cast<const GLuint*>(indices), count);
case GL_UNSIGNED_SHORT:
return computeRange(static_cast<const GLushort*>(indices), count);
default:
UNREACHABLE();
return RangeUI();
}
SafeDelete(mStreamingBufferShort);
SafeDelete(mStreamingBufferInt);
SafeDelete(mCountingBuffer);
}
GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated)
......@@ -184,9 +150,8 @@ GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer
{
streamOffset = offset;
if (!buffer->getIndexRangeCache()->findRange(type, offset, count, &translated->indexRange, NULL))
if (!buffer->getIndexRangeCache()->findRange(type, offset, count, NULL, NULL))
{
translated->indexRange = computeRange(type, indices, count);
buffer->getIndexRangeCache()->addRange(type, offset, count, translated->indexRange, offset);
}
}
......@@ -194,17 +159,12 @@ GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer
{
indexBuffer = staticBuffer;
if (!staticBuffer->getIndexRangeCache()->findRange(type, offset, count, &translated->indexRange, &streamOffset))
if (!staticBuffer->getIndexRangeCache()->findRange(type, offset, count, NULL, &streamOffset))
{
streamOffset = (offset / typeInfo.bytes) * gl::GetTypeInfo(destinationIndexType).bytes;
translated->indexRange = computeRange(type, indices, count);
staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->indexRange, streamOffset);
}
}
else
{
translated->indexRange = computeRange(type, indices, count);
}
// Avoid D3D11's primitive restart index value
// see http://msdn.microsoft.com/en-us/library/windows/desktop/bb205124(v=vs.85).aspx
......@@ -263,7 +223,7 @@ GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer
return GL_OUT_OF_MEMORY;
}
convertIndices(type, destinationIndexType, staticBuffer ? storage->getData() : indices, convertCount, output);
ConvertIndices(type, destinationIndexType, staticBuffer ? storage->getData() : indices, convertCount, output);
if (!indexBuffer->unmapBuffer())
{
......
......@@ -144,13 +144,7 @@ GLenum VertexDataManager::prepareVertexData(const gl::VertexAttribute attribs[],
else
{
int totalCount = StreamingBufferElementCount(attribs[i], count, instances);
// [OpenGL ES 3.0.2] section 2.9.4 page 40:
// We can return INVALID_OPERATION if our vertex attribute does not have enough backing data.
if (bufferImpl && ElementsInBuffer(attribs[i], bufferImpl->getSize()) < totalCount)
{
return GL_INVALID_OPERATION;
}
ASSERT(!bufferImpl || ElementsInBuffer(attribs[i], bufferImpl->getSize()) >= totalCount);
if (!mStreamingBuffer->reserveVertexSpace(attribs[i], totalCount, instances))
{
......@@ -217,13 +211,7 @@ GLenum VertexDataManager::storeAttribute(const gl::VertexAttribute &attrib,
GLsizei instances)
{
gl::Buffer *buffer = attrib.buffer.get();
if (!buffer && attrib.pointer == NULL)
{
// This is an application error that would normally result in a crash, but we catch it and return an error
ERR("An enabled vertex array has no buffer and no pointer.");
return GL_INVALID_OPERATION;
}
ASSERT(buffer || attrib.pointer);
BufferD3D *storage = buffer ? BufferD3D::makeBufferD3D(buffer->getImplementation()) : NULL;
StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBuffer() : NULL;
......
......@@ -20,6 +20,7 @@
#include "libGLESv2/ProgramBinary.h"
#include "libGLESv2/TransformFeedback.h"
#include "libGLESv2/VertexArray.h"
#include "libGLESv2/renderer/BufferImpl.h"
#include "common/mathutil.h"
#include "common/utilities.h"
......@@ -1301,7 +1302,7 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi
return true;
}
static bool ValidateDrawBase(const gl::State &state, GLenum mode, GLsizei count)
static bool ValidateDrawBase(const gl::State &state, GLenum mode, GLsizei count, GLsizei maxVertex, GLsizei primcount)
{
switch (mode)
{
......@@ -1357,11 +1358,55 @@ static bool ValidateDrawBase(const gl::State &state, GLenum mode, GLsizei count)
return gl::error(GL_INVALID_OPERATION, false);
}
// Buffer validations
const VertexArray *vao = state.getVertexArray();
for (int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++)
{
const VertexAttribute &attrib = vao->getVertexAttribute(attributeIndex);
bool attribActive = (programBinary->getSemanticIndex(attributeIndex) != -1);
if (attribActive && attrib.enabled)
{
gl::Buffer *buffer = attrib.buffer.get();
if (buffer)
{
GLint64 attribStride = static_cast<GLint64>(ComputeVertexAttributeStride(attrib));
GLint64 maxVertexElement = 0;
if (attrib.divisor > 0)
{
maxVertexElement = static_cast<GLint64>(primcount) / static_cast<GLint64>(attrib.divisor);
}
else
{
maxVertexElement = static_cast<GLint64>(maxVertex);
}
GLint64 attribDataSize = maxVertexElement * attribStride;
// [OpenGL ES 3.0.2] section 2.9.4 page 40:
// We can return INVALID_OPERATION if our vertex attribute does not have
// enough backing data.
if (attribDataSize > buffer->getSize())
{
return gl::error(GL_INVALID_OPERATION, false);
}
}
else if (attrib.pointer == NULL)
{
// This is an application error that would normally result in a crash,
// but we catch it and return an error
ERR("An enabled vertex array has no buffer and no pointer.");
return gl::error(GL_INVALID_OPERATION, false);
}
}
}
// No-op if zero count
return (count > 0);
}
bool ValidateDrawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count)
bool ValidateDrawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount)
{
if (first < 0)
{
......@@ -1379,7 +1424,7 @@ bool ValidateDrawArrays(const gl::Context *context, GLenum mode, GLint first, GL
return gl::error(GL_INVALID_OPERATION, false);
}
if (!ValidateDrawBase(state, mode, count))
if (!ValidateDrawBase(state, mode, count, count, primcount))
{
return false;
}
......@@ -1394,7 +1439,7 @@ bool ValidateDrawArraysInstanced(const gl::Context *context, GLenum mode, GLint
return gl::error(GL_INVALID_VALUE, false);
}
if (!ValidateDrawArrays(context, mode, first, count))
if (!ValidateDrawArrays(context, mode, first, count, primcount))
{
return false;
}
......@@ -1403,7 +1448,8 @@ bool ValidateDrawArraysInstanced(const gl::Context *context, GLenum mode, GLint
return (primcount > 0);
}
bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)
bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count, GLenum type,
const GLvoid* indices, GLsizei primcount, rx::RangeUI *indexRangeOut)
{
switch (type)
{
......@@ -1436,13 +1482,32 @@ bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count
return gl::error(GL_INVALID_OPERATION, false);
}
gl::VertexArray *vao = state.getVertexArray();
if (!indices && !vao->getElementArrayBuffer())
const gl::VertexArray *vao = state.getVertexArray();
const gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer();
if (!indices && !elementArrayBuffer)
{
return gl::error(GL_INVALID_OPERATION, false);
}
if (!ValidateDrawBase(state, mode, count))
// Use max index to validate if our vertex buffers are large enough for the pull.
// TODO: offer fast path, with disabled index validation.
// TODO: also disable index checking on back-ends that are robust to out-of-range accesses.
if (elementArrayBuffer)
{
unsigned int offset = reinterpret_cast<unsigned int>(indices);
if (!elementArrayBuffer->getIndexRangeCache()->findRange(type, offset, count, indexRangeOut, NULL))
{
const void *dataPointer = elementArrayBuffer->getImplementation()->getData();
const uint8_t *offsetPointer = static_cast<const uint8_t *>(dataPointer) + offset;
*indexRangeOut = rx::IndexRangeCache::ComputeRange(type, offsetPointer, count);
}
}
else
{
*indexRangeOut = rx::IndexRangeCache::ComputeRange(type, indices, count);
}
if (!ValidateDrawBase(state, mode, count, static_cast<GLsizei>(indexRangeOut->end), primcount))
{
return false;
}
......@@ -1450,15 +1515,17 @@ bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count
return true;
}
bool ValidateDrawElementsInstanced(const gl::Context *context, GLenum mode, GLsizei count, GLenum type,
const GLvoid *indices, GLsizei primcount)
bool ValidateDrawElementsInstanced(const gl::Context *context,
GLenum mode, GLsizei count, GLenum type,
const GLvoid *indices, GLsizei primcount,
rx::RangeUI *indexRangeOut)
{
if (primcount < 0)
{
return gl::error(GL_INVALID_VALUE, false);
}
if (!ValidateDrawElements(context, mode, count, type, indices))
if (!ValidateDrawElements(context, mode, count, type, indices, primcount, indexRangeOut))
{
return false;
}
......
......@@ -9,6 +9,8 @@
#ifndef LIBGLESV2_VALIDATION_ES_H
#define LIBGLESV2_VALIDATION_ES_H
#include "common/mathutil.h"
namespace gl
{
......@@ -59,11 +61,14 @@ bool ValidateCopyTexImageParametersBase(gl::Context* context, GLenum target, GLi
GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height,
GLint border, GLenum *textureInternalFormatOut);
bool ValidateDrawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count);
bool ValidateDrawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount);
bool ValidateDrawArraysInstanced(const gl::Context *context, GLenum mode, GLint first, GLsizei count, GLsizei primcount);
bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count, GLenum type, const GLvoid* indices);
bool ValidateDrawElements(const gl::Context *context, GLenum mode, GLsizei count, GLenum type,
const GLvoid* indices, GLsizei primcount, rx::RangeUI *indexRangeOut);
bool ValidateDrawElementsInstanced(const gl::Context *context, GLenum mode, GLsizei count, GLenum type,
const GLvoid *indices, GLsizei primcount);
const GLvoid *indices, GLsizei primcount, rx::RangeUI *indexRangeOut);
bool ValidateFramebufferTextureBase(const gl::Context *context, GLenum target, GLenum attachment,
GLuint texture, GLint level);
......
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