Skip to content

Heap-buffer-overflow READ in pdftoraster.c write_page_image() #145

@kimaiden1984-boop

Description

@kimaiden1984-boop

Summary

A heap-buffer-overflow read vulnerability exists in cupsfilters/pdftoraster.c in the write_page_image() function. When a PDF page renders to an image smaller than the expected page size (a documented and expected scenario), an off-by-one error in the forward copy loop causes one extra row iteration, reading heap data past the allocated image buffer. The out-of-bounds data is passed through the color management pipeline and written to the CUPS raster output, leaking heap contents into the printer output stream.


Root Cause

In write_page_image(), lines 1970-1971 compute safe copy limits:

unsigned int copy_height = (height < doc->header.cupsHeight) ? height : doc->header.cupsHeight;
unsigned int copy_width = (width < doc->header.cupsWidth) ? width : doc->header.cupsWidth;

The forward loop at line 2011 then uses <= instead of <:

if (h <= copy_height)         // ← BUG: should be h < copy_height

When the rendered image height is smaller than the page height, copy_height equals the image height. The loop iterates from h=0 to h=cupsHeight-1. After processing copy_height valid rows, bp has advanced by copy_height * image_rowsize bytes — exactly to the end of the heap allocation. The <= condition allows one additional iteration at h == copy_height, where bp points one row past the allocated buffer. The subsequent convertLine(bp, ...) call reads from this out-of-bounds pointer.

The same bug exists in the duplex/reverse loop at line 1981.


Affected Versions

  • Introduced: commit 0ad5d2a08b40ff4e3f1406a9325cb309050f3800 (2026-01-24)
  • Not in any release tag: The commit sits on master, 34 commits after tag v2.1.1
  • Any build from master between 0ad5d2a0 and a fix is affected

Trigger Conditions

The bug triggers when all of these conditions are met:

  1. A PDF page is rendered by pdftoppm (or any configured PDF renderer) to an image smaller than the output page dimensions — a common scenario with font substitution, differing page size definitions, or rendering differences
  2. The forward (non-duplex, non-swap) loop path is taken
  3. Color space conversion requires reading from the source buffer (true for all non-trivial converters)

These conditions are realistic and occur during normal printer operation.


Impact

Information Disclosure

The out-of-bounds heap data flows through the color management subsystem (lcms2) and is written to the CUPS raster output via cupsRasterWritePixels(). This means heap data is leaked into the printer output stream — either to a captured raster file or to the physical printer.

Leak volume: One image_rowsize (page width × bytes per pixel) per triggering page. For a typical 300 DPI A4 CMYK job: ~9600 bytes of heap data per occurrence.

Denial of Service (related, secondary)

The same commit also added unconditional memset(lineBuf, ...) calls (lines 1983 and 2013) without checking doc->allocLineBuf. When the special-case color space path sets allocLineBuf = false (14 entries including CUPS_CSPACE_K, CUPS_CSPACE_W, CUPS_CSPACE_SW, etc.), lineBuf is NULL, causing a segmentation fault at the memset call.


Reproduction

Standalone (no external dependencies)

cd cupsfilters
gcc -std=c11 -O0 -g -fsanitize=address -fno-omit-frame-pointer \
  test-pdftoraster-copy-height-asan.c -o /tmp/oob-harness
ASAN_OPTIONS=detect_leaks=0 /tmp/oob-harness

Expected output:

==PID==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1
    #0 identity_convert test-pdftoraster-copy-height-asan.c:79
    #1 convert_line_chunked test-pdftoraster-copy-height-asan.c:100
    #2 main test-pdftoraster-copy-height-asan.c:206

Real pipeline (requires ASan-instrumented lcms2)

cd cupsfilters
bash reproduce-oob-read-pipeline.sh

Expected output:

==PID==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1
    #0 Unroll3Bytes /tmp/lcms2-2.14/src/cmspack.c:308
    #1 cmsDoTransform /tmp/lcms2-2.14/src/cmsxform.c:206
    #2 convert_cspace_with_profiles cupsfilters/pdftoraster.c:855
    #3 convert_line_chunked cupsfilters/pdftoraster.c:1142
    #4 write_page_image cupsfilters/pdftoraster.c:2017
    #5 out_page cupsfilters/pdftoraster.c:2301
    #6 cfFilterPDFToRaster cupsfilters/pdftoraster.c:2674

Note: Without ASan-instrumented lcms2, the OOB read inside cmsDoTransform is silent — no runtime detection in production builds.


poc.zip

Attachments

  • test-pdftoraster-copy-height-asan.c — Minimal standalone reproduction (no external deps)
  • reproduce-oob-read.sh — Standalone repro script (build + run, cached)
  • reproduce-oob-read-pipeline.sh — Full pipeline repro script (requires ASan-lcms2)
  • test-pdftoraster-oob-read-harness.c — Real-pipeline harness source

All attachments are located in cupsfilters/ of the repository.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions