By Mark Brand and Ivan Fratric, Google Project Zero
In this weblog put up, we’re going to write a couple of seldom noticed vulnerability magnificence that generally impacts graphic libraries (although it might additionally happen in different kinds of instrument). The root explanation for such problems is the use of restricted precision mathematics in circumstances the place a precision error would invalidate safety assumptions made by means of the software.
While lets additionally name different categories of insects precision problems, particularly integer overflows, the main distinction is: with integer overflows, we’re coping with mathematics operations the place the magnitude of the result’s too huge to be appropriately represented in the given precision. With the problems described in this weblog put up, we’re coping with mathematics operations the place the magnitude of the outcome or part of the result’s too small to be appropriately represented in the given precision.
These problems can happen when the use of floatingpoint mathematics in operations the place the result’s securitysensitive, however, as we’ll reveal later, too can happen in integer mathematics in some circumstances.
Let’s have a look at a trivial instance:
go with the flow a = 100000000;
go with the flow b = 1;
go with the flow c = a + b;
If we had been making the computation with arbitrary precision, the outcome can be 100000001. However, since go with the flow generally most effective permits for 24 bits of precision, the result’s in reality going to be 100000000. If an software makes the most often cheap assumption that a > 0 and b > 0 means that a + b > a, then this would result in problems.
In the instance above, the distinction between a and b is so vital that b utterly vanishes in the results of the calculation, however precision mistakes additionally occur if the distinction is smaller, for instance
go with the flow a = 1000;
go with the flow b = 1.1111111;
go with the flow c = a + b;
The results of the above computation goes to be 1001.111084 and now not 1001.1111111 which might be the correct outcome. Here, most effective part of b is misplaced, however even such effects can on occasion have fascinating penalties.
While we used the go with the flow sort in the above examples, and in those explicit examples the use of double would outcome in extra correct computation, equivalent precision mistakes can occur with double as smartly.
In the rest of this weblog put up, we’re going to display a number of examples of precision problems with safety have an effect on. These problems had been independently explored by means of two Project Zero contributors: Mark Brand, who checked out SwiftShader, a instrument OpenGL implementation used in Chrome, and Ivan Fratric, who checked out the Skia graphics library, used in Chrome and Firefox.
SwiftShader
SwiftShader is “a highperformance CPUbased implementation of the OpenGL ES and Direct3D 9 graphics APIs”. It’s used in Chrome on all platforms as a fallback rendering strategy to paintings round boundaries in graphics hardware or drivers, permitting common use of WebGL and different complex javascript rendering APIs on a some distance wider vary of units.
The code in SwiftShader must deal with emulating a variety of operations that will most often be carried out by means of the GPU. One operation that we regularly bring to mind as necessarily “unfastened” on a GPU is upscaling, or drawing from a small supply texture to a bigger house, for instance on the display. This calls for computing reminiscence indexes the use of noninteger values, which is the place the vulnerability happens.
As famous in the unique worm file, the code that we’ll have a look at right here isn’t somewhat the code which is in reality run in observe – SwiftShader makes use of an LLVMbased JIT engine to optimize performancecritical code at runtime, however that code is tougher to grasp than their fallback implementation, and each comprise the similar worm, so we’ll speak about the fallback code. This code is the copyloop used to duplicate pixels from one floor to any other all through 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);
go with the flow w = sRect.width() / dRect.width();
go with the flow h = sRect.peak() / dRect.peak();
const go with the flow xStart = sRect.x0 + 0.5f * w;
go with the flow y = sRect.y0 + 0.5f * h;
go with the flow x = xStart;
for(int j = dRect.y0; j < dRect.y1; j++)
source>free upInternal();
dest>free upInternal();
}
dest>lockInternal(dRect.x0, dRect.y0, dRect.slice, sw::LOCK_WRITEONLY, sw::PUBLIC);
go with the flow w = sRect.width() / dRect.width();
go with the flow h = sRect.peak() / dRect.peak();
const go with the flow xStart = sRect.x0 + 0.5f * w;
go with the flow y = sRect.y0 + 0.5f * h;
go with the flow x = xStart;
for(int j = dRect.y0; j < dRect.y1; j++)
source>free upInternal();
dest>free upInternal();
}
So – what highlights this code as problematic? We know previous to getting into this serve as that every one the boundschecking has already been carried out, and that any name to reproductionInternal with (i, j) in dRect and (x, y) in sRect might be secure.
The examples in the creation above display circumstances the place the ensuing precision error implies that a roundingdown happens – in this situation that wouldn’t be sufficient to provide a captivating safety worm. Can we motive floatingpoint imprecision to outcome in a largerthancorrect worth, resulting in (x, y) values which can be greater than anticipated?
If we have a look at the code, the aim of the builders is to compute the following:
for(int j = dRect.y0; j < dRect.y1; j++)
If this way were used as a substitute, we’d nonetheless have precision mistakes – however with out the iterative calculation, there’d be no propagation of the error, and lets be expecting the eventual magnitude of the precision error to be strong, and in direct percentage to the dimension of the operands. With the iterative calculation as carried out in the code, the mistakes begin to propagate/snowball into a bigger and bigger error.
There are techniques to estimate the most error in floating level calculations; and should you in point of fact, in point of fact want to keep away from having additional bounds exams, the use of this sort of way and ensuring that you’ve got conservative protection margins round the ones most mistakes may well be a sophisticated and errorprone solution to clear up this factor. It’s now not an ideal option to figuring out the pathological values that we wish right here to reveal a vulnerability; so as a substitute we’ll take a bruteforce way.
Instinctively, we’re rather certain that the multiplicative implementation might be kind of appropriate, and that the implementation with iterative addition might be a lot much less appropriate. Given that the area of conceivable inputs is small (Chrome disallows textures with width or peak more than 8192), we will simply run a brute power over all ratios of supply width to vacation spot width, evaluating the two algorithms, and seeing the place the effects are maximum other. (Note that SwiftShader additionally limits us to even numbers). This leads us to the values of 5828, 8132; and if we examine the computations in this situation (left facet is the iterative addition, correct facet is the multiplication):
0: 1.075012 1.075012
1: 1.791687 1.791687
…
1000: 717.749878 717.749878 Up to right here (at the precision proven) the values are nonetheless equivalent
1001: 718.466553 718.466553
…
2046: 1467.391724 1467.391724 At this level, the first vital mistakes begin to happen, however observe
2047: 1468.108398 1468.108521 that the “flawed” result’s smaller than the extra actual one.
…
2856: 2047.898315 2047.898438
2857: 2048.614990 2048.614990 Here our two computations coincide once more, in brief, and from right here onwards
2858: 2049.331787 2049.331787 the precision mistakes persistently favour a bigger outcome than the extra
2859: 2050.048584 2050.048340 actual calculation.
…
8129: 5827.567871 5826.924805
8130: 5828.284668 5827.641602
8131: 5829.001465 5828.358398 The final index is now sufficiently other that int conversion effects in an oob index.
1: 1.791687 1.791687
…
1000: 717.749878 717.749878 Up to right here (at the precision proven) the values are nonetheless equivalent
1001: 718.466553 718.466553
…
2046: 1467.391724 1467.391724 At this level, the first vital mistakes begin to happen, however observe
2047: 1468.108398 1468.108521 that the “flawed” result’s smaller than the extra actual one.
…
2856: 2047.898315 2047.898438
2857: 2048.614990 2048.614990 Here our two computations coincide once more, in brief, and from right here onwards
2858: 2049.331787 2049.331787 the precision mistakes persistently favour a bigger outcome than the extra
2859: 2050.048584 2050.048340 actual calculation.
…
8129: 5827.567871 5826.924805
8130: 5828.284668 5827.641602
8131: 5829.001465 5828.358398 The final index is now sufficiently other that int conversion effects in an oob index.
(Note additionally that there can also be error in the “secure” calculation; it’s simply that the loss of error propagation implies that that error will stay without delay proportional to the dimension of the enter error, which we think to be “small.”)
We can certainly see that, the multiplicative set of rules would stay inside of bounds; however that the iterative set of rules can go back an index this is out of doors the bounds of the enter texture!
As a outcome, we learn a whole row of pixels previous the finish of our texture allocation – and this can also be simply leaked again to javascript the use of WebGL. Stay tuned for an upcoming weblog put up in which we’ll use this vulnerability in conjunction with any other unrelated factor in SwiftShader to take keep watch over of the GPU procedure from javascript.
Skia
Skia is a graphics library used, amongst different puts, in Chrome, Firefox and Android. In the internet browsers it’s used for instance when drawing to a canvas HTML component the use of CanvasRenderingContext2D or when drawing SVG pictures. Skia could also be used when drawing more than a few different HTML parts, however canvas component and SVG pictures are extra fascinating from the safety standpoint as a result of they allow extra direct keep watch over over the gadgets being drawn by means of the graphic library.
The most complicated form of object (and subsequently, maximum fascinating from the safety standpoint) that Skia can draw is a route. A route is an object that is composed of parts corresponding to strains, but in addition extra advanced curves, in explicit quadratic or cubic splines.
Due to the approach instrument drawing algorithms paintings in Skia, the precision problems are very a lot conceivable and somewhat impactful after they occur, generally resulting in outofbounds writes.
To perceive why those problems can occur, let’s think you will have a picture in reminiscence (represented as a buffer with dimension = width x peak x colour dimension). Normally, when drawing a pixel with coordinates (x, y) and colour c, you possibly can wish to ensure that the pixel in reality falls inside of the area of the symbol, particularly that 0 <= x < width and 0 <= y < peak. Failing to test this would outcome in making an attempt to write down the pixel out of doors the bounds of the allotted buffer. In pc graphics, ensuring that most effective the gadgets in the symbol area are being drawn is named clipping.
So, the place is the drawback? Making a clip take a look at for each pixel is pricey in phrases of CPU cycles and Skia prides itself on velocity. So, as a substitute of constructing a clip take a look at for each pixel, what Skia does is, it first makes the clip take a look at on a whole object (e.g. line, route or some other form of object being drawn). Depending on the clip take a look at, there are three conceivable results:

The object is totally out of doors of the drawing house: The drawing serve as doesn’t draw the rest and returns straight away.

The object is partly within the drawing house: The drawing serve as proceeds with perpixel clip enabled (generally by means of depending on SkRectClipBlitter).

The complete object is in the drawing house: The drawing serve as attracts without delay into the buffer with out acting perpixel clip exams.
The problematic state of affairs is c) the place the clip take a look at is carried out most effective perobject and the extra actual, perpixel exams are disabled. This way, if there’s a precision factor someplace between the perobject clip take a look at and the drawing of pixels and if the precision factor reasons the pixel coordinates to move out of doors of the drawing house, this would outcome in a safety vulnerability.
We can see perobject clip exams resulting in losing perpixel exams in a number of puts, for instance:

In hair_path (serve as for drawing a route with out filling), clip is to start with set to null (which disables clip exams). The clip is most effective set if the bounds of the route, rounded up and prolonged by means of 1 or 2 relying on the drawing choices don’t are compatible in the drawing house. Extending the route bounds by means of 1 turns out like a fairly large protection margin, however it’s in reality the least conceivable secure worth as a result of drawing gadgets with antialiasing on will on occasion outcome in drawing to within sight pixels.

In SkScan::FillPath (serve as for filling a route with antialiasing grew to become off), the bounds of the route are first prolonged by means of kConservativeRoundBias and rounded to procure the “conservative” route bounds. A SkScanClipper object is then created for the present route. As we will see in the definition of SkScanClipper, it is going to most effective use SkRectClipBlitter if the x coordinates of the route bounds are out of doors the drawing house or if irPreClipped is correct (which most effective occurs when route coordinates are very huge).
Similar patterns can also be noticed in different drawing purposes.
Before we take a more indepth have a look at the problems, it comes in handy to briefly pass over more than a few quantity codecs utilized by Skia:

SkScalar is a 32bit floating level quantity

SkFDot6 is outlined as an integer, however it’s in reality a fixedpoint quantity with 26 bits to the left and 6 bits to the correct of the decimal level. For instance, SkFDot6 worth of 0x00000001 represents the quantity 1/64.

SkFixed could also be a fixedpoint quantity, this time with 16 bits to the left and 16 bits to the correct of the decimal level. For instance, SkFixed worth of 0x00000001 represents 1/(2**16)
Precision error with integer to go with the flow conversion
We came upon the preliminary drawback when doing DOM fuzzing towards Firefox final yr. This factor the place Skia wrote outofbounds stuck our eye so we investigated additional. It grew to become out the root motive used to be a discrepancy in the approach Skia transformed floating level to ints in a number of puts. When making the perpath clip take a look at, the decrease coordinates (left and height of the bounding field) had been rounded the use of this serve as:
static inline int round_down_to_int(SkScalar x)
double xx = x;
xx = 0.5;
go back (int)ceil(xx);
Looking at the code you notice that it is going to go back a host better or equivalent to 0 (which is important for passing the pathlevel clip take a look at) for numbers which can be strictly greater than 0.5. However, in any other a part of the code, particularly SkEdge::setLine if SK_RASTERIZE_EVEN_ROUNDING is outlined (which is the case in Firefox), floats are rounded to integers in a different way, the use of the following serve as:
inline SkFDot6 SkScalarRoundToFDot6(SkScalar x, int shift = 0)
Now let’s check out what those two purposes go back for a host 0.499. For this quantity, round_down_to_int returns 0 (which at all times passes the clipping take a look at) and SkScalarRoundToFDot6 returns 32 which corresponds to 0.5, so we in reality finally end up with a host this is smaller than the one we began with.
That’s now not the most effective drawback, although, as a result of there’s any other position the place a precision error happens in SkEdge::setLine.
Precision error when multiplying fractions
SkEdge::setLine calls SkFixedMul which is outlined as:
static inline SkFixed(SkFixed a, SkFixed b)
This serve as is for multiplying two SkFixed numbers. An factor comes up when the use of this serve as to multiply unfavourable numbers. Let’s have a look at a small instance. Let’s think a = 1/(2**16) and b = 1/(2**16). If we multiply those two numbers on paper, the result’s 1/(2**32). However, because of the approach SkFixedMul works, particularly as a result of the correct shift is used to transform the outcome again to SkFixed structure, the outcome we in reality finally end up with is 0xFFFFFFFF which is SkFixed for 1/(2**16). Thus, we finally end up with a outcome with a magnitude a lot greater than anticipated.
As the results of this multiplication is utilized by SkEdge::setLine to regulate the x coordinate of the preliminary line level right here, we will use the factor in SkFixedMul to motive an extra error as much as 1/64 of a pixel to move out of doors of the drawing house bounds.
By combining the earlier two problems, it used to be conceivable to get the x coordinate of a line small enough (smaller than 0.5), in order that, when a fractional illustration used to be rounded to an integer right here, Skia tried to attract at coordinates with x = 1, which is obviously out of doors the symbol bounds. This then ended in an outofbounds write as can also be noticed in the unique worm file. This worm might be exploited in Firefox by means of drawing an SVG symbol with coordinates as described in the earlier phase.
Floating level precision error when changing splines to line segments
When drawing paths, Skia goes to transform all nonlinear curves (conic shapes, quadratic and cubic splines) to line segments. Perhaps unsurprisingly, those conversions be afflicted by precision mistakes.
The conversion of splines into line segments occur in a number of puts, however the maximum at risk of floatingpoint precision mistakes are hair_quad (used for drawing quadratic curves) and hair_cubic (used for drawing cubic curves). Both of those purposes are known as from hair_path, which we already discussed above. Because (unsurprisingly), greater precision mistakes happen when coping with cubic splines, we’ll most effective imagine the cubic case right here.
When approximating the spline, first the cubic coefficients are computed in SkCubicCoeff. The maximum fascinating section 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 enter issues and fA, fB, fC and fD are output coefficients. The line section issues are then computed in hair_cubic the use of the following code
const Sk2s dt(SK_Scalar1 / strains);
Sk2s t(0);
…
Sk2s A = coeff.fA;
Sk2s B = coeff.fB;
Sk2s C = coeff.fC;
Sk2s D = coeff.fD;
for (int i = 1; i < strains; ++i)
t = t + dt;
Sk2s p = ((A * t + B) * t + C) * t + D;
p.retailer(&tmp[i]);
Where p is the output level and strains is the collection of line segments we’re the use of to approximate the curve. Depending on the duration of the spline, a cubic spline can also be approximated with as much as 512 strains.
It is plain that the mathematics right here isn’t going to be actual. As equivalent computations occur for x and y coordinates, let’s simply imagine the x coordinate in the remainder of the put up.
Let’s think the width of the drawing house is 1000 pixels. Because hair_path is used for drawing route with antialiasing grew to become on, it must ensure that all issues of the route are between 1 and 999, which is completed in the preliminary, pathlevel clip take a look at. Let’s imagine the following coordinates that every one go this take a look at:
p0 = 1.501923
p1 = 998.468811
p2 = 998.998779
p3 = 999.000000
For those issues, the coefficients are as follows
a = 995.908203
b = 2989.310547
c = 2990.900879
d = 1.501923
If you do the similar computation in greater precision, you’re going to note that the numbers right here aren’t somewhat appropriate. Now let’s see what occurs if we approximate the spline with 512 line segments. This effects 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 the x coordinate helps to keep rising and at level 511 obviously is going out of doors of the “secure” house and grows greater than 999.
As it occurs, this isn’t enough to cause an outofbounds write, as a result of, because of how drawing antialiased strains works in Skia, we want to pass no less than 1/64 of a pixel out of doors of the clip house for it to transform a safety factor. However, a captivating factor about the precision mistakes in this situation is that the greater the drawing house, the greater the error that may occur.
So let’s as a substitute imagine a drawing house of 32767 pixels (most canvas dimension in Chrome). The preliminary clipping take a look at then exams that every one route issues are in the period [1, 32766]. Now let’s imagine the following issues:
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 the 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 outofbounds considerably extra at index 511.
Fortunately for Skia and sadly for aspiring attackers, this worm can’t be used to cause reminiscence corruption, no less than now not in the uptodate model of skia. The reason why is SkDrawTiler. Whenever Skia attracts the use of SkBitmapDevice (versus the use of a GPU tool) and the drawing house is bigger than 8191 pixels in any size, as a substitute of drawing the entire symbol directly, Skia goes to separate it into tiles of dimension (at maximum) 8191×8191 pixels. This alternate used to be made in March, now not for safety causes, however as a way to beef up greater drawing surfaces. However, it nonetheless successfully averted us from exploiting this factor and also will save you exploiting different circumstances the place a floor greater than 8191 is needed to achieve the precision error of a enough magnitude.
Still, this worm used to be exploitable sooner than March and we expect it effectively demonstrates the thought of precision mistakes.
Integer precision error when changing splines to line segments
There is any other position the place splines are approximated as line segments when drawing (in this situation: filling) paths that used to be additionally suffering from a precision error, in this situation an exploitable one. Interestingly, right here the precision error wasn’t in floatingpoint however moderately in fixedpoint mathematics.
The error occurs in SkQuadraticEdge::setQuadraticWithoutUpdate and SkCubicEdge::setCubicWithoutUpdate. For simplicity, we’re once more going to pay attention simply on the cubic spline model and, once more, most effective on the x coordinate.
In SkCubicEdge::setCubicWithoutUpdate, the curve coordinates are first transformed to SkFDot6 sort (integer with 6 bits used for fraction). After that, parameters similar to the first, 2d and 3rd byproduct of the curve at the preliminary level 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 = SkFDot6ToMounted(x0);
fCDx = B + (C >> shift) + (D >> 2*shift); // biased by means of shift
fCDDx = 2*C + (3*D >> (shift – 1)); // biased by means of 2*shift
fCDDDx = 3*D >> (shift – 1); // biased by means of 2*shift
Where x0, x1, x2 and x3 are x coordinates of the 4 issues that outline the cubic spline and shift and upShift rely on the duration of the curve (this corresponds to the collection of linear segments the curve goes to be approximated in). For simplicity, we will think shift = upShift = 6 (most conceivable values).
Now let’s see what occurs for some quite simple enter values:
x0 = 30
x1 = 31
x2 = 31
x3 = 31
Note that x0, x1, x2 and x3 are of the sort SkFDot6 so worth 30 corresponds to 0.46875 and 31 to 0.484375. These are on the subject of 0.5 however now not somewhat and are thus completely secure when rounded. Now let’s read about the values of the computed parameters:
B = 192
C = 192
D = 64
fCx = 30720
fCDx = 190
fCDDx = 378
fCDDDx = 6
Do you notice the place the factor is? Hint: it’s in the system for fCDx.
When computing fCDx (first derivation of a curve), the worth of D wishes is rightshifted by means of 12. However, D is just too small to try this exactly, and because D is unfavourable, the correct shift
D >> 2*shift
Is going to outcome in 1, which is greater in magnitude than the supposed outcome. (Since D is of sort SkFixed its precise worth is 0.0009765625 and the shift, when interpreted as department by means of 4096, would outcome in 2.384185e07). Because of this, the entire fCDx finally ends up as a bigger unfavourable worth than it will have to (190 vs. 189.015).
Afterwards, the worth of fCDx will get used when calculating the x worth of line segments. This occurs in SkCubicEdge::updateCubic on this line:
newx = oldx + (fCDx >> dshift);
The x values, when approximating the spline with 64 line segments (most for this set of rules), are going to be (expressed as index, integer SkFixed worth and the corresponding floating level worth):
index uncooked 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 the 35th level, the x worth (0.484421) finally ends up being smaller than the smallest enter level (0.484375) and the development continues for the later issues. This worth would nonetheless get rounded to 0 although, however there may be any other drawback.
The x values computed in SkCubicEdge::updateCubic are handed to SkEdge::updateLine, the place they’re transformed from SkFixed sort to SkFDot6 on the following strains:
x0 >>= 10;
x1 >>= 10;
Another correct shift! And when, for instance, SkFixed worth 31747 will get shifted we finally end up with SkFDot6 worth of 32 which represents 0.5.
At this level we will use the similar trick described above in the “Precision error when multiplying fractions” phase to move smaller than 0.5 and escape of the symbol bounds. In different phrases, we will make Skia draw to x = 1 when drawing a route.
But, what are we able to do with it?
In common, for the reason that Skia allocates symbol pixels as a unmarried allocation this is arranged row by means of row (as maximum different instrument would allocate bitmaps), there are a number of circumstances of what can occur with precision problems. If we think an width x peak symbol and that we’re most effective in a position to move one pixel out of bounds:

Drawing to y = 1 or y = peak straight away ends up in heap outofbounds write

Drawing to x = 1 with y = 0 straight away ends up in a heap underflow of 1 pixel

Drawing to x = width with y = peak – 1 straight away ends up in heap overflow of 1 pixel

Drawing to x = 1 with y > 0 ends up in a pixel “spilling” to the earlier symbol row

Drawing to x = peak with y < height1 ends up in a pixel “spilling” to the subsequent symbol row
What now we have here’s state of affairs d) – sadly we will’t draw to x = 1 with y = 0 as a result of the precision error wishes to amass over the rising values of y.
Let’s check out the following instance SVG symbol:
frame
If we render this in an unpatched model of Firefox what we see is proven in the following symbol. Notice how the SVG most effective comprises coordinates on the left facet of the display, however a few of the purple pixels get drawn on the correct. This is as a result of, because of the approach pictures are allotted, drawing to x = 1 and y = row is the same as drawing to x = width – 1 and y = row – 1.
Opening an SVG symbol that triggers a Skia precision factor in Firefox. If you glance carefully you’ll realize some purple pixels on the correct facet of the symbol. How did the ones get there? 🙂
Note that we used Mozilla Firefox and now not Google Chrome as a result of, because of SVG drawing internals (particularly: Skia turns out to attract the complete symbol directly, whilst Chrome makes use of further tiling) it’s more straightforward to reveal the factor in Firefox. However, each Chrome and Firefox had been similarly suffering from this factor.
But, rather than drawing a humorous symbol, is there actual safety have an effect on to this factor? Here, SkARGB32_Shader_Blitter involves the rescue (SkARGB32_Shader_Blitter is used every time shader results are carried out to a colour in Skia). What is particular about SkARGB32_Shader_Blitter is that it allocates a short lived buffer of the similar dimension as a unmarried symbol row. When SkARGB32_Shader_Blitter::blitH is used to attract a whole symbol row, if we will make it draw from x = 1 to x = width – 1 (alternately from x = 0 to x = width), it is going to want to write width + 1 pixels right into a buffer that may most effective cling width pixels, resulting in a buffer overflow as can also be noticed in the ASan log in the worm file.
Note how the PoCs for Chrome and Firefox comprise SVG pictures with a linearGradient component – the linear gradient is used particularly to make a choice SkARGB32_Shader_Blitter as a substitute of drawing pixels to the symbol without delay, which might most effective outcome in pixels spilling to the earlier row.
Another explicit of this factor is that it might most effective be reached when drawing (extra particularly: filling) paths with antialiasing grew to become off. As it isn’t lately conceivable to attract paths to a HTML canvas parts with antialiasing off (there may be an imageSmoothingEnabled assets however it most effective applies to drawing pictures, now not paths), an SVG symbol with shaperendering=”crispEdges” should be used to cause the factor.
All precision problems we reported in Skia had been constant by means of expanding kConservativeRoundBias. While the present bias worth is huge sufficient to hide the most precision mistakes we find out about, we will have to now not brush aside the chance of alternative puts the place precision problems can happen.
Conclusion
While precision problems, corresponding to described in this weblog put up, received’t be provide in maximum instrument merchandise, the place they’re provide they may be able to have somewhat severe penalties. To save you them from happening:

Don’t use floatingpoint mathematics in circumstances the place the result’s securitysensitive. If you completely must, then you wish to have to ensure that the most conceivable precision error can’t be greater than some protection margin. Potentially, period mathematics might be used to decide the most precision error in some circumstances. Alternately, carry out safety exams on the outcome moderately than enter.

With integer mathematics, be cautious of any operations that may scale back the precision of the outcome, corresponding to divisions and correct shifts.
When it involves discovering such problems, sadly, there doesn’t appear to be a good way to do it. When we began taking a look at Skia, to start with we would have liked to check out the use of symbolic execution on the drawing algorithms to search out enter values that will result in drawing outofbounds, as, on the floor, it appeared it is a drawback symbolic execution can be smartly suited to. However, in observe, there have been too many problems: maximum equipment don’t beef up floating level symbolic variables and, even if working towards simply the integer portions of the most straightforward line drawing set of rules, we had been unsuccessful in finishing the run in a cheap time (we had been the use of KLEE with STP and Z3 backends).
In the finish, what we ended up doing used to be a mix of the extra oldschool strategies: guide supply overview, fuzzing (particularly with values on the subject of symbol limitations) and, in some circumstances, after we already recognized doubtlessly problematic spaces of code, even bruteforcing the vary of all conceivable values.
Do you understand of alternative cases the place precision mistakes resulted in safety problems? Let us find out about them in the feedback.