Langsung ke konten utama

Drawing Exterior The Box: Precision Issues Inwards Graphic Libraries

By Mark Brand as well as Ivan Fratric, Google

In this weblog post, nosotros are going to write most a seldom seen vulnerability shape that typically affects graphic libraries (though it tin dismiss also occur inward other types of software). The root drive of such issues is using limited precision arithmetics inward cases where a precision fault would invalidate security assumptions made past times the application.

While nosotros could also telephone telephone other classes of bugs precision issues, namely integer overflows, the major divergence is: alongside integer overflows, nosotros are dealing alongside arithmetics operations where the magnitude of the final result is too large to hold upwards accurately represented inward the given precision. With the issues described inward this weblog post, nosotros are dealing alongside arithmetics operations where the magnitude of the final result or a component of the final result is too small to hold upwards accurately represented inward the given precision.

These issues tin dismiss occur when using floating-point arithmetics inward operations where the final result is security-sensitive, but, as we’ll demonstrate later, tin dismiss also occur inward integer arithmetics inward some cases.

Let’s human face at a niggling example:

 float a = 100000000;
 float b = 1;
 float c = a + b;

If nosotros were making the computation alongside arbitrary precision, the final result would hold upwards 100000001. However, since float typically entirely allows for 24 bits of precision, the final result is genuinely going to hold upwards 100000000. If an application makes the usually reasonable supposition that a > 0 as well as b > 0 implies that a + b > a, so this could Pb to issues.

In the illustration above, the divergence betwixt a as well as b is so pregnant that b completely vanishes inward the final result of the calculation, but precision errors also fall out if the divergence is smaller, for example

 float a = 1000;
 float b = 1.1111111;
 float c = a + b;

The final result of the higher upwards computation is going to hold upwards 1001.111084 as well as non 1001.1111111 which would hold upwards the accurate result. Here, entirely a component of b is lost, but fifty-fifty such results tin dismiss sometimes receive got interesting consequences.

While nosotros used the float type inward the higher upwards examples, as well as inward these particular examples using double would final result inward to a greater extent than accurate computation, similar precision errors tin dismiss fall out alongside double as well.

In the residuum of this weblog post, nosotros are going to exhibit several examples of precision issues alongside security impact. These issues were independently explored past times 2 members: Mark Brand, who looked at SwiftShader, a software OpenGL implementation used inward Chrome, as well as Ivan Fratric, who looked at the Skia graphics library, used inward Chrome as well as Firefox.

SwiftShader

SwiftShader is “a high-performance CPU-based implementation of the OpenGL ES as well as Direct3D ix graphics APIs”. It’s used inward Chrome on all platforms as a fallback rendering pick to piece of work around limitations inward graphics hardware or drivers, allowing universal usage of WebGL as well as other advanced javascript rendering APIs on a far wider make of devices.

The code inward SwiftShader needs to grip emulating a broad make of operations that would usually hold upwards performed past times the GPU. One functioning that nosotros commonly retrieve of as essentially “free” on a GPU is upscaling, or drawing from a modest source texture to a larger area, for illustration on the screen. This requires computing retentiveness indexes using non-integer values, which is where the vulnerability occurs.

As noted inward the master copy põrnikas report, the code that we’ll human face at hither is non quite the code which is genuinely run inward exercise - SwiftShader uses an LLVM-based JIT engine to optimize performance-critical code at runtime, but that code is to a greater extent than hard to empathize than their fallback implementation, as well as both incorporate the same bug, so we’ll hash out the fallback code. This code is the copy-loop used to re-create pixels from i surface to some other 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();
}

So - what highlights this code as problematic? We know prior to entering this component that all the bounds-checking has already been performed, as well as that whatever telephone telephone to copyInternal alongside (i, j) inward dRect as well as (x, y) inward sRect volition hold upwards safe.

The examples inward the introduction higher upwards exhibit cases where the resulting precision fault way that a rounding-down occurs - inward this instance that wouldn’t hold upwards plenty to make an interesting security bug. Can nosotros drive floating-point imprecision to final result inward a larger-than-correct value, leading to (x, y) values that are larger than expected?

If nosotros human face at the code, the intention of the developers is to compute the following:

 for(int j = dRect.y0; j < dRect.y1; j++)
 {
   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);
   }
 }

If this approach had been used instead, we’d nonetheless receive got precision errors - but without the iterative calculation, there’d hold upwards no propagation of the error, as well as nosotros could human face the eventual magnitude of the precision fault to hold upwards stable, as well as inward direct proportion to the size of the operands. With the iterative calculation as performed inward the code, the errors start to propagate/snowball into a larger as well as larger error.

There are ways to gauge the maximum fault inward floating indicate calculations; as well as if you lot really, genuinely demand to avoid having extra bounds checks, using this sort of approach as well as making certain that you lot receive got conservative security margins around those maximum errors mightiness hold upwards a complicated as well as error-prone way to solve this issue. It’s non a groovy approach to identifying the pathological values that nosotros desire hither to demonstrate a vulnerability; so instead we’ll accept a brute-force approach.

Instinctively, we’re fairly certain that the multiplicative implementation volition hold upwards roughly correct, as well as that the implementation alongside iterative add-on volition hold upwards much less correct. Given that the infinite of possible inputs is modest (Chrome disallows textures alongside width or pinnacle greater than 8192), nosotros tin dismiss but run a beast strength over all ratios of source width to goal width, comparison the 2 algorithms, as well as seeing where the results are most different. (Note that SwiftShader also limits us to fifty-fifty numbers). This leads us to the values of 5828, 8132; as well as if nosotros compare the computations inward this instance (left side is the iterative addition, right side is the multiplication):

0:    1.075012 1.075012
1:    1.791687 1.791687
...
1000: 717.749878 717.749878   Up to hither (at the precision shown) the values are nonetheless identical
1001: 718.466553 718.466553
...
2046: 1467.391724 1467.391724 At this point, the kickoff pregnant errors start to occur, but note
2047: 1468.108398 1468.108521 that the "incorrect" final result is smaller than the to a greater extent than precise one.
...
2856: 2047.898315 2047.898438
2857: 2048.614990 2048.614990 Here our 2 computations coincide again, briefly, as well as from hither onwards
2858: 2049.331787 2049.331787 the precision errors consistently favour a larger final result than the more
2859: 2050.048584 2050.048340 precise calculation.
...
8129: 5827.567871 5826.924805
8130: 5828.284668 5827.641602
8131: 5829.001465 5828.358398 The final index is at nowadays sufficiently dissimilar that int conversion results inward an oob index.

(Note also that at that topographic point volition also hold upwards fault inward the “safe” calculation; it’s but that the lack of fault propagation way that that fault volition stay direct proportional to the size of the input error, which nosotros human face to hold upwards “small.”)

We tin dismiss indeed run across that, the multiplicative algorithm would stay within bounds; but that the iterative algorithm tin dismiss homecoming an index that is exterior the bounds of the input texture!

As a result, nosotros read an entire row of pixels past times the cease of our texture allotment - as well as this tin dismiss hold upwards easily leaked dorsum to javascript using WebGL. Stay tuned for an upcoming weblog post inward which we’ll usage this vulnerability together alongside some other unrelated number inward SwiftShader to accept command of the GPU procedure from javascript.

Skia

Skia is a graphics library used, amidst other places, inward Chrome, Firefox as well as Android. In the spider web browsers it is used for illustration when drawing to a sail HTML chemical ingredient using CanvasRenderingContext2D or when drawing SVG images. Skia is also used when drawing diverse other HTML elements, but sail chemical ingredient as well as SVG images are to a greater extent than interesting from the security perspective because they enable to a greater extent than direct command over the objects beingness drawn past times the graphic library.

The most complex type of object (and therefore, most interesting from the security perspective) that Skia tin dismiss clit is a path. H5N1 path is an object that consists of elements such as lines, but also to a greater extent than complex curves, inward particular quadratic or cubic splines.

Due to the way software drawing algorithms piece of work inward Skia, the precision issues are real much possible as well as quite impactful when they happen, typically leading to out-of-bounds writes.

To empathize why these issues tin dismiss happen, let’s assume you lot receive got an icon inward retentiveness (represented as a buffer alongside size = width x height x color size). Normally, when drawing a pixel alongside coordinates (x, y) as well as color c, you lot would desire to brand certain that the pixel genuinely falls within the infinite of the image, specifically that 0 <= x < width as well as 0 <= y < height. Failing to depository fiscal establishment check this could final result inward attempting to write the pixel exterior the bounds of the allocated buffer. In figurer graphics, making certain that entirely the objects inward the icon part are beingness drawn is called clipping.

So, where is the problem? Making a clip depository fiscal establishment check for every pixel is expensive inward price of CPU cycles as well as Skia prides itself on speed. So, instead of making a clip depository fiscal establishment check for every pixel, what Skia does is, it kickoff makes the clip depository fiscal establishment check on an entire object (e.g. line, path or whatever other type of object beingness drawn). Depending on the clip check, at that topographic point are 3 possible outcomes:

  1. The object is completely exterior of the drawing area: The drawing component doesn’t clit anything as well as returns immediately.

  1. The object is partially within the drawing area: The drawing component proceeds alongside per-pixel clip enabled (usually past times relying on SkRectClipBlitter).

  1. The entire object is inward the drawing area: The drawing component draws direct into the buffer without performing per-pixel clip checks.

The problematic scenario is c) where the clip depository fiscal establishment check is performed entirely per-object as well as the to a greater extent than precise, per-pixel checks are disabled. This means, if at that topographic point is a precision number somewhere betwixt the per-object clip depository fiscal establishment check as well as the drawing of pixels as well as if the precision number causes the pixel coordinates to travel exterior of the drawing area, this could final result inward a security vulnerability.

We tin dismiss run across per-object clip checks leading to dropping per-pixel checks inward several places, for example:

  • In hair_path (function for drawing a path without filling), clip is initially laid to nothing (which disables clip checks). The clip is entirely laid if the bounds of the path, rounded upwards as well as extended past times 1 or 2 depending on the drawing options don’t check inward the drawing area. Extending the path bounds past times 1 seems similar a pretty large security margin, but it is genuinely the to the lowest degree possible rubber value because drawing objects alongside antialiasing on volition sometimes final result inward drawing to nearby pixels.

  • In SkScan::FillPath (function for filling a path alongside antialiasing turned off), the bounds of the path are kickoff extended past times kConservativeRoundBias as well as rounded to obtain the “conservative” path bounds. H5N1 SkScanClipper object is so created for the electrical flow path. As nosotros tin dismiss run across inward the definition of SkScanClipper, it volition entirely usage SkRectClipBlitter if the x coordinates of the path bounds are exterior the drawing expanse or if irPreClipped is truthful (which entirely happens when path coordinates are real large).

Similar patterns tin dismiss hold upwards seen inward other drawing functions.

Before nosotros accept a closer human face at the issues, it is useful to speedily travel over diverse number formats used past times Skia:

  • SkScalar is a 32-bit floating indicate number

  • SkFDot6 is defined as an integer, but it is genuinely a fixed-point number alongside 26 bits to the left as well as vi bits to the right of the decimal point. For example, SkFDot6 value of 0x00000001 represents the number 1/64.

  • SkFixed is also a fixed-point number, this fourth dimension alongside sixteen bits to the left as well as sixteen bits to the right of the decimal point. For example, SkFixed value of 0x00000001 represents 1/(2**16)

Precision fault alongside integer to float conversion

We discovered the initial employment when doing DOM fuzzing against Firefox final year. This issue where Skia wrote out-of-bounds caught our oculus so nosotros investigated further. It turned out the root drive was a discrepancy inward the way Skia converted floating indicate to ints inward several places. When making the per-path clip check, the lower coordinates (left as well as top of the 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 the code you lot run across that it volition homecoming a number greater or equal to null (which is necessary for passing the path-level clip check) for numbers that are strictly larger than -0.5. However, inward some other component of the code, specifically SkEdge::setLine if SK_RASTERIZE_EVEN_ROUNDING is defined (which is the instance inward Firefox), floats are rounded to integers differently, using the next function:

inline SkFDot6 SkScalarRoundToFDot6(SkScalar x, int shift = 0)
{
   union {
       double fDouble;
       int32_t fBits[2];
   } tmp;
   int fractionalBits = vi + 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 accept a human face at what these 2 functions homecoming for a number -0.499. For this number, round_down_to_int returns 0 (which ever passes the clipping check) as well as SkScalarRoundToFDot6 returns -32 which corresponds to -0.5, so nosotros genuinely cease upwards alongside a number that is smaller than the i nosotros started with.

That’s non the entirely problem, though, because there’s some other house where a precision fault occurs inward SkEdge::setLine.

Precision fault 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 component is for multiplying 2 SkFixed numbers. An number comes upwards when using this component to multiply negative numbers. Let’s human face at a modest example. Let’s assume a = -1/(2**16) as well as b = 1/(2**16). If nosotros multiply these 2 numbers on paper, the final result is -1/(2**32). However, due to the way SkFixedMul works, specifically because the right shift is used to convert the final result dorsum to SkFixed format, the final result nosotros genuinely cease upwards alongside is 0xFFFFFFFF which is SkFixed for  -1/(2**16). Thus, nosotros cease upwards alongside a final result alongside a magnitude much larger than expected.

As the final result of this multiplication is used past times SkEdge::setLine to adapt the x coordinate of the initial business indicate here, nosotros tin dismiss usage the number inward SkFixedMul to drive an additional fault upwards to 1/64 of a pixel to travel exterior of the drawing expanse bounds.

By combining the previous 2 issues, it was possible to larn the x coordinate of a business sufficiently modest (smaller than -0.5), so that, when a fractional representation was rounded to an integer here, Skia attempted to clit at coordinates alongside x = -1, which is clearly exterior the icon bounds. This so led to an out-of-bounds write as tin dismiss hold upwards seen inward the original põrnikas report. This põrnikas could hold upwards exploited inward Firefox past times drawing an SVG icon alongside coordinates as described inward the previous section.

Floating indicate precision fault when converting splines to business segments

When drawing paths, Skia is going to convert all non-linear curves (conic shapes, quadratic as well as cubic splines) to business segments. Perhaps unsurprisingly, these conversions endure from precision errors.

The conversion of splines into business segments fall out inward several places, but the most susceptible to floating-point precision errors are hair_quad (used for drawing quadratic curves) as well as hair_cubic (used for drawing cubic curves). Both of these functions are called from hair_path, which nosotros already mentioned above. Because (unsurprisingly), larger precision errors occur when dealing alongside cubic splines, we’ll entirely consider the cubic instance here.

When approximating the spline, kickoff the cubic coefficients are computed inward SkCubicCoeff. The most interesting component is:

fA = P3 + 3 * (P1 - P2) - P0;
fB = 3 * (P2 - times_2(P1) + P0);
fC = 3 * (P1 - P0);
fD = P0;

Where P1, P2 as well as P3 are input points as well as fA, fB, fC as well as fD are output coefficients. The business segment points are so computed inward hair_cubic using the next code

const Sk2s dt(SK_Scalar1 / lines);
Sk2s t(0);

...

Sk2s H5N1 = 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 the output indicate as well as lines is the number of business segments nosotros are using to approximate the curve. Depending on the length of the spline, a cubic spline tin dismiss hold upwards approximated alongside upwards to 512 lines.

It is obvious that the arithmetics hither is non going to hold upwards precise. As identical computations fall out for x as well as y coordinates, let’s but consider the x coordinate inward the repose of the post.

Let’s assume the width of the drawing expanse is one m pixels. Because hair_path is used for drawing path alongside antialiasing turned on, it needs to brand certain that all points of the path are betwixt 1 as well as 999, which is done inward the initial, path-level clip check. Let’s consider the next coordinates that all travel past times this check:

p0 = 1.501923
p1 = 998.468811
p2 = 998.998779
p3 = 999.000000

For these points, the coefficients are as follows

a = 995.908203
b = -2989.310547
c = 2990.900879
d = 1.501923

If you lot do the same computation inward larger precision, you’re going to notice that the numbers hither aren’t quite correct. Now let’s run across what happens if nosotros approximate the spline alongside 512 business segments. This results inward 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 tin dismiss run across that the x coordinate keeps growing as well as at indicate 511 clearly goes exterior of the “safe” expanse as well as 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 inward Skia, nosotros demand to travel at to the lowest degree 1/64 of a pixel exterior of the clip expanse for it to larn a security issue. However, an interesting affair most the precision errors inward this instance is that the larger the drawing area, the larger the fault that tin dismiss happen.

So let’s instead consider a drawing expanse of 32767 pixels (maximum sail size inward Chrome). The initial clipping depository fiscal establishment check so checks that all path points are inward the interval [1, 32766]. Now let’s consider the next 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 the corresponding business 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 tin dismiss run across that nosotros went out-of-bounds significantly to a greater extent than at index 511.

Fortunately for Skia as well as unfortunately for aspiring attackers, this põrnikas can’t hold upwards used to trigger retentiveness corruption, at to the lowest degree non inward the up-to-date version of skia. The argue is SkDrawTiler. Whenever Skia draws using SkBitmapDevice (as opposed to using a GPU device) as well as the drawing expanse is larger than 8191 pixels inward whatever dimension, instead of drawing the whole icon at once, Skia is going to separate it into tiles of size (at most) 8191x8191 pixels. This change was made inward March, non for security reasons, but to hold upwards able to back upwards larger drawing surfaces. However, it nonetheless effectively prevented us from exploiting this number as well as volition also forestall exploiting other cases where a surface larger than 8191 is required to attain the precision fault of a sufficient magnitude.

Still, this põrnikas was exploitable earlier March as well as nosotros retrieve it nicely demonstrates the concept of precision errors.

Integer precision fault when converting splines to business segments

There is some other house where splines are approximated as business segments when drawing (in this case: filling) paths that was also affected past times a precision error, inward this instance an exploitable one. Interestingly, hither the precision fault wasn’t inward floating-point but rather inward fixed-point arithmetic.

The fault happens inward SkQuadraticEdge::setQuadraticWithoutUpdate as well as SkCubicEdge::setCubicWithoutUpdate. For simplicity, nosotros are in i lawsuit to a greater extent than going to concentrate but on the cubic spline version and, again, entirely on the x coordinate.

In SkCubicEdge::setCubicWithoutUpdate, the bend coordinates are kickoff converted to SkFDot6 type (integer alongside vi bits used for fraction). After that, parameters corresponding to the first, minute as well as 3rd derivative of the bend at the initial indicate are going to hold upwards 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 past times shift
fCDDx   = 2*C + (3*D >> (shift - 1));           // biased past times 2*shift
fCDDDx  = 3*D >> (shift - 1);                   // biased past times 2*shift

Where x0, x1, x2 as well as x3 are x coordinates of the four points that define the cubic spline as well as shift as well as upShift depend on the length of the bend (this corresponds to the number of linear segments the bend is going to hold upwards approximated in). For simplicity, nosotros tin dismiss assume shift = upShift = vi (maximum possible values).

Now let’s run across what happens for some real uncomplicated input values:

x0 = -30
x1 = -31
x2 = -31
x3 = -31

Note that x0, x1, x2 as well as x3 are of the type SkFDot6 so value -30 corresponds to -0.46875 as well as -31 to -0.484375. These are closed to -0.5 but non quite as well as are thus perfectly rubber when rounded. Now let’s examine the values of the computed parameters:

B = -192
C = 192
D = -64

fCx = -30720
fCDx = -190
fCDDx = 378
fCDDDx = -6

Do you lot run across where the number is? Hint: it’s inward the formula for fCDx.

When computing fCDx (first derivation of a curve), the value of D needs is right-shifted past times 12. However, D is as good modest to do that precisely, as well as since D is negative, the right shift

D >> 2*shift

Is going to final result inward -1, which is larger inward magnitude than the intended result. (Since D is of type SkFixed its actual value is -0.0009765625 as well as the shift, when interpreted as sectionalization past times 4096, would final result inward -2.384185e-07). Because of this, the whole fCDx ends upwards as a larger negative value than it should (-190 vs. -189.015).

Afterwards, the value of fCDx gets used when calculating the x value of business segments. This happens inward SkCubicEdge::updateCubic on this line:

newx    = oldx + (fCDx >> dshift);

The x values, when approximating the spline alongside 64 business segments (maximum for this algorithm), are going to hold upwards (expressed as index, integer SkFixed value as well as the corresponding floating indicate 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 tin dismiss run across that for the 35th point, the x value (-0.484421) ends upwards beingness smaller than the smallest input indicate (-0.484375) as well as the tendency continues for the afterward points. This value would nonetheless larn rounded to 0 though, but at that topographic point is some other problem.

The x values computed inward SkCubicEdge::updateCubic are passed to SkEdge::updateLine, where they are converted from SkFixed type to SkFDot6 on the following lines:

x0 >>= 10;
x1 >>= 10;

Another right shift! And when, for example, SkFixed value -31747 gets shifted nosotros cease upwards alongside SkFDot6 value of -32 which represents -0.5.

At this indicate nosotros tin dismiss usage the same fob described higher upwards inward the “Precision fault when multiplying fractions” department to travel smaller than -0.5 as well as interruption out of the icon bounds. In other words, nosotros tin dismiss brand Skia clit to x = -1 when drawing a path.

But, what tin dismiss nosotros do alongside it?

In general, given that Skia allocates icon pixels as a unmarried allotment that is organized row past times row (as most other software would allocate bitmaps), at that topographic point are several cases of what tin dismiss fall out alongside precision issues. If nosotros assume an width x height icon as well as that nosotros are entirely able to travel i pixel out of bounds:

  1. Drawing to y = -1 or y = pinnacle forthwith leads to heap out-of-bounds write
  2. Drawing to x = -1 alongside y = 0 forthwith leads to a heap underflow of 1 pixel
  3. Drawing to x = width alongside y = pinnacle - 1 forthwith leads to heap overflow of 1 pixel
  4. Drawing to x = -1 alongside y > 0 leads to a pixel “spilling” to the previous icon row
  5. Drawing to x = pinnacle alongside y < height-1 leads to a pixel “spilling” to the side past times side icon row

What nosotros receive got hither is scenario d) - unfortunately nosotros can’t clit to x = 1 alongside y = 0 because the precision fault needs to accumulate over the growing values of y.

Let’s accept a human face at the next illustration SVG image:

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<style>
body {
margin-top: 0px;
margin-right: 0px;
margin-bottom: 0px;
margin-left: 0px
}
</style>
<path d="M -0.46875 -0.484375 C -0.484375 -0.484375, -0.484375 -0.484375, -0.484375 100 L 1 100 L 1 -0.484375" fill="red" shape-rendering="crispEdges" />
</svg>

If nosotros homecoming this inward an unpatched version of Firefox what nosotros run across is shown inward the next image. Notice how the SVG entirely contains coordinates on the left side of the screen, but some of the cerise pixels larn drawn on the right. This is because, due to the way images are allocated, drawing to x = -1 as well as y = row is equal to drawing to x = width - 1 as well as y = row - 1.


Opening an SVG icon that triggers a Skia precision number inward Firefox. If you lot human face closely you’ll notice some cerise pixels on the right side of the image. How did those larn there? :)

Note that nosotros used Mozilla Firefox as well as non Google Chrome because, due to SVG drawing internals (specifically: Skia seems to clit the entire icon at once, piece Chrome uses additional tiling) it is easier to demonstrate the number inward Firefox. However, both Chrome as well as Firefox were as affected past times this issue.

But, other than drawing a funny image, is at that topographic point existent security impact to this issue? Here, SkARGB32_Shader_Blitter comes to the rescue (SkARGB32_Shader_Blitter is used whenever shader effects are applied to a color inward Skia). What is specific most SkARGB32_Shader_Blitter is that it allocates a temporary buffer of the same size as a unmarried icon row. When SkARGB32_Shader_Blitter::blitH is used to clit an entire icon row, if nosotros tin dismiss travel far clit from x = -1 to x = width - 1 (alternately from x = 0 to x = width), it volition demand to write width + 1 pixels into a buffer that tin dismiss entirely concur width pixels, leading to a buffer overflow as tin dismiss hold upwards seen inward the ASan log inward the bug report.

Note how the PoCs for Chrome as well as Firefox incorporate SVG images alongside a linearGradient chemical ingredient - the linear slope is used specifically to pick out SkARGB32_Shader_Blitter instead of drawing pixels to the icon directly, which would entirely final result inward pixels spilling to the previous row.

Another specific of this number is that it tin dismiss entirely hold upwards reached when drawing (more specifically: filling) paths alongside antialiasing turned off. As it is non currently possible to clit paths to a HTML sail elements alongside antialiasing off (there is an imageSmoothingEnabled belongings but it entirely applies to drawing images, non paths), an SVG icon alongside shape-rendering="crispEdges" must hold upwards used to trigger the issue.

All precision issues nosotros reported inward Skia were fixed by increasing kConservativeRoundBias. While the electrical flow bias value is large plenty to comprehend the maximum precision errors nosotros know about, nosotros should non dismiss the possibility of other places where precision issues tin dismiss occur.

Conclusion

While precision issues, such as described inward this weblog post, won’t hold upwards introduce inward most software products, where they are introduce they tin dismiss receive got quite serious consequences. To forestall them from occurring:

  • Don’t usage floating-point arithmetics inward cases where the final result is security-sensitive. If you lot absolutely receive got to, so you lot demand to brand certain that the maximum possible precision fault cannot hold upwards larger than some security margin. Potentially, interval arithmetic could hold upwards used to create upwards one's hear the maximum precision fault inward some cases. Alternately, perform security checks on the final result rather than input.

  • With integer arithmetic, hold upwards wary of whatever operations that tin dismiss cut down the precision of the result, such as divisions as well as right shifts.

When it comes to finding such issues, unfortunately, at that topographic point doesn’t seem to hold upwards a groovy way to do it. When nosotros started looking at Skia, initially nosotros wanted to essay using symbolic execution on the drawing algorithms to abide by input values that would Pb to drawing out-of-bounds, as, on the surface, it seemed this is a employment symbolic execution would hold upwards good suited for. However, inward practice, at that topographic point were as good many issues: most tools don’t back upwards floating indicate symbolic variables and, fifty-fifty when running against but the integer parts of the simplest business drawing algorithm, nosotros were unsuccessful inward completing the run inward a reasonable fourth dimension (we were using KLEE alongside STP as well as Z3 backends).

In the end, what nosotros ended upwards doing was a combination of the to a greater extent than old-school methods: manual source review, fuzzing (especially alongside values closed to icon boundaries) and, inward some cases, when nosotros already identified potentially problematic areas of code, fifty-fifty bruteforcing the make of all possible values.

Do you lot know of other instances where precision errors resulted inward security issues? Let us know most them inward the comments.

Komentar

Postingan populer dari blog ini

Chrome Bone Exploit: 1 Byte Overflow As Well As Symlinks

The next article is an invitee weblog post from an external researcher (i.e. the writer is non a or Google researcher). This post is most a Chrome OS exploit I reported to Chrome VRP inward September. The folks were squeamish to allow me do a invitee post most it, therefore hither goes. The study includes a detailed writeup , therefore this post volition have got less detail. 1 byte overflow inward a DNS library In Apr I constitute a TCP port listening on localhost inward Chrome OS. It was an HTTP proxy built into shill, the Chrome OS network manager. The proxy has at nowadays been removed equally component of a fix, but its source tin give notice nonetheless move seen from an one-time revision: shill/http_proxy.cc . The code is unproblematic in addition to doesn’t seem to incorporate whatever obvious exploitable bugs, although it is real liberal inward what it accepts equally incoming HTTP. It calls into the c-ares library for resolving DNS. There was a possible 1 byte ov...

Exception-Oriented Exploitation On Ios

Posted past times Ian Beer, This postal service covers the regain in addition to exploitation of CVE-2017-2370 , a heap buffer overflow inwards the mach_voucher_extract_attr_recipe_trap mach trap. It covers the bug, the evolution of an exploitation technique which involves repeatedly in addition to deliberately crashing in addition to how to build alive meat introspection features using onetime meat exploits. It’s a trap! Alongside a large number of BSD syscalls (like ioctl, mmap, execve in addition to so on) XNU also has a pocket-sized number of extra syscalls supporting the MACH side of the meat called mach traps. Mach trap syscall numbers start at 0x1000000. Here’s a snippet from the syscall_sw.c file where the trap tabular array is defined: /* 12 */ MACH_TRAP(_kernelrpc_mach_vm_deallocate_trap, 3, 5, munge_wll), /* xiii */ MACH_TRAP(kern_invalid, 0, 0, NULL), /* xiv */ MACH_TRAP(_kernelrpc_mach_vm_protect_trap, 5, 7, munge_wllww), Most of the mach traps a...

Lifting The (Hyper) Visor: Bypassing Samsung’S Real-Time Total Protection

Posted yesteryear Gal Beniamini, Traditionally, the operating system’s total is the concluding security boundary standing betwixt an assaulter together with total command over a target system. As such, additional aid must hold upwards taken inwards lodge to ensure the integrity of the kernel. First, when a organization boots, the integrity of its primal components, including that of the operating system’s kernel, must hold upwards verified. This is achieved on Android yesteryear the verified kicking chain . However, only booting an authenticated total is insufficient—what most maintaining the integrity of the total spell the organization is executing? Imagine a scenario where an assaulter is able to abide by together with exploit a vulnerability inwards the operating system’s kernel. Using such a vulnerability, the assaulter may endeavor to subvert the integrity of the total itself, either yesteryear modifying the contents of its code, or yesteryear introducing novel attacker-co...