Summary
Running imagetoraster on a small valid grayscale PNG with a valid PPD triggers
an AddressSanitizer heap-buffer-overflow in zoom_bilinear():
SUMMARY: AddressSanitizer: heap-buffer-overflow cupsfilters/image-zoom.c:273 in zoom_bilinear
The issue reproduces with libcupsfilters built from current upstream
origin/master:
42d458abe27c6930c7c4bcc42bc115cf33145a96
42d458ab charset: Add Devanagari Unicode range to utf-8 charsets (#141)
The replay uses the cups-filters imagetoraster wrapper. The local wrapper
checkout has no relevant filter/imagetoraster.c diff against current
cups-filters origin/master.
Impact
This is a one-byte read past a two-byte heap allocation during image scaling.
The immediate observed impact is a process abort under ASan. Without ASan, this
can become an out-of-bounds read from adjacent heap memory during print-filter
processing.
Minimal Reproducer
Save this PPD as repro.ppd:
*PPD-Adobe: "4.3"
*FormatVersion: "4.3"
*FileVersion: "1.0"
*LanguageVersion: English
*LanguageEncoding: ISOLatin1
*Manufacturer: "SMT-Fuzzer"
*ModelName: "ImageToRaster Coverage Options"
*ShortNickName: "SMTTemplate"
*NickName: "ImageToRaster Coverage Options"
*PCFileName: "SMTPPD.PPD"
*Product: "(SMTTemplate)"
*PSVersion: "(3010) 0"
*cupsVersion: 1.0
*cupsModelNumber: 0
*cupsManualCopies: False
*cupsFilter: "image/x-portable-anymap 0 imagetoraster"
*OpenUI *ColorModel: PickOne
*DefaultColorModel: RGB
*ColorModel Gray/Gray: "<</cupsColorSpace 18/cupsBitsPerColor 8/cupsBitsPerPixel 8>>setpagedevice"
*ColorModel RGB/RGB: "<</cupsColorSpace 1/cupsBitsPerColor 8/cupsBitsPerPixel 24>>setpagedevice"
*ColorModel CMYK/CMYK: "<</cupsColorSpace 6/cupsBitsPerColor 8/cupsBitsPerPixel 32>>setpagedevice"
*ColorModel Black/Black: "<</cupsColorSpace 3/cupsBitsPerColor 8/cupsBitsPerPixel 8>>setpagedevice"
*CloseUI: *ColorModel
*OpenUI *PrintQuality: PickOne
*DefaultPrintQuality: Draft
*PrintQuality Draft/Draft: "<</cupsInteger0 3>>setpagedevice"
*PrintQuality Normal/Normal: "<</cupsInteger0 4>>setpagedevice"
*PrintQuality High/High: "<</cupsInteger0 5>>setpagedevice"
*PrintQuality Photo/Photo: "<</cupsInteger0 6>>setpagedevice"
*CloseUI: *PrintQuality
*OpenUI *MediaType: PickOne
*DefaultMediaType: Plain
*MediaType Plain/Plain: "<</MediaType(Plain)>>setpagedevice"
*MediaType Glossy/Glossy: "<</MediaType(Glossy)>>setpagedevice"
*MediaType Transparency/Transparency: "<</MediaType(Transparency)>>setpagedevice"
*MediaType Envelope/Envelope: "<</MediaType(Envelope)>>setpagedevice"
*CloseUI: *MediaType
*OpenUI *Duplex: PickOne
*DefaultDuplex: None
*Duplex None/Off: "<</Duplex false>>setpagedevice"
*Duplex DuplexNoTumble/Long edge: "<</Duplex true/Tumble false>>setpagedevice"
*Duplex DuplexTumble/Short edge: "<</Duplex true/Tumble true>>setpagedevice"
*CloseUI: *Duplex
*OpenUI *PageSize: PickOne
*DefaultPageSize: A4
*PageSize A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
*CloseUI: *PageSize
*OpenUI *PageRegion: PickOne
*DefaultPageRegion: A4
*PageRegion A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
*CloseUI: *PageRegion
*DefaultImageableArea: A4
*ImageableArea A4: "12 12 583 830"
*DefaultPaperDimension: A4
*PaperDimension A4: "595 842"
Generate repro.png:
#!/usr/bin/env python3
import struct
import zlib
def png_chunk(kind, payload):
crc = zlib.crc32(kind + payload) & 0xFFFFFFFF
return struct.pack(">I", len(payload)) + kind + payload + struct.pack(">I", crc)
width = 9
height = 2
raw = bytearray()
for y in range(height):
raw.append(0)
for x in range(width):
raw.append((x * 23 + y * 17) & 0xFF)
ihdr = struct.pack(">IIBBBBB", width, height, 8, 0, 0, 0, 0)
data = (
b"\x89PNG\r\n\x1a\n"
+ png_chunk(b"IHDR", ihdr)
+ png_chunk(b"IDAT", zlib.compress(bytes(raw)))
+ png_chunk(b"IEND", b"")
)
open("repro.png", "wb").write(data)
The generated input is:
PNG image data, 9 x 2, 8-bit grayscale, non-interlaced
Observed hashes for the original replay artifacts:
document.png sha256 4f5f710fca2ff97193c3584c9ec5ab6cb1a381a2256c1e649b2eb7d557b47afe
candidate.ppd sha256 c1a6d5622f4a422bcbb81b393e42287a5c661331c924e1b27b29599022b135c5
Build Configuration Used For Verification
libcupsfilters was built from upstream origin/master with ASan:
./autogen.sh
ASAN_OPTIONS=detect_leaks=0 \
PKG_CONFIG_PATH=/data/pre-gsoc/env/pdfio-install/lib/pkgconfig \
CC=gcc CXX=g++ \
CFLAGS='-g -O1 -fsanitize=address -fno-omit-frame-pointer' \
CXXFLAGS='-g -O1 -fsanitize=address -fno-omit-frame-pointer' \
LDFLAGS='-fsanitize=address' \
./configure --prefix=/data/pre-gsoc/libcupsfilters-master-install
ASAN_OPTIONS=detect_leaks=0 make -j4
The replay confirmed that the imagetoraster process loaded this freshly built
library:
libcupsfilters.so.2 => /data/pre-gsoc/libcupsfilters-master-asan/.libs/libcupsfilters.so.2
libpdfio.so.1 => /data/pre-gsoc/env/pdfio-install/lib/libpdfio.so.1
Trigger Command
env \
LD_LIBRARY_PATH=/data/pre-gsoc/libcupsfilters-master-asan/.libs:/data/pre-gsoc/env/pdfio-install/lib \
ASAN_OPTIONS=abort_on_error=0:detect_leaks=0:symbolize=1:exitcode=86 \
PPD=./repro.ppd \
/path/to/cups-filters/imagetoraster \
1 test test 1 '' \
./repro.png
Observed exit code:
ASan Excerpt
INFO: cfFilterImageToRaster: Formatting page 1.
=================================================================
ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1
#0 in zoom_bilinear cupsfilters/image-zoom.c:273
#1 in _cfImageZoomFill cupsfilters/image-zoom.c:65
#2 in cfFilterImageToRaster cupsfilters/imagetoraster.c:1726
#3 in ppdFilterCUPSWrapper
#4 in main filter/imagetoraster.c:68
0x502000007b72 is located 0 bytes after 2-byte region [0x502000007b70,0x502000007b72)
allocated by thread T0 here:
#0 in malloc
#1 in _cfImageZoomNew cupsfilters/image-zoom.c:202
#2 in cfFilterImageToRaster cupsfilters/imagetoraster.c:1678
SUMMARY: AddressSanitizer: heap-buffer-overflow cupsfilters/image-zoom.c:273 in zoom_bilinear
Full local replay log:
work/latest-master-replay/imagetoraster-image-zoom-case0001/asan.txt
Notes On The Faulting Code
The faulting expression reads the next input pixel:
*r++ = (inptr[count] * xerr0 + inptr[z_depth + count] * xerr1) / z_xsize;
In the reproducer, the allocated input row is only two bytes wide, and ASan
reports the read at the first byte immediately after that two-byte region. The
latest upstream image-zoom.c change adds a zero-size guard in
_cfImageZoomNew(), but this reproducer still reaches the out-of-bounds read
in zoom_bilinear().
Expected Result
imagetoraster should either render the image or reject the scaling parameters
without reading past the row buffer.
Actual Result
imagetoraster aborts under ASan with a heap-buffer-overflow in
zoom_bilinear().
issue-image-zoom-oob.tar.gz
Summary
Running
imagetorasteron a small valid grayscale PNG with a valid PPD triggersan AddressSanitizer heap-buffer-overflow in
zoom_bilinear():The issue reproduces with
libcupsfiltersbuilt from current upstreamorigin/master:The replay uses the
cups-filtersimagetorasterwrapper. The local wrappercheckout has no relevant
filter/imagetoraster.cdiff against currentcups-filtersorigin/master.Impact
This is a one-byte read past a two-byte heap allocation during image scaling.
The immediate observed impact is a process abort under ASan. Without ASan, this
can become an out-of-bounds read from adjacent heap memory during print-filter
processing.
Minimal Reproducer
Save this PPD as
repro.ppd:Generate
repro.png:The generated input is:
Observed hashes for the original replay artifacts:
Build Configuration Used For Verification
libcupsfilterswas built from upstreamorigin/masterwith ASan:The replay confirmed that the
imagetorasterprocess loaded this freshly builtlibrary:
Trigger Command
Observed exit code:
ASan Excerpt
Full local replay log:
Notes On The Faulting Code
The faulting expression reads the next input pixel:
In the reproducer, the allocated input row is only two bytes wide, and ASan
reports the read at the first byte immediately after that two-byte region. The
latest upstream
image-zoom.cchange adds a zero-size guard in_cfImageZoomNew(), but this reproducer still reaches the out-of-bounds readin
zoom_bilinear().Expected Result
imagetorastershould either render the image or reject the scaling parameterswithout reading past the row buffer.
Actual Result
imagetorasteraborts under ASan with a heap-buffer-overflow inzoom_bilinear().issue-image-zoom-oob.tar.gz