Corrective release. v0.2.0 remains published and tagged — this release supersedes it rather than replacing it (found → preserved → corrected → tested → superseded).
- C lineage undefined behavior on admitted input (
c/src/canonical_json.c,write_number): the integer fast-path evaluated(double)(long long)dbefore itsfabs(d) < 1e15range guard. For integer-valued fraction tokens ≥ 2⁶³ inside the supported domain (e.g.100000000000000000000.0, |v| < 10²¹), the double→long longconversion is undefined behavior (C11 §6.3.1.4p1), caught by UBSan (runtime error: 1e+20 is outside the range of representable values of type 'long long int'). On tested x86-64 builds the conversion saturated and the code fell through to the correct big-integer branch, so all seven lineages agreed on the affected inputs — no known wrong hash was ever produced — but undefined behavior is not a cross-platform contract. The guard now runs before any conversion (fabs(d) < 1e15 && trunc(d) == d), the same order the C++ lineage already used.
- Regression vector
huge_integer_valued_float({"x":100000000000000000000.0}) pinned in the conformance corpus; all seven lineages agree (25 accept + 31 reject vectors green). - Sanitizer CI gate for the two memory-unsafe lineages (C, C++):
ASan + UBSan + float-cast-overflow with
-fno-sanitize-recover=all, running each lineage's tests and the full corpus through the instrumented CLI (conformance/run_corpus_cli.py).
- C string buffer: allocation failures now latch an OOM flag and
baion_canonicalize_jsonreturns NULL (defined failure — never a partial canonical string, never a write through a failed allocation);reallocno longer assigned directly to the live pointer; capacity growth guarded againstsize_toverflow; the object-key sort array allocation is checked. - Supply chain: workflow declares least-privilege
permissions: contents: read; every job has a timeout; cJSON is fetched by commit SHA (acc76239…, the commit behind v1.7.18) instead of a movable tag; CI toolchains pinned to exact versions (Rust 1.94.0, DMD 2.112.0, GHC 9.6.7, cabal 3.16.1.0, pinned opam package versions);rust/Cargo.lockandhaskell/cabal.project.freezenow committed.
- No enforced caps on input size, nesting depth, or member counts. Resource limits change which documents are rejected, so they are a cross-lineage contract change — all seven lineages must adopt identical limits in the same release. Planned for a future minor version; until then, treat the CLIs as trusted-input tools.
- The C number writer assumes the process stays in the default
Clocale (documented in-source).
Cross-lineage hardening release: uniform rejection contract (invalid UTF-8, raw controls, malformed escapes/tokens, duplicate keys, U+0000, BOM, number domain), corpus-as-data conformance, seeded fuzz agreement layer, ES-262 shortest-digits fix. Breaking: previously-accepted lax inputs are now uniformly rejected. See the GitHub release notes.
Uniform rejection of duplicate object keys across all seven lineages.
Canonicalization contract round: uniform U+0000 rejection, Go number
normalization, build_all.sh, verifier reject-vectors (external review
round 2).
Initial public release: seven-lineage canonical JSON + SHA-256, byte-identity verified.