By Mark Brand and Ivan Fratric, Google Project Zero
In this blog post, we are going to write about a seldom seen vulnerability class that typically affects graphic libraries (though it can also occur in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r types of software). The root cause of such issues is using limited precision arithmetic in cases where a precision error would invalidate security assumptions made by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 application.
While we could also call ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r classes of bugs precision issues, namely integer overflows, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 major difference is: with integer overflows, we are dealing with arithmetic operations where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 magnitude of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result is too large to be accurately represented in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 given precision. With cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issues described in this blog post, we are dealing with arithmetic operations where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 magnitude of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result or a part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result is too small to be accurately represented in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 given precision.
These issues can occur when using floating-point arithmetic in operations where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result is security-sensitive, but, as we’ll demonstrate later, can also occur in integer arithmetic in some cases.
Let’s look at a trivial example:
float a = 100000000;
float b = 1;
float c = a + b;
If we were making cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 computation with arbitrary precision, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result would be 100000001. However, since float typically only allows for 24 bits of precision, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result is actually going to be 100000000. If an application makes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 normally reasonable assumption that a > 0 and b > 0 implies that a + b > a, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n this could lead to issues.
In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 example above, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 difference between a and b is so significant that b completely vanishes in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calculation, but precision errors also happen if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 difference is smaller, for example
float a = 1000;
float b = 1.1111111;
float c = a + b;
The result of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above computation is going to be 1001.111084 and not 1001.1111111 which would be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 accurate result. Here, only a part of b is lost, but even such results can sometimes have interesting consequences.
While we used cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 float type in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above examples, and in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se particular examples using double would result in more accurate computation, similar precision errors can happen with double as well.
In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remainder of this blog post, we are going to show several examples of precision issues with security impact. These issues were independently explored by two Project Zero members: Mark Brand, who looked at SwiftShader, a software OpenGL implementation used in Chrome, and Ivan Fratric, who looked at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Skia graphics library, used in Chrome and Firefox.
SwiftShader
SwiftShader is “a high-performance CPU-based implementation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OpenGL ES and Direct3D 9 graphics APIs”. It’s used in Chrome on all platforms as a fallback rendering option to work around limitations in graphics hardware or drivers, allowing universal use of WebGL and ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r advanced javascript rendering APIs on a far wider range of devices.
The code in SwiftShader needs to handle emulating a wide range of operations that would normally be performed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GPU. One operation that we commonly think of as essentially “free” on a GPU is upscaling, or drawing from a small source texture to a larger area, for example on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 screen. This requires computing memory indexes using non-integer values, which is where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability occurs.
As noted in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original bug report, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code that we’ll look at here is not quite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code which is actually run in practice - SwiftShader uses an LLVM-based JIT engine to optimize performance-critical code at runtime, but that code is more difficult to understand than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir fallback implementation, and both contain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same bug, so we’ll discuss cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fallback code. This code is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy-loop used to copy pixels from one surface to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r during rendering:
source->lockInternal((int)sRect.x0, (int)sRect.y0, sRect.slice, sw::LOCK_READONLY, sw::PUBLIC);
dest->lockInternal(dRect.x0, dRect.y0, dRect.slice, sw::LOCK_WRITEONLY, sw::PUBLIC);
float w = sRect.width() / dRect.width();
float h = sRect.height() / dRect.height();
const float xStart = sRect.x0 + 0.5f * w;
float y = sRect.y0 + 0.5f * h;
float x = xStart;
for(int j = dRect.y0; j < dRect.y1; j++)
{
x = xStart;
for(int i = dRect.x0; i < dRect.x1; i++)
{
// FIXME: Support RGBA mask
dest->copyInternal(source, i, j, x, y, options.filter);
x += w;
}
y += h;
}
source->unlockInternal();
dest->unlockInternal();
}
dest->lockInternal(dRect.x0, dRect.y0, dRect.slice, sw::LOCK_WRITEONLY, sw::PUBLIC);
float w = sRect.width() / dRect.width();
float h = sRect.height() / dRect.height();
const float xStart = sRect.x0 + 0.5f * w;
float y = sRect.y0 + 0.5f * h;
float x = xStart;
for(int j = dRect.y0; j < dRect.y1; j++)
{
x = xStart;
for(int i = dRect.x0; i < dRect.x1; i++)
{
// FIXME: Support RGBA mask
dest->copyInternal(source, i, j, x, y, options.filter);
x += w;
}
y += h;
}
source->unlockInternal();
dest->unlockInternal();
}
So - what highlights this code as problematic? We know prior to entering this function that all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounds-checking has already been performed, and that any call to copyInternal with (i, j) in dRect and (x, y) in sRect will be safe.
The examples in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 introduction above show cases where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting precision error means that a rounding-down occurs - in this case that wouldn’t be enough to produce an interesting security bug. Can we cause floating-point imprecision to result in a larger-than-correct value, leading to (x, y) values that are larger than expected?
If we look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 intention of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 developers is to compute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:
for(int j = dRect.y0; j < dRect.y1; j++)
{
for(int i = dRect.x0; i < dRect.x1; i++)
{
{
for(int i = dRect.x0; i < dRect.x1; i++)
{
x = xStart + (i * w);
Y = yStart + (j * h);
dest->copyInternal(source, i, j, x, y, options.filter);
}
}
dest->copyInternal(source, i, j, x, y, options.filter);
}
}
If this approach had been used instead, we’d still have precision errors - but without cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iterative calculation, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’d be no propagation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 error, and we could expect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 eventual magnitude of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision error to be stable, and in direct proportion to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 operands. With cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iterative calculation as performed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 errors start to propagate/snowball into a larger and larger error.
There are ways to estimate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum error in floating point calculations; and if you really, really need to avoid having extra bounds checks, using this kind of approach and making sure that you have conservative safety margins around those maximum errors might be a complicated and error-prone way to solve this issue. It’s not a great approach to identifying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pathological values that we want here to demonstrate a vulnerability; so instead we’ll take a brute-force approach.
Instinctively, we’re fairly sure that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 multiplicative implementation will be roughly correct, and that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation with iterative addition will be much less correct. Given that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 space of possible inputs is small (Chrome disallows textures with width or height greater than 8192), we can just run a brute force over all ratios of source width to destination width, comparing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two algorithms, and seeing where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 results are most different. (Note that SwiftShader also limits us to even numbers). This leads us to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 values of 5828, 8132; and if we compare cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 computations in this case (left side is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iterative addition, right side is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 multiplication):
0: 1.075012 1.075012
1: 1.791687 1.791687
...
1000: 717.749878 717.749878 Up to here (at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision shown) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 values are still identical
1001: 718.466553 718.466553
...
2046: 1467.391724 1467.391724 At this point, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first significant errors start to occur, but note
2047: 1468.108398 1468.108521 that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "incorrect" result is smaller than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more precise one.
...
2856: 2047.898315 2047.898438
2857: 2048.614990 2048.614990 Here our two computations coincide again, briefly, and from here onwards
2858: 2049.331787 2049.331787 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision errors consistently favour a larger result than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more
2859: 2050.048584 2050.048340 precise calculation.
...
8129: 5827.567871 5826.924805
8130: 5828.284668 5827.641602
8131: 5829.001465 5828.358398 The last index is now sufficiently different that int conversion results in an oob index.
1: 1.791687 1.791687
...
1000: 717.749878 717.749878 Up to here (at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision shown) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 values are still identical
1001: 718.466553 718.466553
...
2046: 1467.391724 1467.391724 At this point, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first significant errors start to occur, but note
2047: 1468.108398 1468.108521 that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "incorrect" result is smaller than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more precise one.
...
2856: 2047.898315 2047.898438
2857: 2048.614990 2048.614990 Here our two computations coincide again, briefly, and from here onwards
2858: 2049.331787 2049.331787 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision errors consistently favour a larger result than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more
2859: 2050.048584 2050.048340 precise calculation.
...
8129: 5827.567871 5826.924805
8130: 5828.284668 5827.641602
8131: 5829.001465 5828.358398 The last index is now sufficiently different that int conversion results in an oob index.
(Note also that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re will also be error in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “safe” calculation; it’s just that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lack of error propagation means that that error will remain directly proportional to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input error, which we expect to be “small.”)
We can indeed see that, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 multiplicative algorithm would remain within bounds; but that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iterative algorithm can return an index that is outside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounds of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input texture!
As a result, we read an entire row of pixels past cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of our texture allocation - and this can be easily leaked back to javascript using WebGL. Stay tuned for an upcoming blog post in which we’ll use this vulnerability togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r with anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r unrelated issue in SwiftShader to take control of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GPU process from javascript.
Skia
Skia is a graphics library used, among ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r places, in Chrome, Firefox and Android. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 web browsers it is used for example when drawing to a canvas HTML element using CanvasRenderingContext2D or when drawing SVG images. Skia is also used when drawing various ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r HTML elements, but canvas element and SVG images are more interesting from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security perspective because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y enable more direct control over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 objects being drawn by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 graphic library.
The most complex type of object (and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore, most interesting from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security perspective) that Skia can draw is a path. A path is an object that consists of elements such as lines, but also more complex curves, in particular quadratic or cubic splines.
Due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way software drawing algorithms work in Skia, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision issues are very much possible and quite impactful when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y happen, typically leading to out-of-bounds writes.
To understand why cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se issues can happen, let’s assume you have an image in memory (represented as a buffer with size = width x height x color size). Normally, when drawing a pixel with coordinates (x, y) and color c, you would want to make sure that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pixel actually falls within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 space of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image, specifically that 0 <= x < width and 0 <= y < height. Failing to check this could result in attempting to write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pixel outside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounds of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 allocated buffer. In computer graphics, making sure that only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 objects in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image region are being drawn is called clipping.
So, where is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 problem? Making a clip check for every pixel is expensive in terms of CPU cycles and Skia prides itself on speed. So, instead of making a clip check for every pixel, what Skia does is, it first makes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clip check on an entire object (e.g. line, path or any ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r type of object being drawn). Depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clip check, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are three possible outcomes:
- The object is completely outside of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area: The drawing function doesn’t draw anything and returns immediately.
- The object is partially inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area: The drawing function proceeds with per-pixel clip enabled (usually by relying on SkRectClipBlitter).
- The entire object is in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area: The drawing function draws directly into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer without performing per-pixel clip checks.
The problematic scenario is c) where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clip check is performed only per-object and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more precise, per-pixel checks are disabled. This means, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is a precision issue somewhere between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-object clip check and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing of pixels and if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision issue causes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pixel coordinates to go outside of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area, this could result in a security vulnerability.
We can see per-object clip checks leading to dropping per-pixel checks in several places, for example:
- In hair_path (function for drawing a path without filling), clip is initially set to null (which disables clip checks). The clip is only set if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounds of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path, rounded up and extended by 1 or 2 depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing options don’t fit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area. Extending cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path bounds by 1 seems like a pretty large safety margin, but it is actually cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 least possible safe value because drawing objects with antialiasing on will sometimes result in drawing to nearby pixels.
- In SkScan::FillPath (function for filling a path with antialiasing turned off), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounds of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path are first extended by kConservativeRoundBias and rounded to obtain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “conservative” path bounds. A SkScanClipper object is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n created for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current path. As we can see in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 definition of SkScanClipper, it will only use SkRectClipBlitter if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x coordinates of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path bounds are outside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area or if irPreClipped is true (which only happens when path coordinates are very large).
Similar patterns can be seen in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r drawing functions.
Before we take a closer look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issues, it is useful to quickly go over various number formats used by Skia:
- SkScalar is a 32-bit floating point number
- SkFDot6 is defined as an integer, but it is actually a fixed-point number with 26 bits to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 left and 6 bits to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decimal point. For example, SkFDot6 value of 0x00000001 represents cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number 1/64.
- SkFixed is also a fixed-point number, this time with 16 bits to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 left and 16 bits to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decimal point. For example, SkFixed value of 0x00000001 represents 1/(2**16)
Precision error with integer to float conversion
We discovered cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 initial problem when doing DOM fuzzing against Firefox last year. This issue where Skia wrote out-of-bounds caught our eye so we investigated furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r. It turned out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root cause was a discrepancy in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way Skia converted floating point to ints in several places. When making cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-path clip check, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lower coordinates (left and top of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounding box) were rounded using this function:
static inline int round_down_to_int(SkScalar x) {
double xx = x;
xx -= 0.5;
return (int)ceil(xx);
}
Looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code you see that it will return a number greater or equal to zero (which is necessary for passing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path-level clip check) for numbers that are strictly larger than -0.5. However, in anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code, specifically SkEdge::setLine if SK_RASTERIZE_EVEN_ROUNDING is defined (which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case in Firefox), floats are rounded to integers differently, using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following function:
inline SkFDot6 SkScalarRoundToFDot6(SkScalar x, int shift = 0)
{
union {
double fDouble;
int32_t fBits[2];
} tmp;
int fractionalBits = 6 + shift;
double magic = (1LL << (52 - (fractionalBits))) * 1.5;
tmp.fDouble = SkScalarToDouble(x) + magic;
#ifdef SK_CPU_BENDIAN
return tmp.fBits[1];
#else
return tmp.fBits[0];
#endif
}
Now let’s take a look at what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se two functions return for a number -0.499. For this number, round_down_to_int returns 0 (which always passes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clipping check) and SkScalarRoundToFDot6 returns -32 which corresponds to -0.5, so we actually end up with a number that is smaller than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one we started with.
That’s not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only problem, though, because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r place where a precision error occurs in SkEdge::setLine.
Precision error when multiplying fractions
SkEdge::setLine calls SkFixedMul which is defined as:
static inline SkFixed(SkFixed a, SkFixed b) {
return (SkFixed)((int64_t)a * b >> 16);
}
This function is for multiplying two SkFixed numbers. An issue comes up when using this function to multiply negative numbers. Let’s look at a small example. Let’s assume a = -1/(2**16) and b = 1/(2**16). If we multiply cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se two numbers on paper, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result is -1/(2**32). However, due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way SkFixedMul works, specifically because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right shift is used to convert cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result back to SkFixed format, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result we actually end up with is 0xFFFFFFFF which is SkFixed for -1/(2**16). Thus, we end up with a result with a magnitude much larger than expected.
As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result of this multiplication is used by SkEdge::setLine to adjust cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x coordinate of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 initial line point here, we can use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue in SkFixedMul to cause an additional error up to 1/64 of a pixel to go outside of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area bounds.
By combining cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous two issues, it was possible to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x coordinate of a line sufficiently small (smaller than -0.5), so that, when a fractional representation was rounded to an integer here, Skia attempted to draw at coordinates with x = -1, which is clearly outside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image bounds. This cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n led to an out-of-bounds write as can be seen in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original bug report. This bug could be exploited in Firefox by drawing an SVG image with coordinates as described in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous section.
Floating point precision error when converting splines to line segments
When drawing paths, Skia is going to convert all non-linear curves (conic shapes, quadratic and cubic splines) to line segments. Perhaps unsurprisingly, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se conversions suffer from precision errors.
The conversion of splines into line segments happen in several places, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most susceptible to floating-point precision errors are hair_quad (used for drawing quadratic curves) and hair_cubic (used for drawing cubic curves). Both of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se functions are called from hair_path, which we already mentioned above. Because (unsurprisingly), larger precision errors occur when dealing with cubic splines, we’ll only consider cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cubic case here.
When approximating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 spline, first cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cubic coefficients are computed in SkCubicCoeff. The most interesting part is:
fA = P3 + three * (P1 - P2) - P0;
fB = three * (P2 - times_2(P1) + P0);
fC = three * (P1 - P0);
fD = P0;
Where P1, P2 and P3 are input points and fA, fB, fC and fD are output coefficients. The line segment points are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n computed in hair_cubic using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following code
const Sk2s dt(SK_Scalar1 / lines);
Sk2s t(0);
...
Sk2s A = coeff.fA;
Sk2s B = coeff.fB;
Sk2s C = coeff.fC;
Sk2s D = coeff.fD;
for (int i = 1; i < lines; ++i) {
t = t + dt;
Sk2s p = ((A * t + B) * t + C) * t + D;
p.store(&tmp[i]);
}
Where p is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output point and lines is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of line segments we are using to approximate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 curve. Depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 spline, a cubic spline can be approximated with up to 512 lines.
It is obvious that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arithmetic here is not going to be precise. As identical computations happen for x and y coordinates, let’s just consider cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x coordinate in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 post.
Let’s assume cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 width of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area is 1000 pixels. Because hair_path is used for drawing path with antialiasing turned on, it needs to make sure that all points of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path are between 1 and 999, which is done in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 initial, path-level clip check. Let’s consider cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following coordinates that all pass this check:
p0 = 1.501923
p1 = 998.468811
p2 = 998.998779
p3 = 999.000000
For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se points, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 coefficients are as follows
a = 995.908203
b = -2989.310547
c = 2990.900879
d = 1.501923
If you do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same computation in larger precision, you’re going to notice that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 numbers here aren’t quite correct. Now let’s see what happens if we approximate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 spline with 512 line segments. This results in 513 x coordinates:
0: 1.501923
1: 7.332130
2: 13.139574
3: 18.924301
4: 24.686356
5: 30.425781
...
500: 998.986389
501: 998.989563
502: 998.992126
503: 998.994141
504: 998.995972
505: 998.997314
506: 998.998291
507: 998.999084
508: 998.999695
509: 998.999878
510: 999.000000
511: 999.000244
512: 999.000000
We can see that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x coordinate keeps growing and at point 511 clearly goes outside of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “safe” area and grows larger than 999.
As it happens, this isn’t sufficient to trigger an out-of-bounds write, because, due to how drawing antialiased lines works in Skia, we need to go at least 1/64 of a pixel outside of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clip area for it to become a security issue. However, an interesting thing about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision errors in this case is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 larger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 larger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 error that can happen.
So let’s instead consider a drawing area of 32767 pixels (maximum canvas size in Chrome). The initial clipping check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n checks that all path points are in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interval [1, 32766]. Now let’s consider cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following points:
p0 = 1.7490234375
p1 = 32765.9902343750
p2 = 32766.000000
p3 = 32766.000000
The corresponding coefficients
a = 32764.222656
b = -98292.687500
c = 98292.726562
d = 1.749023
And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding line approximation
0: 1.74902343
1: 193.352295
2: 384.207123
3: 574.314941
4: 763.677246
5: 952.295532
…
505: 32765.925781
506: 32765.957031
507: 32765.976562
508: 32765.992188
509: 32766.003906
510: 32766.003906
511: 32766.015625
512: 32766.000000
You can see that we went out-of-bounds significantly more at index 511.
Fortunately for Skia and unfortunately for aspiring attackers, this bug can’t be used to trigger memory corruption, at least not in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 up-to-date version of skia. The reason is SkDrawTiler. Whenever Skia draws using SkBitmapDevice (as opposed to using a GPU device) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing area is larger than 8191 pixels in any dimension, instead of drawing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole image at once, Skia is going to split it into tiles of size (at most) 8191x8191 pixels. This change was made in March, not for security reasons, but to be able to support larger drawing surfaces. However, it still effectively prevented us from exploiting this issue and will also prevent exploiting ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cases where a surface larger than 8191 is required to reach cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision error of a sufficient magnitude.
Still, this bug was exploitable before March and we think it nicely demonstrates cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 concept of precision errors.
Integer precision error when converting splines to line segments
There is anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r place where splines are approximated as line segments when drawing (in this case: filling) paths that was also affected by a precision error, in this case an exploitable one. Interestingly, here cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision error wasn’t in floating-point but racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r in fixed-point arithmetic.
The error happens in SkQuadraticEdge::setQuadraticWithoutUpdate and SkCubicEdge::setCubicWithoutUpdate. For simplicity, we are again going to concentrate just on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cubic spline version and, again, only on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x coordinate.
In SkCubicEdge::setCubicWithoutUpdate, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 curve coordinates are first converted to SkFDot6 type (integer with 6 bits used for fraction). After that, parameters corresponding to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first, second and third derivative of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 curve at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 initial point are going to be computed:
SkFixed B = SkFDot6UpShift(3 * (x1 - x0), upShift);
SkFixed C = SkFDot6UpShift(3 * (x0 - x1 - x1 + x2), upShift);
SkFixed D = SkFDot6UpShift(x3 + 3 * (x1 - x2) - x0, upShift);
fCx = SkFDot6ToFixed(x0);
fCDx = B + (C >> shift) + (D >> 2*shift); // biased by shift
fCDDx = 2*C + (3*D >> (shift - 1)); // biased by 2*shift
fCDDDx = 3*D >> (shift - 1); // biased by 2*shift
Where x0, x1, x2 and x3 are x coordinates of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 4 points that define cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cubic spline and shift and upShift depend on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 curve (this corresponds to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of linear segments cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 curve is going to be approximated in). For simplicity, we can assume shift = upShift = 6 (maximum possible values).
Now let’s see what happens for some very simple input values:
x0 = -30
x1 = -31
x2 = -31
x3 = -31
Note that x0, x1, x2 and x3 are of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type SkFDot6 so value -30 corresponds to -0.46875 and -31 to -0.484375. These are close to -0.5 but not quite and are thus perfectly safe when rounded. Now let’s examine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 values of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 computed parameters:
B = -192
C = 192
D = -64
fCx = -30720
fCDx = -190
fCDDx = 378
fCDDDx = -6
Do you see where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue is? Hint: it’s in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 formula for fCDx.
When computing fCDx (first derivation of a curve), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of D needs is right-shifted by 12. However, D is too small to do that precisely, and since D is negative, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right shift
D >> 2*shift
Is going to result in -1, which is larger in magnitude than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 intended result. (Since D is of type SkFixed its actual value is -0.0009765625 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shift, when interpreted as division by 4096, would result in -2.384185e-07). Because of this, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole fCDx ends up as a larger negative value than it should (-190 vs. -189.015).
Afterwards, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of fCDx gets used when calculating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x value of line segments. This happens in SkCubicEdge::updateCubic on this line:
newx = oldx + (fCDx >> dshift);
The x values, when approximating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 spline with 64 line segments (maximum for this algorithm), are going to be (expressed as index, integer SkFixed value and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding floating point value):
index raw interpretation
0: -30720 -0.46875
1: -30768 -0.469482
2: -30815 -0.470200
3: -30860 -0.470886
4: -30904 -0.471558
5: -30947 -0.472214
...
31: -31683 -0.483444
32: -31700 -0.483704
33: -31716 -0.483948
34: -31732 -0.484192
35: -31747 -0.484421
36: -31762 -0.484650
37: -31776 -0.484863
38: -31790 -0.485077
...
60: -32005 -0.488358
61: -32013 -0.488480
62: -32021 -0.488602
63: -32029 -0.488724
64: -32037 -0.488846
You can see that for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 35th point, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x value (-0.484421) ends up being smaller than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 smallest input point (-0.484375) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trend continues for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 later points. This value would still get rounded to 0 though, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r problem.
The x values computed in SkCubicEdge::updateCubic are passed to SkEdge::updateLine, where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are converted from SkFixed type to SkFDot6 on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following lines:
x0 >>= 10;
x1 >>= 10;
Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r right shift! And when, for example, SkFixed value -31747 gets shifted we end up with SkFDot6 value of -32 which represents -0.5.
At this point we can use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same trick described above in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “Precision error when multiplying fractions” section to go smaller than -0.5 and break out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image bounds. In ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, we can make Skia draw to x = -1 when drawing a path.
But, what can we do with it?
In general, given that Skia allocates image pixels as a single allocation that is organized row by row (as most ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r software would allocate bitmaps), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are several cases of what can happen with precision issues. If we assume an width x height image and that we are only able to go one pixel out of bounds:
- Drawing to y = -1 or y = height immediately leads to heap out-of-bounds write
- Drawing to x = -1 with y = 0 immediately leads to a heap underflow of 1 pixel
- Drawing to x = width with y = height - 1 immediately leads to heap overflow of 1 pixel
- Drawing to x = -1 with y > 0 leads to a pixel “spilling” to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous image row
- Drawing to x = height with y < height-1 leads to a pixel “spilling” to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next image row
What we have here is scenario d) - unfortunately we can’t draw to x = 1 with y = 0 because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision error needs to accumulate over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 growing values of y.
Let’s take a look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following example SVG image:
If we render this in an unpatched version of Firefox what we see is shown in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following image. Notice how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SVG only contains coordinates on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 left side of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 screen, but some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 red pixels get drawn on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right. This is because, due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way images are allocated, drawing to x = -1 and y = row is equal to drawing to x = width - 1 and y = row - 1.
Opening an SVG image that triggers a Skia precision issue in Firefox. If you look closely you’ll notice some red pixels on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right side of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image. How did those get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re? :)
Note that we used Mozilla Firefox and not Google Chrome because, due to SVG drawing internals (specifically: Skia seems to draw cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entire image at once, while Chrome uses additional tiling) it is easier to demonstrate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue in Firefox. However, both Chrome and Firefox were equally affected by this issue.
But, ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than drawing a funny image, is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re real security impact to this issue? Here, SkARGB32_Shader_Blitter comes to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rescue (SkARGB32_Shader_Blitter is used whenever shader effects are applied to a color in Skia). What is specific about SkARGB32_Shader_Blitter is that it allocates a temporary buffer of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same size as a single image row. When SkARGB32_Shader_Blitter::blitH is used to draw an entire image row, if we can make it draw from x = -1 to x = width - 1 (alternately from x = 0 to x = width), it will need to write width + 1 pixels into a buffer that can only hold width pixels, leading to a buffer overflow as can be seen in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ASan log in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug report.
Note how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PoCs for Chrome and Firefox contain SVG images with a linearGradient element - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 linear gradient is used specifically to select SkARGB32_Shader_Blitter instead of drawing pixels to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image directly, which would only result in pixels spilling to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous row.
Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r specific of this issue is that it can only be reached when drawing (more specifically: filling) paths with antialiasing turned off. As it is not currently possible to draw paths to a HTML canvas elements with antialiasing off (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is an imageSmoothingEnabled property but it only applies to drawing images, not paths), an SVG image with shape-rendering="crispEdges" must be used to trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue.
All precision issues we reported in Skia were fixed by increasing kConservativeRoundBias. While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current bias value is large enough to cover cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum precision errors we know about, we should not dismiss cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 possibility of ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r places where precision issues can occur.
Conclusion
While precision issues, such as described in this blog post, won’t be present in most software products, where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are present cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can have quite serious consequences. To prevent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m from occurring:
- Don’t use floating-point arithmetic in cases where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result is security-sensitive. If you absolutely have to, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n you need to make sure that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum possible precision error cannot be larger than some safety margin. Potentially, interval arithmetic could be used to determine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum precision error in some cases. Alternately, perform security checks on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than input.
- With integer arithmetic, be wary of any operations that can reduce cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 precision of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result, such as divisions and right shifts.
When it comes to finding such issues, unfortunately, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re doesn’t seem to be a great way to do it. When we started looking at Skia, initially we wanted to try using symbolic execution on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drawing algorithms to find input values that would lead to drawing out-of-bounds, as, on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 surface, it seemed this is a problem symbolic execution would be well suited for. However, in practice, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re were too many issues: most tools don’t support floating point symbolic variables and, even when running against just cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 integer parts of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 simplest line drawing algorithm, we were unsuccessful in completing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 run in a reasonable time (we were using KLEE with STP and Z3 backends).
In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end, what we ended up doing was a combination of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more old-school methods: manual source review, fuzzing (especially with values close to image boundaries) and, in some cases, when we already identified potentially problematic areas of code, even bruteforcing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 range of all possible values.
Do you know of ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r instances where precision errors resulted in security issues? Let us know about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 comments.