The archive/CVE-2022-3602
OpenSSLDisclosed 2022

CVE-2022-3602:OpenSSL: Off-by-one stack buffer overflow in X.509 name-constraint pu

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

Historical recordopenssl-3.0.0 (last-vulnerable; range 3.0.0-3.0.6)
WeaknessCWE-787 → CWE-193 · off-by-one stack buffer overflow in X.509 name-constraint pu
ImpactA off-by-one stack buffer overflow in x.509 name-constraint pu in OpenSSL, reachable from attacker-controlled input.
Read the reconstruction
01What happened

With no family and no line seeded, the whole-taxonomy enumerator put 3 of its memory.copy.capacity leads inside crypto/punycode.c (lines 187/276/297) -- the.

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-2022-3602
Vulnerable target
openssl-3.0.0 (last-vulnerable; range 3.0.0-3.0.6) · vulnerable
Prior knowledge
CVE-2022-3602 · openssl-3.0.0 (last-vulnerable; range 3.0.0-3.0.6) is vulnerable
then
02Lachesis run
Verified captured run

Independent re-detection

The experiment graph was built around the last-vulnerable release (openssl-3.0.0), scoped to crypto/punycode.c plus include/crypto and include/internal so the single vulnerable translation unit builds under the pass budget (build 1s, enrich 1811 nodes / 1952 edges), then enumerated BLIND over the whole constructor registry (no family hard-coded, no target line seeded). The census and candidate ids below are the enumerator's own verbatim output; the graph was released immediately after capture, so the provenance body-reads are not re-quoted here. Prose in `adjudication`/`trace` is MY reading of the public advisory + fix, clearly separated from tool output.

Seeded inputs
None
Run timestamp
2026-09-03T00:00:00Z
Evidence artifact
cve-2022-3602.kuzu (built from OpenSSL openssl-3.0.0, scoped to the punycode translation unit; 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-2022-3602
With no family and no line seeded, the whole-taxonomy enumerator put 3 of its memory.copy.capacity leads inside.
01Source// crafted punycode label length from a certificate GENERAL_NAME / email name constraint
02Sink// ossl_punycode_decode / ossl_a2ulabel: decoded label element written to the fixed buffer
03Missing guard// decoded label element written to the output buffer with an off-by-one capacity bound
$ lachesis sources_of → reaches → guard_dominance
OpenSSL · openssl-3.0.0 (last-vulnerable; range 3.0.0-3.0.6) · 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.

RepositoryOpenSSL
Revisionopenssl-3.0.0 (last-vulnerable; range 3.0.0-3.0.6) · vulnerable
EngineLachesis

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

01 · lachesis01 / 03
Lachesis ran
lachesis build (scoped crypto/punycode.c + include/crypto + include/internal) && enrich

Investigate the candidate

The narrowed scope builds the single vulnerable TU in 1s under the pass cap (the prior all-of-include/ scope tripped a manifest/snapshot node mismatch; the two-header-dir scope is clean).

Captured resultCaptured · lachesis
raw captured outputlachesis
build rc=0 1s
enrich rc=0 :: pass2 published 1811 nodes and 1952 edges
crypto/punycode.cline 0
Code location returned for this evidence step.
The repair

A small line with a large consequence.

the index-vs-capacity comparison guarding the label write is off by one, so exactly one element is written past the fixed buffer for a crafted label length.

Before
// decoded label element written to the output buffer with an off-by-one capacity bound
After the patch
// 3.0.7 corrects the bound so the output index is checked against the true remaining capacity before every write, and fixes ossl_a2ulabel's size accounting.
Sources & further reading

The original record.

Read the historical advisory and vulnerability record behind this reconstruction.

Open the Lachesis quickstart