Float-cast-overflow in gfx::Transform::IsIdentityOrIntegerTranslation |
|||||
Issue descriptionDetailed report: https://cluster-fuzz.appspot.com/testcase?key=4689749039382528 Fuzzer: ifratric-browserfuzzer-v3 Job Type: linux_ubsan_chrome Platform Id: linux Crash Type: Float-cast-overflow Crash Address: Crash State: gfx::Transform::IsIdentityOrIntegerTranslation gfx::Transform::IsIdentityOrIntegerTranslation cc::MathUtil::ProjectEnclosingClippedRect Regressed: https://cluster-fuzz.appspot.com/revisions?job=linux_ubsan_chrome&range=435261:438085 Unminimized Testcase: https://cluster-fuzz.appspot.com/download/AMIfv95oiEF8JPi5seww9Szz2hfq-3T1iMCTBbJpcVSev8SWA4XZW_KdDFl6icXHX79tzX4YgHjOOfdHkBpr5p8wL1x4hzrHYD_DYwB5ippd1_xC7Iv2yuGpOnsVIUqK9bM8sCM59YgBQDxsHhm88jEOr0If5LO1OS5ozyzIUASACzDJIzbmKtI?testcase_id=4689749039382528 Issue filed automatically. See https://dev.chromium.org/Home/chromium-security/bugs/reproducing-clusterfuzz-bugs for more information.
,
Dec 19 2016
So the code in question here is:
bool Transform::IsIdentityOrIntegerTranslation() const {
if (!IsIdentityOrTranslation())
return false;
bool no_fractional_translation =
static_cast<int>(matrix_.get(0, 3)) == matrix_.get(0, 3) &&
static_cast<int>(matrix_.get(1, 3)) == matrix_.get(1, 3) &&
static_cast<int>(matrix_.get(2, 3)) == matrix_.get(2, 3);
return no_fractional_translation;
}
We're trying to tell if the floats have a non-integer component, but it is out of the range of ints so UBSAN is not happy - and maybe it would not give the right result then, I'm not sure.
,
Dec 19 2016
Perhaps these should be floorf(f) == f instead.
,
Dec 19 2016
Or static_cast<long> maybe? Not sure which is better efficiency-wise.
,
Dec 19 2016
Oh, nvm, static_cast<long> has the same problem as static_cast<int>, just for larger floats.
,
Dec 19 2016
Yeh, floats can be large :(
,
Dec 19 2016
Unfortunately, floorf is worse, because for big floats the imprecision problems kick in, and you get false positives.
,
Dec 19 2016
This passes with static_cast<int> but fails with floorf float max_int = std::numeric_limits<int>::max(); transform.MakeIdentity(); transform.Translate3d(0, 0, max_int + 1000.5f); EXPECT_FALSE(transform.IsIdentityOrIntegerTranslation());
,
Dec 19 2016
Does it help to static_cast to double before calling floorf?
,
Dec 19 2016
For that to work it would have to be the case that for any value representable by a float but stored in a double, taking the floor() of it creates a new representation. How to verify that..
,
Dec 19 2016
Using doubles does not pass for the following
float max_int = std::numeric_limits<int>::max();
transform.MakeIdentity();
transform.Translate3d(0, 0, max_int + 1000.5f);
EXPECT_FALSE(transform.IsIdentityOrIntegerTranslation());
float max_float = std::numeric_limits<float>::max();
transform.MakeIdentity();
transform.Translate3d(0, 0, max_float - 0.5f);
EXPECT_FALSE(transform.IsIdentityOrIntegerTranslation());
I did this:
double zero = matrix_.get(0, 3);
double one = matrix_.get(1, 3);
double two = matrix_.get(2, 3);
bool no_fractional_translation =
floor(zero) == zero &&
floor(one) == one &&
floor(two) == two;
,
Dec 19 2016
Of course, static_cast works for both of those.. :)
,
Dec 19 2016
Altho, the question is why does static_cast work. Because when you take a float non-representable by an int, and static_cast it you get some undefined int. When you turn that back into a float, you get something that is not equal of course. That means that for any float outside of max/min int, this returns false (even if it has no fractional part), which I verified. Saturated cast would do the same thing, except perhaps right on the boundary - as max_int+0.001 would saturated_cast to max_int which may be reprented as a float the same as max_int+0.001 (for small enough epsilon) and thus you get |true| for a fractional float. Similarly, if you took static_cast<float>(MAX_INT)-0.0001f, that would (for small enough epsilon) not be represented differently than just static_cast<float>(MAX_INT) so one of the two would give the wrong answer since they both will cast to the same int, even tho both are inside the range of integers. I think what I'm trying to say is that we can't really return a correct answer here always afaict. But it's okay to return |false| incorrectly, just not to return |true| because returning |false| prevents us from taking shortcuts only.
,
Dec 19 2016
Also, I guess starting from an integer here doesn't really matter, because that's not reality. If you translate by max_int-30 and that ends up represented as slightly fractional, we can't tell if you really intended an integer or not, we lose that. All we know is that it's fractional. So thinking about it being the wrong answer to go from int to float isn't the right way to think about it I guess. So, what am I saying... - If the float is not representable by an int, we can return false. - If it is representable, then we can static_cast it, but float->int->float==float does not mean that the float has no fraction. It means that if you took the integer and made it a float, you'd get that result. - When we return true, code will as a result static_cast the values to ints and use them. At that point, we can't actually tell if that is right or wrong, or more right or more wrong than using the floats directly.. so this seems probably just fine. So I think we just want a IsValueInRangeForNumericType() early out.
,
Dec 19 2016
,
Dec 19 2016
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/602038a90b8b56b4a96a619962f41f03d42b3c46 commit 602038a90b8b56b4a96a619962f41f03d42b3c46 Author: danakj <danakj@chromium.org> Date: Mon Dec 19 22:06:46 2016 Avoid overflow when checking if a transform is integer translation. If the floating point number in the matrix can not be represented as an integer then we should return false so that the calling code does not try to cast it as one. For large enough integers this may return an incorrect result still, as it may return true if the floating point representation has a fractional part if that floating point representation also is used for an integer converted to float. But at that point we can not tell which would be closer to the original intent - using the integer-casted number or the floating point one, so we leave the result as before. R=ajuma@chromium.org BUG= 675435 Review-Url: https://codereview.chromium.org/2585263003 Cr-Commit-Position: refs/heads/master@{#439584} [modify] https://crrev.com/602038a90b8b56b4a96a619962f41f03d42b3c46/ui/gfx/transform.cc [modify] https://crrev.com/602038a90b8b56b4a96a619962f41f03d42b3c46/ui/gfx/transform.h [modify] https://crrev.com/602038a90b8b56b4a96a619962f41f03d42b3c46/ui/gfx/transform_unittest.cc
,
Dec 19 2016
,
Dec 20 2016
ClusterFuzz has detected this issue as fixed in range 439552:439637. Detailed report: https://cluster-fuzz.appspot.com/testcase?key=4689749039382528 Fuzzer: ifratric-browserfuzzer-v3 Job Type: linux_ubsan_chrome Platform Id: linux Crash Type: Float-cast-overflow Crash Address: Crash State: gfx::Transform::IsIdentityOrIntegerTranslation gfx::Transform::IsIdentityOrIntegerTranslation cc::MathUtil::ProjectEnclosingClippedRect Regressed: https://cluster-fuzz.appspot.com/revisions?job=linux_ubsan_chrome&range=435261:438085 Fixed: https://cluster-fuzz.appspot.com/revisions?job=linux_ubsan_chrome&range=439552:439637 Unminimized Testcase: https://cluster-fuzz.appspot.com/download/AMIfv95oiEF8JPi5seww9Szz2hfq-3T1iMCTBbJpcVSev8SWA4XZW_KdDFl6icXHX79tzX4YgHjOOfdHkBpr5p8wL1x4hzrHYD_DYwB5ippd1_xC7Iv2yuGpOnsVIUqK9bM8sCM59YgBQDxsHhm88jEOr0If5LO1OS5ozyzIUASACzDJIzbmKtI?testcase_id=4689749039382528 See https://dev.chromium.org/Home/chromium-security/bugs/reproducing-clusterfuzz-bugs for more information. If you suspect that the result above is incorrect, try re-doing that job on the test case report page.
,
Dec 20 2016
Yiaa Pada tanggal 20 Des 2016 4.13 PM, "clusterf… via monorail" < monorail+v2.2757933724@chromium.org> menulis: |
|||||
►
Sign in to add a comment |
|||||
Comment 1 by msrchandra@chromium.org
, Dec 19 2016Components: Internals>Compositing
Labels: Test-Predator-Correct-CLs
Owner: danakj@chromium.org
Status: Assigned (was: Untriaged)