OpenPrinting / OpenPrinting/libcupsfilters

texttopdf: prettyprint without wrapping corrupts the page heap

Open
#197 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C
Stars
17
Forks
71
Avg merge
2d 17h
Merged PRs (30d)
13

Description

Summary

With prettyprint=true wrap=false, a source line can advance beyond the
allocated page width. If text beyond that boundary is recognized as a
keyword, cfFilterTextToPDF() writes ATTR_BOLD to lchar_t cells after the
page allocation.

The document and normal filter options reach the standalone texttopdf
pipeline. The demonstrated write sets a fixed attribute bit at four-byte
intervals; it is reliable heap corruption, but not an arbitrary write.

Reproduction

Final upstream recheck on 2026-08-02: OpenPrinting/cups-filters 11d1a190530f85a361a1b3835f57d635256dff1a, libcupsfilters 905fd94fb22a9298bc8e2c845eb7e04f7055b686, and libppd fc41539f761286396a7df8aeeda762070192e37e.

Validated revisions:

  • cups-filters: 11d1a190530f85a361a1b3835f57d635256dff1a
  • libcupsfilters: 905fd94fb22a9298bc8e2c845eb7e04f7055b686
  • libppd: 522af8dd135f4dde66b1aac8b9d067808bbe122d

poc/document.txt contains 6,468 spaces followed by while . Its SHA-256 is
9fbe67a38cc10f92818dd642435843ca7ca7fcebff33b7cc3cd078d02d9446a2.

Run:

./reproduce.sh

The following commented Bash script constructs the exact PoC and control
inputs. Save it as make_poc.sh, then run bash make_poc.sh poc.

#!/usr/bin/env bash
set -euo pipefail

# PoC: texttopdf: prettyprint without wrapping corrupts the page heap
# Finding ID: texttopdf-prettyprint-nowrap-heap-oob
# Trigger: With prettyprint=true wrap=false, a source line can advance beyond
# the allocated page width. If text beyond that boundary is recognized as a
# keyword, cfFilterTextToPDF() writes ATTR_BOLD to lchar_t cells after the
# page allocation.
#
# Binary PDFs, Raster files, images, and PPDs are stored as gzip-compressed
# base64 so embedded NUL bytes and exact parser offsets survive copy/paste.
# Every reconstructed file is checked before the vulnerable program is run.

OUTPUT_DIR="${1:-poc}"
mkdir -p "$OUTPUT_DIR"

for tool in base64 gzip sha256sum; do
  command -v "$tool" >/dev/null || {
    printf 'missing required tool: %s\n' "$tool" >&2
    exit 1
  }
done

write_file() {
  local name="$1"
  local expected_sha256="$2"
  local path="$OUTPUT_DIR/$name"
  local actual_sha256

  mkdir -p "$(dirname "$path")"
  base64 --decode | gzip --decompress > "$path"
  actual_sha256="$(sha256sum "$path")"
  actual_sha256="${actual_sha256%% *}"
  if [[ "$actual_sha256" != "$expected_sha256" ]]; then
    printf 'SHA-256 mismatch for %s\n' "$path" >&2
    return 1
  fi
  printf '%s  %s bytes  sha256=%s\n' \
    "$path" "$(wc -c < "$path")" "$actual_sha256"
}

# Malformed or boundary document consumed by the real filter/API.
# Output: document.txt (6474 bytes)
write_file document.txt 9fbe67a38cc10f92818dd642435843ca7ca7fcebff33b7cc3cd078d02d9446a2 <<'POC_PAYLOAD_0'
H4sIAAAAAAACA+3BwQkAMAgEsFVuOaGCf9fv3xmSJAAAAAAAAFz7eiofJraT60oZAAA=
POC_PAYLOAD_0

Result

ASan reports a heap-buffer-overflow at texttopdf.c:1292. The plain build
reaches glibc heap consistency checks and aborts with
double free or corruption (out), status 134. Changing the option to
wrap=true completes normally.

Cause and expected behavior

When wrapping is disabled, the input loop at texttopdf.c:883 allows
column to grow beyond the physical page width. The syntax highlighter saves
that position as keycol at line 1304 and, after recognizing a keyword,
iterates from keycol to column at lines 1290-1293. It does not verify that
keycol + page-column offset is inside the contiguous page allocation made
at line 964.

The filter should clip syntax attributes to the allocated row width, or stop
recording/highlighting columns once no-wrap text leaves the page.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in texttopdf.c at the input loop around line 883, page allocation around line 964, and syntax-highlighting code around lines 1290-1304. Run reproduce.sh with the supplied poc/document.txt, preferably under ASan, and compare with wrap=true. Done means no out-of-bounds write or heap abort when prettyprint=true and wrap=false, while valid highlighting remains bounded by the allocated page width.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
cli, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.