# Pillow: Invalid free in FreeType glyph rendering when a stroker is active

Pillow's stroked text rendering frees an uninitialized/stale glyph pointer when FT_Load_Glyph fails on a malformed font, causing invalid free.

- Payout: Research
- Severity: high
- Program: Pillow
- Found by: MegaManSec
- Disclosed: 2026-10-21
- CVE: CVE-2026-112893
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-j4m9-6w36-pw47
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-j4m9-6w36-pw47
- Tags: area:oss domain:oss impact:dos kind:research platform:ghsa sev:high tech:python technique:source-review vuln:dos vuln:memory-corruption

**Target:** src/_imagingft.c FreeType glyph rendering path
**Root cause:** The `glyph` variable is declared without a NULL initializer and is not reset to NULL after each FT_Done_Glyph; the glyph_error cleanup calls FT_Done_Glyph on it whenever a stroker exists, even if FT_Load_Glyph failed before assignment.
**Steps:**
1. Craft a TTF where glyph 'A' claims `numberOfContours = 5000` via `struct.pack_into(">h", raw, glyf_off + o0, 5000)` while the glyph body is too short, so the face still opens but FT_Load_Glyph errors.
2. Load it with `ImageFont.truetype("evil.ttf", 20)`.
3. Draw text with a non-zero stroke: `d.text((0, 0), "A", font=f, stroke_width=2)`.
4. FT_Load_Glyph fails and the glyph_error cleanup calls `FT_Done_Glyph(glyph)` on the uninitialized/stale pointer.
**How found:** Code reading of src/_imagingft.c: noticed `glyph` declared without NULL init at line 848, the measurement loop able to jump to glyph_error before assignment (1010-1013), and the render loop freeing glyph without resetting to NULL (1242) before the stroker cleanup (1253-1256).
**Exploitation:** Attacker supplies a malformed font (bytes or file object) to ImageFont.truetype; when the app renders text with stroke_width != 0, the load error reaches the cleanup path that frees an uninitialized or already-freed glyph pointer, yielding invalid free or double free.
**Insight:** In C extensions wrapping C libraries, audit error-cleanup labels for pointers that are freed unconditionally but only conditionally assigned; a stroker/optional-resource guard around a free is a classic sign the pointer may be uninitialized or stale across loop iterations.

