OpenPrinting / OpenPrinting/libcupsfilters
texttopdf: prettyprint without wrapping corrupts the page heap
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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