Commit 7a7f630f by Sean Risser

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/+/36408Reviewed-by: 's avatarAlexis Hétu <sugoi@google.com> Tested-by: 's avatarSean Risser <srisser@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
parent 30847688
...@@ -979,7 +979,7 @@ namespace es2 ...@@ -979,7 +979,7 @@ namespace es2
{ {
float ratio = static_cast<float>(dstRect.width()) / srcRect.width(); float ratio = static_cast<float>(dstRect.width()) / srcRect.width();
float offsetf = roundf((static_cast<float>(clipRect.x0) - srcRect.x0) * ratio); 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; return false;
} }
...@@ -998,7 +998,7 @@ namespace es2 ...@@ -998,7 +998,7 @@ namespace es2
{ {
float ratio = static_cast<float>(dstRect.width()) / srcRect.width(); float ratio = static_cast<float>(dstRect.width()) / srcRect.width();
float offsetf = roundf((srcRect.x1 - static_cast<float>(clipRect.x1)) * ratio); 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; return false;
} }
...@@ -1017,7 +1017,7 @@ namespace es2 ...@@ -1017,7 +1017,7 @@ namespace es2
{ {
float ratio = static_cast<float>(dstRect.height()) / srcRect.height(); float ratio = static_cast<float>(dstRect.height()) / srcRect.height();
float offsetf = roundf((static_cast<float>(clipRect.y0) - srcRect.y0) * ratio); 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; return false;
} }
...@@ -1036,7 +1036,7 @@ namespace es2 ...@@ -1036,7 +1036,7 @@ namespace es2
{ {
float ratio = static_cast<float>(dstRect.height()) / srcRect.height(); float ratio = static_cast<float>(dstRect.height()) / srcRect.height();
float offsetf = roundf((srcRect.y1 - static_cast<float>(clipRect.y1)) * ratio); 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; return false;
} }
......
...@@ -2070,6 +2070,22 @@ namespace es2 ...@@ -2070,6 +2070,22 @@ namespace es2
return name.substr(0, open); 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 namespace es2sw
......
...@@ -86,6 +86,8 @@ namespace es2 ...@@ -86,6 +86,8 @@ namespace es2
// Parse the base uniform name and array index. Returns the base name of the uniform. outSubscript is // 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. // 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); std::string ParseUniformName(const std::string &name, unsigned int *outSubscript);
bool FloatFitsInInt(float f);
} }
namespace es2sw namespace es2sw
......
...@@ -1764,6 +1764,7 @@ TEST_F(SwiftShaderTest, BlitTest) ...@@ -1764,6 +1764,7 @@ TEST_F(SwiftShaderTest, BlitTest)
const int small = 200; const int small = 200;
const int neg_small = -small; const int neg_small = -small;
const int neg_big = -big; const int neg_big = -big;
int max = 0x7fffffff;
int data[][8] = { int data[][8] = {
// sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 // sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1
{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0},
...@@ -1776,7 +1777,14 @@ TEST_F(SwiftShaderTest, BlitTest) ...@@ -1776,7 +1777,14 @@ TEST_F(SwiftShaderTest, BlitTest)
{neg_small, small, neg_small, neg_small, neg_small, big, 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, 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}, {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++) for (int i = 0; i < (int) (sizeof(data)/sizeof(data[0])); i++)
......
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