OpenPrinting / OpenPrinting/libcupsfilters

texttopdf: truncated UTF-8 title overruns make_wide() allocation

Open
#178 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

make_wide() allocates its destination from the byte length of the job title,
but its two- and three-byte UTF-8 branches do not check that continuation
bytes remain. A one-byte title containing 0xc0 advances past its terminating
NUL, reads adjacent argument storage, and writes the wide-string terminator
past the heap allocation.

This is reachable through the standalone wrapper and direct filter API.
Standard CUPS job submission rejects malformed UTF-8 job names, so normal
remote reachability is low.

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

The title byte is stored in poc/title.bin; its SHA-256 is
e4ff5e7d7a7f08e9800a3e25cb774533cb20040df30b6ba10f956f9acd0eb3f7.
The one-byte document has SHA-256
559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd.

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: truncated UTF-8 title overruns make_wide() allocation
# Finding ID: texttopdf-malformed-title-heap-oob
# Trigger: make_wide() allocates its destination from the byte length of the
# job title, but its two- and three-byte UTF-8 branches do not check that
# continuation bytes remain. A one-byte title containing 0xc0 advances past
# its terminating NUL, reads adjacent argument storage, and writes the wide-
# string terminator past the heap 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 (1 bytes)
write_file document.txt 559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd <<'POC_PAYLOAD_0'
H4sIAAAAAAACA3MEAIue2dMBAAAA
POC_PAYLOAD_0

# Malformed or boundary document consumed by the real filter/API.
# Output: title.bin (1 bytes)
write_file title.bin e4ff5e7d7a7f08e9800a3e25cb774533cb20040df30b6ba10f956f9acd0eb3f7 <<'POC_PAYLOAD_1'
H4sIAAAAAAACAzsAAD0tZkkBAAAA
POC_PAYLOAD_1

Result

ASan reports a heap-buffer-overflow write of size two at
texttopdf.c:2427, immediately after an eight-byte allocation. The plain
build completes, so the demonstrated observable impact is sanitizer-visible
heap corruption.

Cause and expected behavior

make_wide() allocates (strlen(buf) + 1) * sizeof(lchar_t) at line 2396.
Lines 2405-2422 consume two or three input bytes based only on the lead byte,
without validating continuation bytes or the remaining string length. The
loop can therefore step over the original NUL and emit more lchar_t objects
than were allocated.

The conversion must validate each sequence before consuming it and replace or
reject truncated/invalid UTF-8. CUPS already calls ippValidateAttribute() on
job names in its scheduler path, but direct callers must not depend on that
external validation for memory safety.

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 make_wide(), especially the allocation at line 2396 and UTF-8 handling at lines 2405-2422. Run ./reproduce.sh with the supplied title.bin and document.txt under an ASan build to observe the failure. Done means truncated or invalid UTF-8 is rejected or replaced without reading past the input or writing beyond the allocation.

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
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.