The archive/CVE-2020-15999
freetype/freetypeDisclosed 2020

CVE-2020-15999:freetype: Integer truncation in Load_SBit_Png

A reconstruction of the flaw, and the evidence path Lachesis follows to make it visible.

Historical recordVER-2-10-3 (last-vulnerable)
WeaknessCWE-190 → CWE-681 → CWE-787 · integer truncation in Load_SBit_Png
ImpactA integer truncation in load_sbit_png in freetype, reachable from attacker-controlled input.
Read the reconstruction
01What happened

integer truncation in Load_SBit_Png: 32-bit PNG width/height are stored into narrower fields used to size the glyph bitmap buffer, so a large PNG undersizes the buffer.

That is the shape of the failure. The rest of this case file follows the evidence behind it.

Separate the record from the run

What was known.
What Lachesis established.

01Historical record

Known before Lachesis ran

The CVE and vulnerable release were selected from public history. We are not claiming novel discovery.

CVE
CVE-2020-15999
Vulnerable target
VER-2-10-3 (last-vulnerable) · vulnerable
Prior knowledge
CVE-2020-15999 · VER-2-10-3 (last-vulnerable) is vulnerable
then
02Lachesis run
Verified captured run

Guided historical reconstruction

The experiment graph was built around the last-vulnerable release (VER-2-10-3), scoped to src/sfnt + include, enriched (pass2 dataflow + atropos bind), then enumerated BLIND over the whole constructor registry (no family hard-coded, no target line seeded). Build 22s under the 300s pass cap, bounded memory. The census below is the enumerator's own verbatim output; the real-sink note is MY reading of the advisory, kept separate from tool output.

Seeded inputs
None
Run timestamp
2026-09-03T00:00:00Z
Evidence artifact
cve-2020-15999.kuzu (built from freetype VER-2-10-3, scoped to src/sfnt + include; graph + sidecars deleted after capture)

The commands and outputs below come from this recorded Lachesis run.

The reconstruction, in one frame

What Lachesis reconstructed.

One historical repository. Four captured queries. A complete source-to-sink argument.

Lachesis Casefiles · Unbound ComputeVerified runCVE-2020-15999
integer truncation in Load_SBit_Png: 32-bit PNG width/height are stored into narrower fields used to size the glyph.
01Sourceattacker-controlled input
02Sink// see real_sink_note
03Missing guard// see real_sink_note
$ lachesis sources_of → reaches → guard_dominance
freetype/freetype · VER-2-10-3 (last-vulnerable) · vulnerableunboundcompute.com
This was known history. Your code is not.

Lachesis follows the same evidence path through unfamiliar repositories.

The Lachesis reconstruction

Here is how the path becomes visible.

These are the recorded questions Lachesis asked of the historical vulnerable code, followed by the raw result and source location each query returned.

Repositoryfreetype/freetype
RevisionVER-2-10-3 (last-vulnerable) · vulnerable
EngineLachesis

Captured runA real replay of Lachesis over the graph we built for this case (build 22s under the pass cap, bounded memory). Every census value is verbatim captured tool output; the graph was released immediately after capture, so nothing accumulates.

01 · candidates01 / 06
Lachesis ran
candidates --constructor_id memory.copy.capacity --detail full

The dangerous operation surfaces as a candidate

class-matched candidate surfaced in a sibling file, not in pngshim.c

Captured resultCaptured · candidates
raw captured outputcandidates
memory.copy.capacity candidate at sfdriver.c:967 (id obl_sf_967)
sfdriver.cline 967
Code location returned for this evidence step.
The repair

A small line with a large consequence.

Load_SBit_Png stores a PNG's 32-bit width/height into narrower fields used to compute the glyph bitmap size, so dimensions above 0xFFFF truncate and undersize the buffer; libpng then writes the full-size image into it -- a heap OOB write.

Before
// see real_sink_note
After the patch
// 2.10.4 checks the PNG dimensions against the expected/target metrics before allocation and rejects images whose width/height do not fit, closing the truncation.
Sources & further reading

The original record.

Read the historical advisory and vulnerability record behind this reconstruction.

Open the Lachesis quickstart