Avoid blitting malformed rectangles

If glBlitFramebuffer tries to blit massive rectangles, it can result in
NaNs. This change ignores those rectangles instead of trying to blit
them.

Added a unittest for blitting a framebuffer.

Also fixed a typo in a debug message.

Bug chromium:979986

Change-Id: Ifd8e379f27b6882044bb93c6a68647b8474e7afc
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/34428
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Presubmit-Ready: Sean Risser <srisser@google.com>
Tested-by: Sean Risser <srisser@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/OpenGL/libGLESv2/Device.cpp b/src/OpenGL/libGLESv2/Device.cpp
index 0c5c63e..da76467 100644
--- a/src/OpenGL/libGLESv2/Device.cpp
+++ b/src/OpenGL/libGLESv2/Device.cpp
@@ -452,6 +452,14 @@
 		int dWidth = dest->getWidth();
 		int dHeight = dest->getHeight();
 
+		if(sourceRect && destRect &&
+			(sourceRect->width() == 0.0f || std::isnan(sourceRect->width()) ||
+			sourceRect->height() == 0.0f || std::isnan(sourceRect->height()) ||
+			destRect->width() == 0.0f || destRect->height() == 0.0f))
+		{
+			return true; // No work to do.
+		}
+
 		bool flipX = false;
 		bool flipY = false;
 		if(sourceRect && destRect)
@@ -528,7 +536,8 @@
 		ClipDstRect(sRect, dRect, dstClipRect, flipX, flipY);
 
 		if((sRect.width() == 0) || (sRect.height() == 0) ||
-		   (dRect.width() == 0) || (dRect.height() == 0))
+		   (dRect.width() == 0) || (dRect.height() == 0) ||
+		   (std::isnan(sRect.width()) || std::isnan(sRect.height())))
 		{
 			return true; // no work to do
 		}
@@ -876,6 +885,11 @@
 			return false;
 		}
 
+		if (std::isnan(rect->x0) || std::isnan(rect->x1) || std::isnan(rect->y0) || std::isnan(rect->y1))
+		{
+			return false;
+		}
+
 		return true;
 	}
 
diff --git a/src/OpenGL/libGLESv2/libGLESv3.cpp b/src/OpenGL/libGLESv2/libGLESv3.cpp
index e513a50..e12ea4d 100644
--- a/src/OpenGL/libGLESv2/libGLESv3.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv3.cpp
@@ -1175,7 +1175,7 @@
 	TRACE("(GLint srcX0 = %d, GLint srcY0 = %d, GLint srcX1 = %d, GLint srcY1 = %d, "
 	      "GLint dstX0 = %d, GLint dstY0 = %d, GLint dstX1 = %d, GLint dstY1 = %d, "
 	      "GLbitfield mask = 0x%X, GLenum filter = 0x%X)",
-	      srcX0, srcY0, srcX1, srcX1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+	      srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
 
 	switch(filter)
 	{
diff --git a/tests/GLESUnitTests/unittests.cpp b/tests/GLESUnitTests/unittests.cpp
index 7951e27..56a377e 100644
--- a/tests/GLESUnitTests/unittests.cpp
+++ b/tests/GLESUnitTests/unittests.cpp
@@ -1717,6 +1717,84 @@
 	Uninitialize();
 }
 
+TEST_F(SwiftShaderTest, BlitTest)
+{
+	Initialize(3, false);
+
+	GLuint fbos[] = {0, 0};
+	glGenFramebuffers(2, fbos);
+
+	glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]);
+	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]);
+
+	GLuint textures[] = {0, 0};
+	glGenTextures(2, textures);
+
+	glBindTexture(GL_TEXTURE_2D, textures[0]);
+	unsigned char red[4][4] = {
+		{255, 0, 0, 255},
+		{255, 0, 0, 255},
+		{255, 0, 0, 255},
+		{255, 0, 0, 255}
+	};
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, red);
+	EXPECT_NO_GL_ERROR();
+
+	glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0);
+	EXPECT_NO_GL_ERROR();
+
+	glBindTexture(GL_TEXTURE_2D, textures[1]);
+	unsigned char black[4][4] = {
+		{0, 0, 0, 255},
+		{0, 0, 0, 255},
+		{0, 0, 0, 255},
+		{0, 0, 0, 255}
+	};
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, black);
+	EXPECT_NO_GL_ERROR();
+	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0);
+
+	// Test that glBlitFramebuffer works as expected for the normal case.
+	glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+	EXPECT_NO_GL_ERROR();
+	EXPECT_EQ(red[0][1], black[0][1]);
+
+	// Check that glBlitFramebuffer doesn't crash with ugly input.
+	glBlitFramebuffer(-2, -2, 127, 2147483470, 10, 10, 200, 200, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+	EXPECT_NO_GL_ERROR();
+
+	const int big = (int) 2e9;
+	const int small = 200;
+	const int neg_small = -small;
+	const int neg_big = -big;
+	int data[][8] = {
+		// sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1
+		{0, 0, 0, 0, 0, 0, 0, 0},
+		{-1, -1, -1, -1, -1, -1, -1, -1},
+		{1, 1, 1, 1, 1, 1, 1, 1},
+		{-1, -1, 1, 1, -1, -1, 1, 1},
+		{0, 0, 127, (int) 2e9, 10, 10, 200, 200},
+		{big, small, small, big, big, big, small, small},
+		{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}
+	};
+
+	for (int i = 0; i < (int) (sizeof(data)/sizeof(data[0])); i++)
+	{
+		glBlitFramebuffer(
+				data[i][0], data[i][1], data[i][2], data[i][3],
+				data[i][4], data[i][5], data[i][6], data[i][7],
+				GL_COLOR_BUFFER_BIT, GL_NEAREST);
+		EXPECT_NO_GL_ERROR();
+	}
+
+	glDeleteFramebuffers(2, fbos);
+	glDeleteTextures(2, textures);
+	Uninitialize();
+}
+
 TEST_F(SwiftShaderTest, InvalidEnum_TexImage2D)
 {
 	Initialize(3, false);