Validate xoffset and yoffset are multiples of blocksize.

Affects glCompressedTexSubImage and glCopyCompressedTexSubImage calls.

BUG=668223

Change-Id: Ie71faa1fa7dac12cec51a2e29e0ce212ac54e411
Reviewed-on: https://chromium-review.googlesource.com/437605
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 2297f6f..78073e4 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -1718,6 +1718,8 @@
 
 bool ValidCompressedImageSize(const ValidationContext *context,
                               GLenum internalFormat,
+                              GLint xoffset,
+                              GLint yoffset,
                               GLsizei width,
                               GLsizei height)
 {
@@ -1727,14 +1729,16 @@
         return false;
     }
 
-    if (width < 0 || height < 0)
+    if (xoffset < 0 || yoffset < 0 || width < 0 || height < 0)
     {
         return false;
     }
 
     if (CompressedTextureFormatRequiresExactSize(internalFormat))
     {
-        if ((static_cast<GLuint>(width) > formatInfo.compressedBlockWidth &&
+        if (xoffset % formatInfo.compressedBlockWidth != 0 ||
+            yoffset % formatInfo.compressedBlockHeight != 0 ||
+            (static_cast<GLuint>(width) > formatInfo.compressedBlockWidth &&
              width % formatInfo.compressedBlockWidth != 0) ||
             (static_cast<GLuint>(height) > formatInfo.compressedBlockHeight &&
              height % formatInfo.compressedBlockHeight != 0))
@@ -3143,7 +3147,8 @@
         return false;
     }
 
-    if (formatInfo.compressed && !ValidCompressedImageSize(context, internalformat, width, height))
+    if (formatInfo.compressed &&
+        !ValidCompressedImageSize(context, internalformat, xoffset, yoffset, width, height))
     {
         context->handleError(Error(GL_INVALID_OPERATION));
         return false;
diff --git a/src/libANGLE/validationES.h b/src/libANGLE/validationES.h
index 6f5fbb3..24306e7 100644
--- a/src/libANGLE/validationES.h
+++ b/src/libANGLE/validationES.h
@@ -49,6 +49,8 @@
                               bool isSubImage);
 bool ValidCompressedImageSize(const ValidationContext *context,
                               GLenum internalFormat,
+                              GLint xoffset,
+                              GLint yoffset,
                               GLsizei width,
                               GLsizei height);
 bool ValidImageDataSize(ValidationContext *context,
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index b107f68..43a1b47 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -467,7 +467,8 @@
                           "internalformat is not a supported compressed internal format"));
                 return false;
         }
-        if (!ValidCompressedImageSize(context, actualInternalFormat, width, height))
+        if (!ValidCompressedImageSize(context, actualInternalFormat, xoffset, yoffset, width,
+                                      height))
         {
             context->handleError(Error(GL_INVALID_OPERATION));
             return false;
diff --git a/src/libANGLE/validationES3.cpp b/src/libANGLE/validationES3.cpp
index 9754d5e..cf368e9 100644
--- a/src/libANGLE/validationES3.cpp
+++ b/src/libANGLE/validationES3.cpp
@@ -188,7 +188,8 @@
             return false;
         }
 
-        if (!ValidCompressedImageSize(context, actualInternalFormat, width, height))
+        if (!ValidCompressedImageSize(context, actualInternalFormat, xoffset, yoffset, width,
+                                      height))
         {
             context->handleError(Error(GL_INVALID_OPERATION));
             return false;
diff --git a/src/tests/gl_tests/DXT1CompressedTextureTest.cpp b/src/tests/gl_tests/DXT1CompressedTextureTest.cpp
index 34b6b9e..b2a932f 100644
--- a/src/tests/gl_tests/DXT1CompressedTextureTest.cpp
+++ b/src/tests/gl_tests/DXT1CompressedTextureTest.cpp
@@ -5,6 +5,7 @@
 //
 
 #include "test_utils/ANGLETest.h"
+#include "test_utils/gl_raii.h"
 
 #include "media/pixel.inl"
 
@@ -174,6 +175,29 @@
     EXPECT_GL_NO_ERROR();
 }
 
+// Test validation of glCompressedTexSubImage2D with DXT formats
+TEST_P(DXT1CompressedTextureTest, CompressedTexSubImageValidation)
+{
+    if (!extensionEnabled("GL_EXT_texture_compression_dxt1"))
+    {
+        std::cout << "Test skipped due to missing GL_EXT_texture_compression_dxt1" << std::endl;
+        return;
+    }
+
+    GLTexture texture;
+    glBindTexture(GL_TEXTURE_2D, texture.get());
+
+    // Size mip 0 to a large size
+    glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, pixel_0_width,
+                           pixel_0_height, 0, pixel_0_size, nullptr);
+    ASSERT_GL_NO_ERROR();
+
+    // Set a sub image with an offset that isn't a multiple of the block size
+    glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 1, 3, pixel_1_width, pixel_1_height,
+                              GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, pixel_1_size, pixel_1_data);
+    ASSERT_GL_ERROR(GL_INVALID_OPERATION);
+}
+
 class DXT1CompressedTextureTestES3 : public DXT1CompressedTextureTest { };
 
 class DXT1CompressedTextureTestD3D11 : public DXT1CompressedTextureTest { };