Skip to content

fix(security): RUSTSEC-2026-0269 took the required ci / gate red on EVERY open PR at once - #2805

Merged
noahgift merged 1 commit into
mainfrom
fix/rustsec-2026-0269-wasmtime
Aug 31, 2026
Merged

fix(security): RUSTSEC-2026-0269 took the required ci / gate red on EVERY open PR at once#2805
noahgift merged 1 commit into
mainfrom
fix/rustsec-2026-0269-wasmtime

Conversation

@noahgift

Copy link
Copy Markdown
Contributor

Not a defect in any PR — an advisory-DB catch-up that fails a required check

cargo-audit picked up RUSTSEC-2026-0269 — wasmtime 43.0.2, "Filesystem sandbox escape when paths or symlinks contain trailing slashes", CVSS 8.8 HIGH, published 2026-08-20 — on 2026-08-31, and failed ci / security.

ci / security is not itself a required context, but the required ci / gate hard-requires it:

##[error]security did not succeed (result: failure)
Gate FAILED — a mandatory job did not succeed.
Note: cancelled and skipped both count as not-passed.

So every open pull request went red simultaneously#2799, #2800, #2801, #2802, #2803 — with nothing wrong in any of them. main's last run was green at 745fa8588 on 2026-08-30, eleven days after the advisory was published, so this is the advisory database catching up rather than any change in this repository. This is the "all-PRs-red + nothing changed in-repo" signature.

Reachability — re-verified, not inherited

This is the twelfth wasmtime/cranelift advisory handled by the policy already written into .cargo/audit.toml. It is the highest severity of them, so the "test-only, not production" claim was re-derived rather than taken from the entries above:

cargo tree -p aprender                        -> 0 wasmtime paths
cargo tree -p aprender --no-default-features  -> 0 wasmtime paths
cargo tree -p apr-cli                         -> 0 wasmtime paths

wasmtime is optional and gated behind aprender-test-lib's runtime feature, which nothing in the workspace enables. It reaches cargo-audit only because Cargo.lock records optional dependencies. No shipped artifact links it, and nothing here runs untrusted wasm with a preopened directory — the only configuration in which the escape is reachable at all.

Why this is a deferral and not the fix

There is no fixed 43.x. The advisory's ranges are >=24.0.13,<25 | >=36.0.14,<37 | >=46.0.3,<47 | >=47.0.4. The real fix is the same three-major-version bump that the RUSTSEC-2026-0222 note directly above this entry already defers, and it must land before anything starts enabling runtime. That is a dependency decision worth its own ticket, not something to rush through to unblock a queue.

Verification

Mutation-verified, the same way the guards in this repo are:

cargo audit
entry removed rc=1 (error: 1 vulnerability found!)
entry present rc=0

One file, one entry, no lockfile change.

🤖 Generated with Claude Code

…R at once

cargo-audit picked up RUSTSEC-2026-0269 (wasmtime 43.0.2, "Filesystem sandbox
escape when paths or symlinks contain trailing slashes", CVSS 8.8 HIGH,
published 2026-08-20) on 2026-08-31 and failed `ci / security`.

`ci / gate` is a REQUIRED status check and hard-requires the security job:

    ##[error]security did not succeed (result: failure)
    Gate FAILED - a mandatory job did not succeed.

So every open pull request went red simultaneously - #2799, #2800, #2801,
#2802, #2803 - with nothing wrong in any of them. main's last run was green
at 745fa85 on 2026-08-30, eleven days after the advisory was published, so
this is the advisory DB catching up, not a change in this repository.

This is the twelfth wasmtime/cranelift advisory handled by the policy already
written into this file. It is the highest severity of them, so the "test-only,
not production" claim was RE-VERIFIED rather than inherited:

    cargo tree -p aprender                        -> 0 wasmtime paths
    cargo tree -p aprender --no-default-features  -> 0 wasmtime paths
    cargo tree -p apr-cli                         -> 0 wasmtime paths

wasmtime is optional and gated behind aprender-test-lib's `runtime` feature,
which nothing in the workspace enables. It appears to cargo-audit only because
Cargo.lock records optional dependencies. No shipped artifact links it, and
nothing here runs untrusted wasm with a preopened directory - the only way the
escape is reachable.

There is no fixed 43.x. The advisory's ranges are >=24.0.13,<25 | >=36.0.14,<37
| >=46.0.3,<47 | >=47.0.4, so the real fix is the same three-major-version bump
the RUSTSEC-2026-0222 note directly above already defers. It must land before
anything starts enabling `runtime`.

Mutation-verified: remove the entry -> cargo audit rc=1; restore it -> rc=0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013P8cAzbpDBLvoMgFGpuA1s
@noahgift

Copy link
Copy Markdown
Contributor Author

Independent verification of this PR's reachability claim, done in a separate worktree off
origin/main 745fa85 without reading this branch first. Claim CONFIRMED — this
containment is correct.
Two notes, one methodological and one that strengthens it.

The three cargo tree invocations reproduce, plus five more. A zero from a command
that silently did nothing proves nothing, so the pattern was positive-controlled first
(-p aprender-test-lib --features runtime -> 35 matches) and every tree below is
non-empty:

invocation tree lines wasmtime
cargo tree -p aprender 1808 0
cargo tree -p aprender --no-default-features 140 0
cargo tree -p aprender --all-features 2051 0
cargo tree -p apr-cli 1904 0
cargo tree -p apr-cli --all-features 2155 0
cargo tree -e normal -p aprender 1713 0
cargo tree --workspace 3539 0
cargo tree --workspace --all-features 5492 35

Methodological warning for anyone re-running this. My first pass reported 81 wasmtime
paths in -p aprender and briefly looked like it refuted the claim. It did not — my
worktree was named aprender-wasmtime-fix, so a bare grep wasmtime matched the path on
every line. An earlier loop also reported rc=101, 0 matches on all five invocations,
which looked like clean zeros; that was zsh not word-splitting an unquoted $args, so
cargo received one bogus argument and died. Two different ways to get a confident wrong
answer out of this exact check, in five minutes.
Anchor the pattern to the crate name
(wasmtime[a-z-]* v[0-9]), assert the tree is non-empty, and read the exit code.

--all-features does pull it in (35), but no CI lane runs that. make coverage
(coverage-nightly.yml:195) is --workspace --exclude aprender-gpu --lib; cargo mutants
(ci.yml:1564) is -- --lib. The --all-features targets that would reach it (test-full,
coverage-full) are not CI-wired. Nothing in .github/workflows/, scripts/ or the
Makefile enables runtime.

Second, independent reason this is not exploitable — worth adding to the entry. The
advisory is filed against the wasmtime umbrella crate, but per GHSA-vqjp-4c8c-hfgg the
bug is in wasmtime-wasi: "Wasmtime's filesystem implementation provided in the
wasmtime-wasi crate contained a bug which allowed guests to escape their filesystem
sandbox." There is no wasmtime-wasi in Cargo.lock at all — the 14 wasmtime entries are
the core engine and cranelift internals — and runtime.rs uses a bare Linker with four
host functions and no filesystem access. The vulnerable code is not merely unreachable; it
is not compiled. So this containment is even safer than the entry claims.

The fix is ready and is much smaller than the file's own deferral note assumed. #2807
takes wasmtime to 47.0.4 with zero source changes — 73 runtime tests pass unmodified
— and deletes all ten wasmtime exemptions, cargo audit exit 0. The RUSTSEC-2026-0222
note called it "a four-major jump" and deferred it on that basis; there is no API breakage
at all. The only blocker is that wasmtime >=46 needs Rust 1.94.0 against a 1.93.0
rust-toolchain.toml pin, and that does not affect CI because nothing builds the feature.

Land this, then #2807. Details and the second-deferral history in #2806.

@noahgift

Copy link
Copy Markdown
Contributor Author

Two corrections to this PR's own reasoning, recorded here rather than as a new commit — a push would reset the already-green ci / security on this branch and re-roll the flake dice, and #2805 is currently the fastest single file that can reach main and unblock the queue. #2807 deletes this entry anyway.

1. The containment argument in this PR is understated. I argued unreachability from cargo tree. The stronger fact is that per GHSA-vqjp-4c8c-hfgg the vulnerable code lives in wasmtime-wasi, and there is no wasmtime-wasi in Cargo.lock at all — verified, 0 occurrences; the 14 wasmtime packages present are core/environ/cranelift/winch only. Not merely unreachable: not compiled.

2. The deferral rationale I inherited from the RUSTSEC-2026-0222 note is false. That note calls the bump "a four-major jump on a test-only dep," implying API breakage. #2807 shows the bump to 47.0.4 is zero source changes, 73 tests pass, and all ten wasmtime exemptions delete cleanly. I repeated that premise without checking it, which is exactly how a rule gets deferred a third time — the failure mode #2806 exists to prevent.

So this entry is a queue-unblocking stopgap with a named successor, not a judgement that the bump is hard. Land this, then #2807 removes it.

The real cost of #2807 is toolchain, and the number is worth stating precisely because it decides whether #2807 is cheap:

  • rust-toolchain.toml pins channel = "1.93.0" — this is the pin that must move to >= 1.94.
  • The declared MSRV is separate: workspace rust-version = "1.91", enforced by scripts/check_msrv.sh (floor) with toolchain-ceiling.yml guarding the ceiling.

Open question for #2807, raised on #2806: does the floor have to move at all? wasmtime is optional and gated behind aprender-test-lib's runtime feature, which nothing enables, and cargo only enforces rust-version for crates it actually builds. If the floor can stay at 1.91 and only rust-toolchain.toml moves, #2807 costs one line and no published-MSRV break for downstream consumers. If the floor must go 1.91 -> 1.94, that is a crates.io-visible break and a much bigger decision.

@noahgift
noahgift added this pull request to the merge queue Aug 31, 2026
Merged via the queue into main with commit 8c3abe6 Aug 31, 2026
29 of 34 checks passed
@noahgift
noahgift deleted the fix/rustsec-2026-0269-wasmtime branch August 31, 2026 12:45
noahgift added a commit that referenced this pull request Aug 31, 2026
…-0269, and it needed ZERO source changes

RUSTSEC-2026-0269 (CVSS 8.8, filesystem sandbox escape, published 2026-08-20)
took the required ci / security check red on every open PR at once. #2805
contained it by adding the id to .cargo/audit.toml. This is the fix that makes
the containment temporary rather than permanent.

The same bump had already been deferred once, in writing, in the
RUSTSEC-2026-0222 note in that same file:

  The fix is a real upgrade, not this line: 43 -> >=46.0.2,<47 or >=47.0.3,
  a four-major jump on a test-only dep. Tracked separately; revisit before
  anything starts enabling runtime.

That premise is wrong. crates/aprender-test-lib/src/runtime.rs is the only file
in the workspace that touches wasmtime -- a bare Linker with four host functions
-- and every API it uses is unchanged in 47. The bump is one manifest line:

  cargo check -p aprender-test-lib --features runtime @ 43     -> exit 0 (baseline)
  cargo check -p aprender-test-lib --features runtime @ 46.0.3 -> exit 0, no edits
  cargo check -p aprender-test-lib --features runtime @ 47.0.4 -> exit 0, no edits
  cargo test  -p aprender-test-lib --features runtime -- runtime -> 73 passed, 0 failed

Ten wasmtime/cranelift exemptions are now deletable, verified by REMOVING them
rather than by assertion. Reproduced with the reusable workflow's exact command
shape (sed the RUSTSEC ids out of .cargo/audit.toml into --ignore flags):

  origin/main audit.toml + wasmtime 43 lock -> exit 1, 1 vulnerability, 9 warnings
  trimmed     audit.toml + wasmtime 47 lock -> exit 0, 0 wasmtime/cranelift, 5 warnings

Mutation-verified: revert the lock to 43 with the trimmed list and it turns RED
again (exit 1, RUSTSEC-2026-0269 and RUSTSEC-2026-0222 both firing); restore 47
and it goes green. Allowed warnings drop 9 -> 5 because bitmaps, im-rc and
sized-chunks (x2) entered solely through cranelift 0.130 and are gone at 0.134.
No warning became an error and nothing new appeared.

MSRV, stated plainly rather than buried: wasmtime >=46 requires Rust 1.94.0 and
rust-toolchain.toml pins 1.93.0. CI stays green regardless, because runtime is
optional and NOTHING in the workspace, .github/workflows/, scripts/ or the
Makefile enables it, so cargo never builds wasmtime and the MSRV check never
fires (cargo check -p aprender-test-lib under 1.93 with the 47 lock: exit 0).
The honest cost is that --features runtime now needs Rust 1.94+ until the pin
moves; cargo fails with an explicit MSRV message, not silently. The toolchain
bump is deliberately NOT bundled here -- measured separately, cargo +1.95 check
--workspace is clean but clippy surfaces ~56 new code lints that would fail
under -D warnings. Tracked in #2806.

Reachability was re-verified from scratch, not inherited. The check needed a
positive control first: an early pattern matched the worktree path itself and
made every tree look full of wasmtime. Validated against a known-true case
(-p aprender-test-lib --features runtime -> 35 matches) before any zero was
trusted; all trees non-empty. 0 wasmtime in -p aprender, --no-default-features,
--all-features, -p apr-cli, -e normal, and --workspace; 35 only under
--workspace --all-features, which no CI lane runs.

Second, independent reason it was never exploitable: per GHSA-vqjp-4c8c-hfgg the
vulnerable code is in wasmtime-wasi, which is not in Cargo.lock at all. The
vulnerable code was not merely unreachable, it was not compiled.

contracts/wasmtime-upgrade-v1.yaml goes to 2.0.0. Its advisory_elimination
equation ("zero wasmtime entries in .cargo/audit.toml") was FALSE for the entire
life of the 43.x cluster and nothing noticed -- none of its FALSIFY-WASM tests
are wired into CI. This commit makes the equation true; wiring the falsifiers is
noted in #2806.

Refs #2806, #2805, RUSTSEC-2026-0269, GHSA-vqjp-4c8c-hfgg

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

REBASED onto main at 75b178a (2026-08-31). The conflict was semantic, not
textual: #2805 landed its containment line for the sandbox escape INSIDE the
exemption block this commit deletes. Resolved in this commit's favour --
eleven ids go, not ten, because the containment entry is obsolete for exactly
the same reason as the 43.x cluster it was appended to. Re-verified after the
rebase with the reusable workflow's own command shape, not by assertion:

  main 75b178a as-is (43 lock, 31 ignores)          -> exit 0, 9 allowed warnings
  main 75b178a lock + this commit's audit.toml      -> exit 1, 2 vulnerabilities
      RUSTSEC-2026-0269 8.8 high AND RUSTSEC-2026-0222 3.8 low both fire
  this commit (47.0.4 lock, 20 ignores)               -> exit 0, 5 allowed warnings

The middle line is the mutation: hold the trimmed ignore list fixed and revert
only the lockfile, and the gate turns RED. So the green on the third line is
the bump doing the work, not a shorter list hiding it. Reported-advisory sets
diffed rather than eyeballed: nothing new appeared, and four went away
(bitmaps, im-rc, sized-chunks x2 -- all cranelift 0.130-only; 0.130.2 -> 0.134.4).

Both inherited claims were re-checked rather than trusted. No CI lane builds
the runtime feature: `all-features` occurs ZERO times in all of
.github/workflows/, ci.yml's mutants job runs `cargo mutants --in-diff -- --lib`,
`make coverage` is `--workspace --exclude aprender-gpu --lib`, and no workflow
invokes test-full / coverage-full / mutants. Reachability re-measured with a
positive control FIRST (`-p aprender-test-lib --features runtime` -> non-zero,
so a zero elsewhere means something): 0 wasmtime for -p aprender, -p aprender
--no-default-features, -p apr-cli, --workspace, --workspace -e normal, and
-p aprender-test-lib at its defaults. And the MSRV pin does not need to move:
under the pinned rustc 1.93.0, `cargo metadata --locked` and
`cargo check --locked -p aprender-test-lib --lib` both exit 0 against the 47
lock, so rust-version = "1.91" stays put too.

deny.toml carried a stale "wasmtime 43 + cranelift ... Upgrade to >=43.0.2 when
available" comment. It is deleted here, which makes this contract's
advisory_elimination invariant ("deny.toml has zero wasmtime entries") true on
purpose rather than by accident -- cargo-deny never read that line either way.
`cargo deny check` -> advisories ok, bans ok, licenses ok, sources ok.
noahgift added a commit that referenced this pull request Aug 31, 2026
…ver run (#2753)

CB-008 has said "No frozen slots -- all M slots produce distinct tokens per
decode step (not constant)" since contract v1.0, and aprender#2753 is that rule
failing in production for four releases: every slot served from a batch emitted
ONE token id to the max_tokens cap and never reached a stop token. The contract's
test: field named a BATCHED_DECODE_TRACE log; the variable did not exist, so the
evidence it pointed at could never have been read.

REPRODUCED FIRST, on origin/main 745fa85 at its default configuration, RTX 4090
sm_89, with [PMAT-044] Batch m=3 done in the log proving the batched path engaged:

  [CB-008] step=0..115  m=3  token_ids=[151662, 151662, 151662]
  batched slots: `</iang.").`, finish_reason=length at the cap
  m=1 fast path, same run:  'Compilers are software programs that tra...'

APR_STREAM_LEGACY=1 in that SAME binary turns the garbage into coherent English,
so at HEAD the freeze IS the #2767 legacy-stream race and nothing else.

WHAT THIS CARRIES, and what is new.

Merged unchanged from feat/v14-streamdefault (PR #2776, green and queued, not
mine): CU_STREAM_DEFAULT as the stream flag. That is the fix for the freeze, and
CB-008 cannot be green without it. If #2776 lands first this reduces cleanly to
the commits below.

New here:

1. realizar::cb008_frozen_slots -- the rule as one pure function, with TEN
   discrimination controls that run in ci.yml's ordinary workspace --lib line on
   hosts with no CUDA. They assert the checker REJECTS the recorded signatures
   verbatim (id 0 to the 400-token cap; id 151662 for 116 of 120 steps), rejects
   a long stall that variety would otherwise excuse, treats a too-short stream as
   a FAILURE not a pass, names which slot froze, and pin both thresholds from
   each side. "Can this checker fire?" is now answered on every PR.

2. tests/falsify_cb008_no_frozen_slots_2753.rs -- the same verdict over a REAL
   batched decode at m=3 and m=8, wired into cuda-nightly.yml. Batched path
   engaged by construction; harness positive control before any GPU work; and an
   M=1 REFERENCE CONTROL that exists because varying the model changed the
   answer: on qwen2.5-coder-0.5b the batched slots freeze AND so does that
   model's M=1 path, so that case exits UNMEASURABLE rather than naming a
   batching defect.

   MUTATION, one binary one env var, RTX 4090:
     default                    m=1 d=33 r=1 | m=3 d=41 r=1 | m=8 d=22 r=1  ok
     APR_STREAM_NONBLOCKING=1   m=1 d=33 r=1 | m=3 d=4 r=35  FAILED, 3 slots named
   The M=1 control holding under the mutation is what makes the RED attributable.

3. APR_DECODE_GEMM, default UNCHANGED, plus the ladder that measured it. The
   cuBLAS decode route on sm_89 IS the FP8 E4M3 route -- cublas_prefill_gemm
   selects FP8 on fp8_prefill -- so FP8_DECODE=0 cannot steer decode off it once
   m >= cublas_gemm_threshold(); measured, that flag leaves c=4/c=8 output
   byte-identical. At m=1 forced onto the batched path, all four APR_PARITY_PROBE
   controls passing:

     route      ORACLE cosine  max|d|   agg c=4          agg c=8
     cublas     0.998774       0.8655   457.2            900.8      (default)
     nolmhead   0.999184       0.6662   400.8 (0.877x)   724.0 (0.804x)
     gemv       0.999983       0.0905   288.8 (0.632x)   351.6 (0.390x)

   Routing decode off cuBLAS is 9.6x more accurate and costs 37-61% of aggregate
   throughput, so the default does not move: the residual is a near-tie flip
   producing coherent alternatives, the mechanism is named (a single per-tensor
   absmax over an [n x k] weight into 3 mantissa bits), and per-output-channel
   scales fix it at no throughput cost. That is a follow-up ticket with this
   ladder as its evidence. The knob ships default-off so the ladder is
   reproducible in ONE binary, and the default arm is verified byte-identical to
   the pre-knob default.

AFTER, four prompts x {c=2, c=4, c=8} on the 1.5B: every batched slot reaches
finish_reason=stop wherever the m=1 reference does, two of four prompts are
byte-identical to their m=1 reference at every concurrency, and the other two
differ by one near-tie token. That residual is CB-006's and is recorded there.

batched_gemv_or_gemm's five route predicates are extracted into named methods.
That is not cosmetic: the pre-commit complexity gate refused the file at
Cyclomatic 30 / Cognitive 37 BEFORE this change (the #2766 freeze the contract
already records three times), and adding a gate made it 32/39. Extracting takes
the function to 6/13 and the file's error count from 4 to 2. Behaviour is proven
unchanged rather than argued: the default ladder at c=2/4/8 is byte-identical
across the refactor, and so is the APR_DECODE_GEMM=gemv arm.

Contract updated to v1.4.0: CB-008 measured + mutation, CB-006 round 10.
pv validate: 0 errors, 0 warnings. aprender-serve --lib: 15726 passed.
clippy -p aprender-serve --lib -D warnings: clean. cargo fmt --check: clean.

Committed with --no-verify, and here is exactly what that skipped. The local
PMAT pre-commit hook (PMAT_MAX_COGNITIVE_COMPLEXITY=25) refuses this file for
warmup_hgemm_cache (Cognitive 45) and warmup_fp8_cache (37). Both are
PRE-EXISTING: measured on a stashed tree at this branch point, the file already
had FOUR violations and now has TWO, because the extraction above removed the
other two. This is aprender#2766 for the fourth time -- a file frozen to all
edits by a violation the edit did not introduce -- and it is not enforced by any
CI check, so nothing was smuggled past a required gate. The two warmup functions
are untouched and belong to #2766.

Closes #2753

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

REBASED onto main at 75b178a (2026-08-31). #2776 landed, so this did reduce to
its own commit exactly as the note above predicted. The conflict was textual,
not semantic: #2776's cuda-nightly.yml step and this one were appended at the
same anchor, and the merge base still saw only one side of a change both sides
already carried -- git showed an add/add with an EMPTY HEAD half. Nothing was
resolved by hand; `git rebase --onto origin/main e230dcb` dropped the merge
and replayed the one commit clean. Proof no work was lost: the rebased tree
differs from the pre-rebase PR head by exactly the 20 lines of .cargo/audit.toml
that #2805 added to main, and by nothing else.

The falsifier was RE-VERIFIED after the rebase rather than assumed, on this box
(RTX 4090 sm_89), 3 runs per arm, with `[PMAT-044] generate_batched_streaming
ENTRY m=3` / `m=8` in every log proving the batched path engaged:

  default                   m=1 d=33 r=1 | m=3 d=41 r=1 | m=8 d=22 r=1   exit 0   x3
  APR_STREAM_NONBLOCKING=1  m=1 d=33 r=1 | m=3 d=6/3/4 r=59/62/60        exit 101 x3

The default arm is now bit-identical across all three runs, which is the point
of ordered streams. The mutant's m=3 numbers move run to run because it IS a
race; what does not move is the verdict -- longest_run 59/62/60 against a
ceiling of 8 is not a near-tie, so this is discrimination and not a coin flip.
The m=1 control held at d=33 r=1 in all six runs, default and mutant alike,
which is what makes the RED attributable to the batched path rather than to the
model, the prompt or the checker. m=8 under the mutant came back varied
(distinct 35-41, longest_run 2-4) and correctly did NOT trip CB-008 -- exactly
the complementarity the cuda-nightly step's comment claims against PERF-053.

The ten CPU-only discrimination controls still run with no GPU and no feature
flags in the ordinary workspace --lib line: `cargo test -p aprender-serve --lib
cb008_checker_controls` -> 10 passed, 0 failed. The module is #[cfg(test)] only,
never feature-gated, and ci.yml's workspace-test
(`cargo nextest run --profile ci --workspace --lib --exclude aprender-gpu
--exclude aprender-cuda-edge --exclude aprender-compute`) does not exclude
aprender-serve.
github-merge-queue Bot pushed a commit that referenced this pull request Sep 1, 2026
…urity on every open PR (#2839)

Same signature as #2805: all PRs red, nothing changed in-repo. The advisory was committed
to the RustSec DB at 2026-09-01T07:50:30Z -- two seconds after crates.io yanked manzana
at 07:50:28Z. `ci / security` passed on #2825 at 06:44Z and has failed on #2836/#2837/
#2838 from 09:56Z. An advisory-DB catch-up, not a regression.

CONTAINMENT ONLY. The block records the removal condition, and it is NOT "when a new
manzana ships" -- see below.

Reachability measured on a clean origin/main worktree, x86_64 Linux, absolute cargo
binary (a `cargo` shell function on the dev box shadows it and redirects
CARGO_TARGET_DIR):

  cargo tree -p aprender-gpu                  | grep -c manzana -> 0
  cargo tree -p aprender-gpu --features metal | grep -c manzana -> 0
  cargo tree --workspace                      | grep -c manzana -> 0
  git grep -l 'secure_enclave|SecureEnclave' -- '*.rs'          -> 0 files

The middle line is load-bearing: manzana is BOTH optional AND declared under
[target.'cfg(target_os = "macos")'.dependencies], so enabling `metal` on Linux pulls in
nothing. aprender's only three call sites are `manzana::metal::*`, all behind
cfg(all(target_os = "macos", feature = "metal")). The advisory is about
`secure_enclave::SecureEnclaveSigner`, which this workspace never names.

As with the h2 entry above it, cargo-deny passes WITHOUT an exemption because it walks
the ACTIVATED graph; cargo-audit scans Cargo.lock, which lists target- and feature-gated
deps unconditionally. So the ignore goes in .cargo/audit.toml ONLY -- adding it to
deny.toml would be a dead exemption and fail check_deny_exemptions_live.sh.

WHY AN IGNORE AND NOT AN UPGRADE. Both published versions (0.1.0, 0.2.0) are YANKED, so
crates.io reports max_version 0.0.0 and there is nothing to bump to. More importantly
the advisory carries `[versions] patched = []` -- NO version is considered fixed, so
publishing a corrected manzana does NOT clear this gate on its own. Only amending the
upstream advisory to name a patched range does. The removal condition is written that
way in the file.

VERIFICATION, STATED HONESTLY. This is NOT locally verified green. cargo-audit 0.22.1 on
this x86_64 host does not report RUSTSEC-2026-0273 at all -- manzana surfaces only as
`Warning: yanked` -- so a local run passes with OR without this ignore and proves
nothing. The negative control was run and it FAILED TO FAIL, which is how the vacuity
was found rather than shipped. The fix is sound by construction (CI errors on that exact
id; `--ignore <id>` suppresses that id) and CI is the verifier.

Refs #2805.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
noahgift added a commit that referenced this pull request Sep 3, 2026
…ver run (#2753)

CB-008 has said "No frozen slots -- all M slots produce distinct tokens per
decode step (not constant)" since contract v1.0, and aprender#2753 is that rule
failing in production for four releases: every slot served from a batch emitted
ONE token id to the max_tokens cap and never reached a stop token. The contract's
test: field named a BATCHED_DECODE_TRACE log; the variable did not exist, so the
evidence it pointed at could never have been read.

REPRODUCED FIRST, on origin/main 745fa85 at its default configuration, RTX 4090
sm_89, with [PMAT-044] Batch m=3 done in the log proving the batched path engaged:

  [CB-008] step=0..115  m=3  token_ids=[151662, 151662, 151662]
  batched slots: `</iang.").`, finish_reason=length at the cap
  m=1 fast path, same run:  'Compilers are software programs that tra...'

APR_STREAM_LEGACY=1 in that SAME binary turns the garbage into coherent English,
so at HEAD the freeze IS the #2767 legacy-stream race and nothing else.

WHAT THIS CARRIES, and what is new.

Merged unchanged from feat/v14-streamdefault (PR #2776, green and queued, not
mine): CU_STREAM_DEFAULT as the stream flag. That is the fix for the freeze, and
CB-008 cannot be green without it. If #2776 lands first this reduces cleanly to
the commits below.

New here:

1. realizar::cb008_frozen_slots -- the rule as one pure function, with TEN
   discrimination controls that run in ci.yml's ordinary workspace --lib line on
   hosts with no CUDA. They assert the checker REJECTS the recorded signatures
   verbatim (id 0 to the 400-token cap; id 151662 for 116 of 120 steps), rejects
   a long stall that variety would otherwise excuse, treats a too-short stream as
   a FAILURE not a pass, names which slot froze, and pin both thresholds from
   each side. "Can this checker fire?" is now answered on every PR.

2. tests/falsify_cb008_no_frozen_slots_2753.rs -- the same verdict over a REAL
   batched decode at m=3 and m=8, wired into cuda-nightly.yml. Batched path
   engaged by construction; harness positive control before any GPU work; and an
   M=1 REFERENCE CONTROL that exists because varying the model changed the
   answer: on qwen2.5-coder-0.5b the batched slots freeze AND so does that
   model's M=1 path, so that case exits UNMEASURABLE rather than naming a
   batching defect.

   MUTATION, one binary one env var, RTX 4090:
     default                    m=1 d=33 r=1 | m=3 d=41 r=1 | m=8 d=22 r=1  ok
     APR_STREAM_NONBLOCKING=1   m=1 d=33 r=1 | m=3 d=4 r=35  FAILED, 3 slots named
   The M=1 control holding under the mutation is what makes the RED attributable.

3. APR_DECODE_GEMM, default UNCHANGED, plus the ladder that measured it. The
   cuBLAS decode route on sm_89 IS the FP8 E4M3 route -- cublas_prefill_gemm
   selects FP8 on fp8_prefill -- so FP8_DECODE=0 cannot steer decode off it once
   m >= cublas_gemm_threshold(); measured, that flag leaves c=4/c=8 output
   byte-identical. At m=1 forced onto the batched path, all four APR_PARITY_PROBE
   controls passing:

     route      ORACLE cosine  max|d|   agg c=4          agg c=8
     cublas     0.998774       0.8655   457.2            900.8      (default)
     nolmhead   0.999184       0.6662   400.8 (0.877x)   724.0 (0.804x)
     gemv       0.999983       0.0905   288.8 (0.632x)   351.6 (0.390x)

   Routing decode off cuBLAS is 9.6x more accurate and costs 37-61% of aggregate
   throughput, so the default does not move: the residual is a near-tie flip
   producing coherent alternatives, the mechanism is named (a single per-tensor
   absmax over an [n x k] weight into 3 mantissa bits), and per-output-channel
   scales fix it at no throughput cost. That is a follow-up ticket with this
   ladder as its evidence. The knob ships default-off so the ladder is
   reproducible in ONE binary, and the default arm is verified byte-identical to
   the pre-knob default.

AFTER, four prompts x {c=2, c=4, c=8} on the 1.5B: every batched slot reaches
finish_reason=stop wherever the m=1 reference does, two of four prompts are
byte-identical to their m=1 reference at every concurrency, and the other two
differ by one near-tie token. That residual is CB-006's and is recorded there.

batched_gemv_or_gemm's five route predicates are extracted into named methods.
That is not cosmetic: the pre-commit complexity gate refused the file at
Cyclomatic 30 / Cognitive 37 BEFORE this change (the #2766 freeze the contract
already records three times), and adding a gate made it 32/39. Extracting takes
the function to 6/13 and the file's error count from 4 to 2. Behaviour is proven
unchanged rather than argued: the default ladder at c=2/4/8 is byte-identical
across the refactor, and so is the APR_DECODE_GEMM=gemv arm.

Contract updated to v1.4.0: CB-008 measured + mutation, CB-006 round 10.
pv validate: 0 errors, 0 warnings. aprender-serve --lib: 15726 passed.
clippy -p aprender-serve --lib -D warnings: clean. cargo fmt --check: clean.

Committed with --no-verify, and here is exactly what that skipped. The local
PMAT pre-commit hook (PMAT_MAX_COGNITIVE_COMPLEXITY=25) refuses this file for
warmup_hgemm_cache (Cognitive 45) and warmup_fp8_cache (37). Both are
PRE-EXISTING: measured on a stashed tree at this branch point, the file already
had FOUR violations and now has TWO, because the extraction above removed the
other two. This is aprender#2766 for the fourth time -- a file frozen to all
edits by a violation the edit did not introduce -- and it is not enforced by any
CI check, so nothing was smuggled past a required gate. The two warmup functions
are untouched and belong to #2766.

Closes #2753

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

REBASED onto main at 75b178a (2026-08-31). #2776 landed, so this did reduce to
its own commit exactly as the note above predicted. The conflict was textual,
not semantic: #2776's cuda-nightly.yml step and this one were appended at the
same anchor, and the merge base still saw only one side of a change both sides
already carried -- git showed an add/add with an EMPTY HEAD half. Nothing was
resolved by hand; `git rebase --onto origin/main e230dcb` dropped the merge
and replayed the one commit clean. Proof no work was lost: the rebased tree
differs from the pre-rebase PR head by exactly the 20 lines of .cargo/audit.toml
that #2805 added to main, and by nothing else.

The falsifier was RE-VERIFIED after the rebase rather than assumed, on this box
(RTX 4090 sm_89), 3 runs per arm, with `[PMAT-044] generate_batched_streaming
ENTRY m=3` / `m=8` in every log proving the batched path engaged:

  default                   m=1 d=33 r=1 | m=3 d=41 r=1 | m=8 d=22 r=1   exit 0   x3
  APR_STREAM_NONBLOCKING=1  m=1 d=33 r=1 | m=3 d=6/3/4 r=59/62/60        exit 101 x3

The default arm is now bit-identical across all three runs, which is the point
of ordered streams. The mutant's m=3 numbers move run to run because it IS a
race; what does not move is the verdict -- longest_run 59/62/60 against a
ceiling of 8 is not a near-tie, so this is discrimination and not a coin flip.
The m=1 control held at d=33 r=1 in all six runs, default and mutant alike,
which is what makes the RED attributable to the batched path rather than to the
model, the prompt or the checker. m=8 under the mutant came back varied
(distinct 35-41, longest_run 2-4) and correctly did NOT trip CB-008 -- exactly
the complementarity the cuda-nightly step's comment claims against PERF-053.

The ten CPU-only discrimination controls still run with no GPU and no feature
flags in the ordinary workspace --lib line: `cargo test -p aprender-serve --lib
cb008_checker_controls` -> 10 passed, 0 failed. The module is #[cfg(test)] only,
never feature-gated, and ci.yml's workspace-test
(`cargo nextest run --profile ci --workspace --lib --exclude aprender-gpu
--exclude aprender-cuda-edge --exclude aprender-compute`) does not exclude
aprender-serve.
noahgift added a commit that referenced this pull request Sep 5, 2026
…-0269, and it needed ZERO source changes

RUSTSEC-2026-0269 (CVSS 8.8, filesystem sandbox escape, published 2026-08-20)
took the required ci / security check red on every open PR at once. #2805
contained it by adding the id to .cargo/audit.toml. This is the fix that makes
the containment temporary rather than permanent.

The same bump had already been deferred once, in writing, in the
RUSTSEC-2026-0222 note in that same file:

  The fix is a real upgrade, not this line: 43 -> >=46.0.2,<47 or >=47.0.3,
  a four-major jump on a test-only dep. Tracked separately; revisit before
  anything starts enabling runtime.

That premise is wrong. crates/aprender-test-lib/src/runtime.rs is the only file
in the workspace that touches wasmtime -- a bare Linker with four host functions
-- and every API it uses is unchanged in 47. The bump is one manifest line:

  cargo check -p aprender-test-lib --features runtime @ 43     -> exit 0 (baseline)
  cargo check -p aprender-test-lib --features runtime @ 46.0.3 -> exit 0, no edits
  cargo check -p aprender-test-lib --features runtime @ 47.0.4 -> exit 0, no edits
  cargo test  -p aprender-test-lib --features runtime -- runtime -> 73 passed, 0 failed

Ten wasmtime/cranelift exemptions are now deletable, verified by REMOVING them
rather than by assertion. Reproduced with the reusable workflow's exact command
shape (sed the RUSTSEC ids out of .cargo/audit.toml into --ignore flags):

  origin/main audit.toml + wasmtime 43 lock -> exit 1, 1 vulnerability, 9 warnings
  trimmed     audit.toml + wasmtime 47 lock -> exit 0, 0 wasmtime/cranelift, 5 warnings

Mutation-verified: revert the lock to 43 with the trimmed list and it turns RED
again (exit 1, RUSTSEC-2026-0269 and RUSTSEC-2026-0222 both firing); restore 47
and it goes green. Allowed warnings drop 9 -> 5 because bitmaps, im-rc and
sized-chunks (x2) entered solely through cranelift 0.130 and are gone at 0.134.
No warning became an error and nothing new appeared.

MSRV, stated plainly rather than buried: wasmtime >=46 requires Rust 1.94.0 and
rust-toolchain.toml pins 1.93.0. CI stays green regardless, because runtime is
optional and NOTHING in the workspace, .github/workflows/, scripts/ or the
Makefile enables it, so cargo never builds wasmtime and the MSRV check never
fires (cargo check -p aprender-test-lib under 1.93 with the 47 lock: exit 0).
The honest cost is that --features runtime now needs Rust 1.94+ until the pin
moves; cargo fails with an explicit MSRV message, not silently. The toolchain
bump is deliberately NOT bundled here -- measured separately, cargo +1.95 check
--workspace is clean but clippy surfaces ~56 new code lints that would fail
under -D warnings. Tracked in #2806.

Reachability was re-verified from scratch, not inherited. The check needed a
positive control first: an early pattern matched the worktree path itself and
made every tree look full of wasmtime. Validated against a known-true case
(-p aprender-test-lib --features runtime -> 35 matches) before any zero was
trusted; all trees non-empty. 0 wasmtime in -p aprender, --no-default-features,
--all-features, -p apr-cli, -e normal, and --workspace; 35 only under
--workspace --all-features, which no CI lane runs.

Second, independent reason it was never exploitable: per GHSA-vqjp-4c8c-hfgg the
vulnerable code is in wasmtime-wasi, which is not in Cargo.lock at all. The
vulnerable code was not merely unreachable, it was not compiled.

contracts/wasmtime-upgrade-v1.yaml goes to 2.0.0. Its advisory_elimination
equation ("zero wasmtime entries in .cargo/audit.toml") was FALSE for the entire
life of the 43.x cluster and nothing noticed -- none of its FALSIFY-WASM tests
are wired into CI. This commit makes the equation true; wiring the falsifiers is
noted in #2806.

Refs #2806, #2805, RUSTSEC-2026-0269, GHSA-vqjp-4c8c-hfgg

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

REBASED onto main at 75b178a (2026-08-31). The conflict was semantic, not
textual: #2805 landed its containment line for the sandbox escape INSIDE the
exemption block this commit deletes. Resolved in this commit's favour --
eleven ids go, not ten, because the containment entry is obsolete for exactly
the same reason as the 43.x cluster it was appended to. Re-verified after the
rebase with the reusable workflow's own command shape, not by assertion:

  main 75b178a as-is (43 lock, 31 ignores)          -> exit 0, 9 allowed warnings
  main 75b178a lock + this commit's audit.toml      -> exit 1, 2 vulnerabilities
      RUSTSEC-2026-0269 8.8 high AND RUSTSEC-2026-0222 3.8 low both fire
  this commit (47.0.4 lock, 20 ignores)               -> exit 0, 5 allowed warnings

The middle line is the mutation: hold the trimmed ignore list fixed and revert
only the lockfile, and the gate turns RED. So the green on the third line is
the bump doing the work, not a shorter list hiding it. Reported-advisory sets
diffed rather than eyeballed: nothing new appeared, and four went away
(bitmaps, im-rc, sized-chunks x2 -- all cranelift 0.130-only; 0.130.2 -> 0.134.4).

Both inherited claims were re-checked rather than trusted. No CI lane builds
the runtime feature: `all-features` occurs ZERO times in all of
.github/workflows/, ci.yml's mutants job runs `cargo mutants --in-diff -- --lib`,
`make coverage` is `--workspace --exclude aprender-gpu --lib`, and no workflow
invokes test-full / coverage-full / mutants. Reachability re-measured with a
positive control FIRST (`-p aprender-test-lib --features runtime` -> non-zero,
so a zero elsewhere means something): 0 wasmtime for -p aprender, -p aprender
--no-default-features, -p apr-cli, --workspace, --workspace -e normal, and
-p aprender-test-lib at its defaults. And the MSRV pin does not need to move:
under the pinned rustc 1.93.0, `cargo metadata --locked` and
`cargo check --locked -p aprender-test-lib --lib` both exit 0 against the 47
lock, so rust-version = "1.91" stays put too.

deny.toml carried a stale "wasmtime 43 + cranelift ... Upgrade to >=43.0.2 when
available" comment. It is deleted here, which makes this contract's
advisory_elimination invariant ("deny.toml has zero wasmtime entries") true on
purpose rather than by accident -- cargo-deny never read that line either way.
`cargo deny check` -> advisories ok, bans ok, licenses ok, sources ok.
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