Bug 100056

Summary: pdftocairo crashes on converting big images in PDFs
Product: poppler Reporter: Alex Korobkin <korobkin+lpad>
Component: cairo backendAssignee: poppler-bugs <poppler-bugs>
Status: RESOLVED FIXED QA Contact:
Severity: normal    
Priority: medium    
Version: unspecified   
Hardware: x86-64 (AMD64)   
OS: Linux (All)   
Whiteboard:
i915 platform: i915 features:
Attachments: onepager.pdf
don't overflow y*stride on 64-bit

Description Alex Korobkin 2017-03-03 19:58:03 UTC
Created attachment 130054 [details]
onepager.pdf

When converting the PDFs that contain just a single image (large grayscale image, seems like a scan), pdftocairo segfaults. 
Tested on Ubuntu 14.04 x64, 
cairo 1.14.8
poppler 0.49.0

$ /usr/bin/pdftocairo -ps -level3 onepager.pdf out.ps
Segmentation fault

Yes, the file contains large image inside: PNG image data, 19833 x 28067, 8-bit/color RGBA, non-interlaced, but pdftops converts the file instantly. 

Here is the output from gdb:

(gdb) file /usr/bin/pdftocairo
Reading symbols from /usr/bin/pdftocairo...Reading symbols from /usr/lib/debug/.build-id/06/b55d5ad8a0410ac56bafe79e17dca8d917b612.debug...done.
Unable to determine compiler version.
done.
(gdb) set args -ps -level3 onepager.pdf out.ps
(gdb) run
Starting program: /usr/bin/pdftocairo -ps -level3 onepager.pdf out.ps
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff6575034 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) bt
#0  0x00007ffff6575034 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff736b594 in _cairo_image_surface_snapshot (abstract_surface=0x5555557b9a40) at /tmp/build-debs.brV892/build/src/cairo-image-surface.c:792
#2  0x00007ffff73c0516 in _cairo_surface_snapshot_copy_on_write (surface=0x5555557ba390) at /tmp/build-debs.brV892/build/src/cairo-surface-snapshot.c:189
#3  0x00007ffff73b62ef in _cairo_surface_detach_snapshot (snapshot=0x5555557ba390) at /tmp/build-debs.brV892/build/src/cairo-surface.c:348
#4  0x00007ffff73b625d in _cairo_surface_detach_snapshots (surface=0x5555557b9a40) at /tmp/build-debs.brV892/build/src/cairo-surface.c:333
#5  0x00007ffff73b7d29 in _cairo_surface_flush (surface=0x5555557b9a40, flags=0) at /tmp/build-debs.brV892/build/src/cairo-surface.c:1545
#6  0x00007ffff73b7481 in _cairo_surface_finish_snapshots (surface=0x5555557b9a40) at /tmp/build-debs.brV892/build/src/cairo-surface.c:1017
#7  0x00007ffff73b72f1 in INT_cairo_surface_destroy (surface=0x5555557b9a40) at /tmp/build-debs.brV892/build/src/cairo-surface.c:961
#8  0x00007ffff738f51c in _cairo_pattern_fini (pattern=0x5555557b98a0) at /tmp/build-debs.brV892/build/src/cairo-pattern.c:443
#9  0x00007ffff738feea in INT_cairo_pattern_destroy (pattern=0x5555557b98a0) at /tmp/build-debs.brV892/build/src/cairo-pattern.c:1131
#10 0x000055555556950a in CairoOutputDev::drawImage (this=<optimized out>, state=0x5555557bcd70, ref=0x7fffffffd960, str=0x5555557bb9a0, widthA=<optimized out>, 
    heightA=<optimized out>, colorMap=0x5555558012b0, interpolate=false, maskColors=0x0, inlineImg=false) at CairoOutputDev.cc:3265
#11 0x00007ffff7a618cd in Gfx::doImage (this=this@entry=0x5555557af5a0, ref=ref@entry=0x7fffffffd960, str=0x5555557bb9a0, inlineImg=inlineImg@entry=false) at Gfx.cc:4709
#12 0x00007ffff7a62ed9 in Gfx::opXObject (this=0x5555557af5a0, args=<optimized out>, numArgs=<optimized out>) at Gfx.cc:4208
#13 0x00007ffff7a5cd79 in Gfx::go (this=this@entry=0x5555557af5a0, topLevel=topLevel@entry=true) at Gfx.cc:763
#14 0x00007ffff7a5d18d in Gfx::display (this=this@entry=0x5555557af5a0, obj=obj@entry=0x7fffffffdd40, topLevel=topLevel@entry=true) at Gfx.cc:729
#15 0x00007ffff7aa5128 in Page::displaySlice (this=0x5555557a93e0, out=0x5555557a9d70, hDPI=72, vDPI=72, rotate=<optimized out>, useMediaBox=<optimized out>, 
    crop=<optimized out>, sliceX=<optimized out>, sliceY=-1, sliceW=-1, sliceH=-1, printing=true, abortCheckCbk=0x0, abortCheckCbkData=0x0, annotDisplayDecideCbk=0x0, 
    annotDisplayDecideCbkData=0x0, copyXRef=false) at Page.cc:601
#16 0x000055555555fb61 in renderPage (output_h=842, output_w=595, page_h=<optimized out>, page_w=<optimized out>, pg=1, cairoOut=0x5555557a9d70, doc=0x5555557b0050)
    at pdftocairo.cc:673
#17 main (argc=3, argv=<optimized out>) at pdftocairo.cc:1197
(gdb)
Comment 1 Alex Korobkin 2017-03-06 16:17:15 UTC
Perhaps it would be more reasonable to file this bug under poppler component. If the underlying bug is in cairo lib, not pdftocairo, please route it back to cairo component?

Thank you.
-Alex
Comment 2 foca@salesforce.com 2017-06-14 22:24:04 UTC
This bug is in the cairo backend component of poppler, in CairoOutputDev.cc:3090.

3087       buffer = cairo_image_surface_get_data (image);
3088       stride = cairo_image_surface_get_stride (image);
3089       for (int y = 0; y < height; y++) {
3090         uint32_t *dest = (uint32_t *) (buffer + y * stride);
3091         getRow(y, dest);
3092       }

The vulnerability is an integer overflow with the operation y * stride is calculated. y and stride are signed int variables. So the y * stride can be overflown in certain situations. When this happens the result of y * stride is a negative number that gets added (subtracted) to buffer. 

The result is the variable data pointing to an invalid area and the program crashing.

A possible fix is to replace:
3078	      int stride;
3089	      for (int y = 0; y < height; y++) {
for:
3078	      unsigned int stride;
3089	      for (unsigned int y = 0; y < height; y++) {
Comment 3 Adrian Johnson 2017-06-14 22:34:16 UTC
ptrdiff_t would be better than unsigned to ensure it does not overflow on 64-bit. The bug also needs to be fixed in cairo (bug 98165).
Comment 4 Adrian Johnson 2017-11-08 10:43:35 UTC
Created attachment 135296 [details] [review]
don't overflow y*stride on 64-bit

The fix for cairo has been committed to master. Here is the corresponding fix for poppler. I'll push it out next week if there are no objections.
Comment 5 Carlos Garcia Campos 2017-11-08 15:40:51 UTC
No objections.
Comment 6 Adrian Johnson 2017-11-09 08:13:40 UTC
Pushed

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.