Skip to content

feat(s141): complete slice 141 (the headless release's floor, OTP 28.5.0.8 on the OTP 28 line; the decoder asked directly; the container images on 28.5.0.8) - #136

Merged
HackTuah merged 2 commits into
mainfrom
slice/141-box-floor
Oct 20, 2026
Merged

HackTuah merged 2 commits into
mainfrom
slice/141-box-floor

Conversation

@HackTuah

Copy link
Copy Markdown
Member

Slice 141. The headless release, the one deployed as a server, now refuses to boot below OTP 28.5.0.8 and on any OTP line but 28. Every node also asks its decoder directly whether it refuses the oversize tuple CVE-2026-55737 is about. The container images build OTP 28.5.0.8. The desktop keeps the slice 137 floor.

What it does

  • The headless floor (Trinity.Toolchain.floor(:headless)).
    • OTP 28.5.0.8 fixes CVE-2026-94200 and CVE-2026-105236 in ssl, CVE-2026-42789 in public_key and CVE-2026-48856 in inets.
    • Those fixes are in the applications, and erlang.org says erts-16.4.0.7 "can be applied independently of other applications on a full OTP 28 installation". So, as in slice 137, erts and every loaded application are compared with the release's row of otp_versions.table. The row is checked in verbatim, from the same erlang/otp commit as slice 137's three, whose file SHA-256 is unchanged.
    • A node is held to it when its release name is headless or it runs with TRINITY_MODE=headless. Either is enough, so leaving one unset does not lower the floor.
    • Any line other than OTP 28 is refused, above as well as below.
  • The decoder, asked directly (Trinity.Toolchain.check_decoder/1), in every profile.
    • It decodes a LARGE_TUPLE_EXT of arity 2^24, one over erts's ERTS_MAX_TUPLE_SIZE. That is the bound the CVE-2026-55737 fix added to decoded_size() in external.c.
    • A control of the same encoding at arity 256 must still decode, so a decoder that refuses everything is not taken for a fixed one. A probe that fails any other way is a refusal too.
    • It runs in a process of its own, so the 16 MiB probe, and the 128 MiB tuple an unfixed runtime builds from it, are not left on the booting process.
    • Measured here: OTP 28.1.1 builds the tuple in 82 ms; 28.5.0.5 to 28.5.0.8 and 29.1 refuse it in about 20 microseconds.
  • The container images move to 28.5.0.8: ci/container.tool-versions, the Iron Bank manifest and Dockerfile, and the FIPS Containerfile. The source archive's SHA-256, 8a696e15bcbe5d57197b1f4c03c92d415f9fd9af70f9222a05911c8a5463cba6, was read from the release API and SHA256.txt and matched a download. The pcre sources the image's justifications rest on are byte for byte the same as 28.5.0.7's.
  • ADR-0014 gains a proposed amendment: the floor follows the release, not the pin. It needs the owner's acceptance. It also says what the desktop, still on 28.5.0.6, reaches of the four CVEs.

Evidence

  • Red first. On the tree before the change, a headless node booted on 28.5.0.6 and on 28.5.0.7.

  • Both branches. A headless boot on the suite's own runtime has its expectation read from that runtime's OTP_VERSION file, not from the code under test. It refuses on 28.5.0.6 and boots on 28.5.0.8, and each run prints which.

  • On OTP 28.5.0.7 a headless node refuses, naming erts and ssl, and a desktop node on the same runtime boots.

  • On OTP 28.1.1 (and 28.5.0.3 in CI) the probe itself answers {:decoder_accepts_oversize_tuple, 16777216}.

  • Planted faults. Each was restored and compared byte for byte afterwards, and each turned its own tests red:

    1. the release ignored;
    2. the release name alone ignored;
    3. other lines admitted;
    4. the probe skipped at boot;
    5. the probe's control skipped;
    6. the old row used;
    7. erts compared alone.

    Skipping the probe at boot was observable only through a fault seam, config :trinity, :decoder_probe_fault, true, which can only turn a pass into a refusal, as the ML-DSA-87 preflight's seam does.

CI

A new gate step boots on Hex's prebuilt OTP 28.5.0.7, pinned by its builds.txt digest. The existing 28.5.0.3 step now also runs the probe on that runtime. The FIPS image is rebuilt for the new pin, and the fips job runs the headless boot on 28.5.0.8.

Not in this PR

  • The desktop pin, which still moves only by the canary.
  • Checking the floor under bin/headless eval, which never starts the application. The security model and the amendment both say so.

…5.0.8 on the OTP 28 line; the decoder asked directly; the container images on 28.5.0.8)

Signed-off-by: Ayla Croft <aylacroft@proton.me>
…age pins, not the newest

Signed-off-by: Ayla Croft <aylacroft@proton.me>
@HackTuah
HackTuah merged commit bbbbeb5 into main Oct 20, 2026
13 checks passed
@HackTuah
HackTuah deleted the slice/141-box-floor branch October 20, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant