OpenPrinting / OpenPrinting/libcupsfilters
texttotext: accepted zero tab width causes division by zero
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 17
- Forks
- 71
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 13
Description
Summary
cfFilterTextToText() accepts TabWidth=0, logs that the value is invalid and
the default will be used, but leaves the internal tab_width set to zero. A
single tab in the document then reaches a modulo operation with a zero divisor
and terminates the filter with SIGFPE.
The failure is deterministic through the normal standalone filter pipeline and
requires only a one-byte text document.
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 is one tab byte (0x09) and has SHA-256
2b4c342f5433ebe591a1da77e013d1b72475562d48578dca8b84bac6651c3cb9.
Run:
./reproduce.sh
This supplies TabWidth=0 through the standard CUPS job-options argument.
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: texttotext: accepted zero tab width causes division by zero
# Finding ID: texttotext-zero-tab-width-fpe
# Trigger: cfFilterTextToText() accepts TabWidth=0, logs that the value is
# invalid and the default will be used, but leaves the internal tab_width set
# to zero. A single tab in the document then reaches a modulo operation with a
# zero divisor and terminates the filter with SIGFPE.
#
# 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 (1 bytes)
write_file document.txt 2b4c342f5433ebe591a1da77e013d1b72475562d48578dca8b84bac6651c3cb9 <<'POC_PAYLOAD_0'
H4sIAAAAAAACA+MEAClX3qsBAAAA
POC_PAYLOAD_0
Result
UBSan and ASan report:
texttotext.c:1055:20: runtime error: division by zero
ERROR: AddressSanitizer: FPE
cfFilterTextToText .../cupsfilters/texttotext.c:1055:20
The complete diagnostic is in asan.txt. The unsanitized build also receives
SIGFPE and exits with status 136. Its log first says
Tab width not set or invalid, use default, then reports Tab width: 0.
Cause and expected behavior
texttotext.c:551-570 parses the option directly into tab_width.
Lines 573-575 detect that zero is invalid but only log a message; they do not
restore the initialized default value of eight. At line 1055, processing a tab
evaluates column % tab_width.
An invalid option must either be rejected or replaced with the documented
default before document processing. No job option should leave a zero divisor
in the formatter.
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 texttotext.c around lines 551-575 to trace TabWidth parsing and the invalid-value handling, then inspect line 1055 where the tab is processed. Run reproduce.sh with poc/document.txt and TabWidth=0. Done means invalid input cannot leave a zero divisor, the documented default is used or the option is rejected, and the reproducer no longer produces SIGFPE.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100