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: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/validationES3.cpp b/src/libANGLE/validationES3.cpp
index bbacace..3b3a568 100644
--- a/src/libANGLE/validationES3.cpp
+++ b/src/libANGLE/validationES3.cpp
@@ -8,6 +8,9 @@
 
 #include "libANGLE/validationES3.h"
 
+#include "base/numerics/safe_conversions.h"
+#include "common/mathutil.h"
+#include "common/utilities.h"
 #include "libANGLE/validationES.h"
 #include "libANGLE/Context.h"
 #include "libANGLE/Texture.h"
@@ -16,9 +19,6 @@
 #include "libANGLE/formatutils.h"
 #include "libANGLE/FramebufferAttachment.h"
 
-#include "common/mathutil.h"
-#include "common/utilities.h"
-
 using namespace angle;
 
 namespace gl
@@ -1948,13 +1948,14 @@
 {
     if (context->getClientMajorVersion() < 3)
     {
-        context->handleError(Error(GL_INVALID_OPERATION));
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "CopyBufferSubData requires ES 3 or greater"));
         return false;
     }
 
     if (!ValidBufferTarget(context, readTarget) || !ValidBufferTarget(context, writeTarget))
     {
-        context->handleError(Error(GL_INVALID_ENUM));
+        context->handleError(Error(GL_INVALID_ENUM, "Invalid buffer target"));
         return false;
     }
 
@@ -1963,31 +1964,68 @@
 
     if (!readBuffer || !writeBuffer)
     {
-        context->handleError(Error(GL_INVALID_OPERATION));
+        context->handleError(Error(GL_INVALID_OPERATION, "No buffer bound to target"));
         return false;
     }
 
     // Verify that readBuffer and writeBuffer are not currently mapped
     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;
     }
 
-    if (readOffset < 0 || writeOffset < 0 || size < 0 ||
-        static_cast<unsigned int>(readOffset + size) > readBuffer->getSize() ||
-        static_cast<unsigned int>(writeOffset + size) > writeBuffer->getSize())
+    CheckedNumeric<GLintptr> checkedReadOffset(readOffset);
+    CheckedNumeric<GLintptr> checkedWriteOffset(writeOffset);
+    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;
     }
 
-    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;
     }
 
+    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;
 }