Fix integer overflows in ClipSrcRect

While blitting, it's possible to generate a float that is outside the
representable values of an integer. Since converting such a float
results in undefined behavior, we instead throw an invalid operation
error and return without continuing the blit.

Bug chromium:1001874

Change-Id: Ic6938adf75176e34021d0ca1404176e4979a3ca6
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/36408
Reviewed-by: Alexis Hétu <sugoi@google.com>
Tested-by: Sean Risser <srisser@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/OpenGL/libGLESv2/Device.cpp b/src/OpenGL/libGLESv2/Device.cpp
index c70da83..0758428 100644
--- a/src/OpenGL/libGLESv2/Device.cpp
+++ b/src/OpenGL/libGLESv2/Device.cpp
@@ -979,7 +979,7 @@
 		{
 			float ratio = static_cast<float>(dstRect.width()) / srcRect.width();
 			float offsetf = roundf((static_cast<float>(clipRect.x0) - srcRect.x0) * ratio);
-			if (!std::isfinite(offsetf) || !std::isfinite(ratio))
+			if (!FloatFitsInInt(offsetf) || !std::isfinite(ratio))
 			{
 				return false;
 			}
@@ -998,7 +998,7 @@
 		{
 			float ratio = static_cast<float>(dstRect.width()) / srcRect.width();
 			float offsetf = roundf((srcRect.x1 - static_cast<float>(clipRect.x1)) * ratio);
-			if (!std::isfinite(offsetf) || !std::isfinite(ratio))
+			if (!FloatFitsInInt(offsetf) || !std::isfinite(ratio))
 			{
 				return false;
 			}
@@ -1017,7 +1017,7 @@
 		{
 			float ratio = static_cast<float>(dstRect.height()) / srcRect.height();
 			float offsetf = roundf((static_cast<float>(clipRect.y0) - srcRect.y0) * ratio);
-			if (!std::isfinite(offsetf) || !std::isfinite(ratio))
+			if (!FloatFitsInInt(offsetf) || !std::isfinite(ratio))
 			{
 				return false;
 			}
@@ -1036,7 +1036,7 @@
 		{
 			float ratio = static_cast<float>(dstRect.height()) / srcRect.height();
 			float offsetf = roundf((srcRect.y1 - static_cast<float>(clipRect.y1)) * ratio);
-			if (!std::isfinite(offsetf) || !std::isfinite(ratio))
+			if (!FloatFitsInInt(offsetf) || !std::isfinite(ratio))
 			{
 				return false;
 			}
diff --git a/src/OpenGL/libGLESv2/utilities.cpp b/src/OpenGL/libGLESv2/utilities.cpp
index 96510e7..b8c91be 100644
--- a/src/OpenGL/libGLESv2/utilities.cpp
+++ b/src/OpenGL/libGLESv2/utilities.cpp
@@ -2070,6 +2070,22 @@
 
 		return name.substr(0, open);
 	}
+
+	bool FloatFitsInInt(float f)
+	{
+		// We can't just do a raw comparison of "f > (float) INT32_MAX",
+		// because "(float) INT32_MAX" is unrepresentable as an integer.
+		//
+		// So instead I subtracted an ULP from "(float) INT32_MAX", cast that
+		// to an int, and do the comparison with that value. That value is
+		// 2147483520, and can be found with the following code:
+		//    float f_max = static_cast<float>(INT32_MAX);
+		//    int32_t f_bits = *static_cast<int32_t *>((void *)&f_max);
+		//    f_bits -= 1;
+		//    float f_next = *static_cast<float *>((void *)&f_bits);
+		//    int32_t out = static_cast<int32_t>(f_next);
+		return std::isfinite(f) && (-2147483520.f < f) && (f < 2147483520.f);
+	}
 }
 
 namespace es2sw
@@ -2401,4 +2417,4 @@
 
 		return GL_DEPTH24_STENCIL8_OES;
 	}
-}
\ No newline at end of file
+}
diff --git a/src/OpenGL/libGLESv2/utilities.h b/src/OpenGL/libGLESv2/utilities.h
index 187ef13..d469d74 100644
--- a/src/OpenGL/libGLESv2/utilities.h
+++ b/src/OpenGL/libGLESv2/utilities.h
@@ -86,6 +86,8 @@
 	// Parse the base uniform name and array index.  Returns the base name of the uniform. outSubscript is
 	// set to GL_INVALID_INDEX if the provided name is not an array or the array index is invalid.
 	std::string ParseUniformName(const std::string &name, unsigned int *outSubscript);
+
+	bool FloatFitsInInt(float f);
 }
 
 namespace es2sw
diff --git a/tests/GLESUnitTests/unittests.cpp b/tests/GLESUnitTests/unittests.cpp
index bfd2112..f4e1a86 100644
--- a/tests/GLESUnitTests/unittests.cpp
+++ b/tests/GLESUnitTests/unittests.cpp
@@ -1764,6 +1764,7 @@
 	const int small = 200;
 	const int neg_small = -small;
 	const int neg_big = -big;
+	int max = 0x7fffffff;
 	int data[][8] = {
 		// sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1
 		{0, 0, 0, 0, 0, 0, 0, 0},
@@ -1776,7 +1777,14 @@
 		{neg_small, small, neg_small, neg_small, neg_small, big, small},
 		{big, big-1, big-2, big-3, big-4, big-5, big-6, big-7},
 		{big, neg_big, neg_big, big, small, big, 0, neg_small},
-		{323479648, 21931, 1769809195, 32733, 0, 0, -161640504, 32766}
+		{323479648, 21931, 1769809195, 32733, 0, 0, -161640504, 32766},
+		{0, 0, max, max, 0, 0, 8, 8},
+		{0, 0, 8, 8, 0, 0, max, max},
+		{0, 0, max, max, 0, 0, max, max},
+		{-1, -1, max, max, 0, 0, 8, 8},
+		{0, 0, 8, 8, -1, -1, max, max},
+		{-1, -1, max, max, -1, -1, max, max},
+		{-max-1, -max-1, max, max, -max-1, -max-1, max, max}
 	};
 
 	for (int i = 0; i < (int) (sizeof(data)/sizeof(data[0])); i++)