The unresolved crashes from Session 37 now have a reproducible explanation. No new payload or plaintext was recovered. OutGuess interprets identical header bits as a long message, then its reading procedure can reach an impossible state. A fresh image containing no deliberately embedded message reproduces the same failure.
We built a diagnostic copy of OutGuess 0.4, located the division by zero, added checks that stop invalid reads, verified six constructed recovery cases, and reran all 75 archived files. All previously successful outputs remained byte-identical.
What failed
The diagnostic build identifies src/iterator.c:56, where OutGuess takes a random integer modulo skipmod. The adaptive reading routine can reduce skipmod to zero, causing SIGFPE. The original decoder does not guard this operation.
For archive 18.jpg, LP2 page 1, the trace is:
| Measurement | Value |
|---|---|
| Usable carrier bits | 687,226 |
| Four raw header bytes | FF FF FF FF |
| Interpreted seed | 41,408 |
| Interpreted payload length | 58,152 bytes |
| Bytes already traversed at failure | 58,120 |
| Bytes still requested | 32 |
| Carrier bits remaining | 253 |
| Bits needed to finish | 256 |
There are three fewer bits left than required, even if every remaining bit were read. Continuing would require changing the decoding rule or fabricating data. Our guard stops with an explicit invalid-state error and writes no partial payload.
All 41 formerly crashing files reached this same zero-step condition. Each was short by between one and six carrier bits at the recorded point. This explains their crashes; it does not authenticate the preceding traversed bytes as message content.
The upstream source, fixed-version source manifest, and patch make the diagnosis inspectable. The patch also checks that a requested bit lies inside the usable carrier before reading it. It does not clamp the zero step to one, change the key, or alter the successful decoding path.
Why the repeated message length is not a clue by itself
All 58 LP2 files yield the same four raw header bytes, FF FF FF FF, at the positions selected by this setting. Processing these bytes with OutGuess's built-in Default key produces the same seed and length. Calling OutGuess without -k uses that literal default key; it is not the same operation as supplying an empty key.
We generated a deterministic JPEG with a white upper quarter and seeded image noise below, with no embedding operation. It produced the same raw header, seed, claimed length and original division-by-zero failure. The guarded build stopped explicitly instead.
That control establishes that this header and crash can arise from ordinary image data. They do not, on their own, demonstrate an intentionally hidden message or special page grouping. It does not prove that every possible interpretation of the LP2 images lacks hidden data.
Validation and complete rerun
The recovery checks were:
- Original and guarded builds each recovered both existing 101-byte planted OutGuess messages, using the default key and the named control key: four exact recoveries.
- Two new messages embedded by the guarded build were recovered exactly by the original build: two further recoveries.
- All 29 archived-file outputs that previously exited successfully matched their Session 37 hashes and remained identical between the original and guarded builds. This includes all nine readable historical-message outputs.
| Guarded outcome | LP1 | LP2 | Total |
|---|---|---|---|
| Output produced, unchanged from Session 37 | 13 | 16 | 29 |
| Existing length-check rejection | 2 | 3 | 5 |
| Explicit invalid-iterator rejection replacing a crash | 2 | 39 | 41 |
| Crashes | 0 | 0 | 0 |
The 16 LP2 outputs remain unvalidated binary data. A successful exit is still not evidence of an embedded payload. No new output was produced by resolving the crashes, and no source JPEG was changed.
The unmodified diagnostic build uses a division sanitizer, so it reports exit status 1 and a precise diagnostic where the installed binary previously terminated with signal 8. Those statuses reflect the diagnostic build, not a change in the underlying failure.
What this closes, and what remains open
Session 37's unexplained crashes are now explained and reproducibly handled for the default-key, no-error-correction OutGuess check. The result is a bounded extraction failure, not a general ruling out of steganography. Other keys, error-correction mode, other tools and other embedding schemes remain outside this test.
We are not claiming this software defect is previously unknown to other researchers. The progress here is resolving a concrete limitation in our own investigation. There is no evidence from this test that a crash was an intentional Cicada clue.
The next cipher experiment should return to the repeated six-rune passage and use its two surrounding contexts to reject incompatible rules before conducting another large search.
Reproduction files
- Audit and build script
- Full diagnostic, control and rerun results
- Counts and per-file bit shortfalls
- Guard and tracing patch
- Unembedded control JPEG
- Source URL and SHA-256, upstream license
The script takes the extracted OutGuess 0.4 source directory, the 75-image directory, Session 37's local results directory and a new output directory. It requires GCC, make and Pillow. It builds an unchanged diagnostic executable and a guarded executable, checks constructed messages before the archive scan, compares successful output hashes, and records errors without executing extracted bytes. The synthetic cover and planted messages are available from Session 37.
This product includes software developed by Niels Provos. The local guard is an investigation aid, not an upstream release or a comprehensive security audit of OutGuess.