Skip to content

Fix: upgrade wasmtime past RUSTSEC-2026-0316 and RUSTSEC-2026-0314 - #538

Open
0xLeif wants to merge 6 commits into
mainfrom
0xleif/fix/wasmtime-rustsec-2026-0316
Open

0xLeif wants to merge 6 commits into
mainfrom
0xleif/fix/wasmtime-rustsec-2026-0316

Conversation

@0xLeif

@0xLeif 0xLeif commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • CI's audit job on main fails on two advisories against the wasmtime that sandboxes WASM plugins: RUSTSEC-2026-0316 (wasmtime 46.0.3, GHSA-jqpg-j7w6-42pr, dynamic record lifting can allocate beyond the hostcall fuel limit) and RUSTSEC-2026-0314 (wasmtime-wasi 46.0.3, GHSA-j2g9-4prp-pf6h, a guest can panic the host through a filesystem datetime overflow).
  • The job audits a fresh cargo generate-lockfile, so the Cargo.toml requirement has to move. wasmtime and wasmtime-wasi go from 46.0.1 to 49.0.1, the newest patched line. A targeted cargo update -p wasmtime -p wasmtime-wasi moves only the wasmtime tree: wiggle, pulley and every wasmtime-internal-* to 49.0.1, cranelift to 0.136.1, wasm-tools to 0.258, cap-std replaced by cap-primitives 4, and toml 1.1 inside wasmtime-internal-cache only.
  • Two wasmtime-wasi 49 API changes reach build_wasi_p1. Both are ported so the sandbox behaves exactly as before:
    • preopened_dir takes one FsPerms instead of DirPerms + FilePerms. /project (READ + READ) becomes FsPerms::ReadOnly; /plugin (all() + all()) becomes FsPerms::ReadWrite.
    • TCP and UDP are off by default in 49 (they were on in 46), so inherit_network() alone no longer grants sockets. A network grant now also calls allow_tcp(true) and allow_udp(true), which is the 46 configuration exactly. IP name lookup stays off, and plugins without the grant keep sockets off.
  • Checked and unchanged: fuel (10 billion), the 60-second epoch timeout, the 256 MiB memory cap, StoreLimits, the Trap::OutOfFuel and Trap::Interrupt error mapping, and the env/args/stdin/stderr defaults. The .cwasm cache stamp follows the requirement string, so each installed plugin recompiles once on its next run.
  • New tests in src/plugin/wasm.rs make real WASI preview 1 calls to pin /project read-only, /plugin read-write, .. confinement, no preopens without a grant, and the memory cap. They pass unchanged on main's 46.0.3 with the same errno values. Five of them fail when the 46.0.3 sandbox is weakened (/project made writable, memory limiter removed).
  • Windows: fledge now declares the windows-sys Win32_Security feature, which CreateJobObjectW in src/lanes/execute.rs needs. The feature had only ever arrived through feature unification from wasmtime-wasi 46's cap-std (cap-primitives 3.4.6). 49 dropped cap-std, and this PR's first test (windows-latest) run failed to compile. Main's --no-default-features Windows build already lacked the feature. This adds a feature flag only, with no new crate and no Cargo.lock change.
  • Exposure: neither advisory looks reachable from fledge's host, which runs core modules over WASI preview 1 only. 0316 is in the component-model Val API. Preview 1 cannot form the 0314 overflow: a preview 1 guest passing extreme timestamps to 46.0.3 returned normally. The upgrade ships anyway, because the plugin runtime is a security boundary and audit gates CI.
  • Building from source now needs rustc 1.96, wasmtime 49's minimum (46 needed 1.94). CI builds on stable. The rust-version = "1.89" in Cargo.toml was already below what 46 needed. It is left alone here; the change record's context explains why.
  • Review fix (8964ca0): wasmtime 47 to 49 turned on GC, exception handling, typed function references and wide arithmetic by default, and create_engine never set features itself, so the upgrade would have widened what a plugin can run. The engine now pins wasmtime 46's feature set. The new test engine_keeps_wasmtime_46_feature_set checks that a baseline module compiles and that each of the four proposals is refused. It passes on main's 46.0.3, and it fails if the pins are removed.
  • Pre-existing, left for a separate issue: StoreLimits sets no table-element limit, so a plugin can grow a table far past the 256 MiB memory cap (same on 46 and 49).
  • CHANGELOG: a Security entry under 1.8.1, and the 1.8.1 note no longer says dependencies are unchanged from 1.8.0. The version stays 1.8.1, and the tag waits on this PR.
  • SpecSync: draft change upgrade-wasmtime-past-rustsec-2026-0316-and-rustsec-2026-0314 (bug fix, no canonical spec change). Definition approval is pending with orc/Leif. Nothing is approved, reviewed or shipped in this PR.

Test Plan

  • cargo fmt --check
  • cargo clippy --locked -- -D warnings, cargo clippy --all-targets -- -D warnings and cargo clippy --locked --no-default-features -- -D warnings (rustc 1.98.0, macOS)
  • cargo test --verbose --locked: 1097 unit tests (including the feature-set test) and every integration test binary pass
  • cargo audit against advisory DB 9b3a3b7, the commit CI used: the branch lock and a fresh generate-lockfile both report 0 vulnerabilities, and main's lock still reports both (negative control)
  • The new sandbox tests pass on 49.0.1 and on main's 46.0.3, and fail against a weakened sandbox
  • specsync check --force --strict --require-coverage 100, specsync lifecycle enforce --all, specsync change check --strict --require-coverage 100
  • fledge trust verify
  • cargo deny: not applicable, the repository has no deny.toml
  • cargo tree --target x86_64-pc-windows-msvc -e features: Win32_Security on windows-sys 0.59 now comes from fledge, with and without default features (no Windows target locally, so CI's windows cells are the compile check)
  • CI on 8964ca0 (run 36809320239): test and integration on ubuntu, macos and windows; lint; audit (0 vulnerabilities); spec-check; intent-check; trust; CodeQL
  • SpecSync definition approval (orc/Leif)

🤖 Generated with Claude Code

https://claude.ai/code/session_01V3ZZAEiUP7xRJPozhZb6rL

CI's audit job on main (run 36787734566) fails on two advisories,
published 2026-09-24, against the wasmtime that sandboxes WASM plugins:

  RUSTSEC-2026-0316 (GHSA-jqpg-j7w6-42pr), wasmtime 46.0.3:
    dynamic record lifting can allocate beyond the hostcall fuel limit
  RUSTSEC-2026-0314 (GHSA-j2g9-4prp-pf6h), wasmtime-wasi 46.0.3:
    a guest can panic the host through a filesystem datetime overflow

The job runs `cargo generate-lockfile` before auditing, so the Cargo.toml
requirement itself has to move. wasmtime and wasmtime-wasi go from 46.0.1
to 49.0.1, the newest patched line. A targeted `cargo update -p wasmtime
-p wasmtime-wasi` moves only the wasmtime tree (wiggle, pulley, every
wasmtime-internal crate, cranelift 0.136, wasm-tools 0.258).

Two wasmtime-wasi 49 API changes reach build_wasi_p1:

  - preopened_dir takes one FsPerms instead of DirPerms + FilePerms.
    /project's READ + READ becomes FsPerms::ReadOnly and /plugin's
    all() + all() becomes FsPerms::ReadWrite. The permission check at
    every call site is equivalent.
  - TCP and UDP are now off by default (46 had them on), so
    inherit_network() alone would leave a network-granted plugin with no
    sockets. The grant now also calls allow_tcp(true) and allow_udp(true),
    which is the 46 configuration exactly. IP name lookup stays off.

Fuel, epoch and memory limits, trap mapping and the CLI context defaults
are unchanged. New tests drive real WASI preview 1 calls to pin the
read-only /project and read-write /plugin grants, `..` confinement, the
no-grant case and the 256 MiB memory cap. They give the same results on
main's 46.0.3, and fail when the 46.0.3 sandbox is weakened.

Neither advisory looks reachable from fledge's host, which runs core
modules over WASI preview 1 only. The upgrade ships anyway, because the
runtime is a security boundary and audit gates CI. Building from source
now needs rustc 1.96 (wasmtime 49's minimum; 46 needed 1.94).

Adds the CHANGELOG entry under 1.8.1 and the SpecSync change record
(draft, awaiting definition approval).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3ZZAEiUP7xRJPozhZb6rL
@0xLeif
0xLeif requested a review from a team as a code owner October 1, 2026 02:07
@0xLeif
0xLeif requested review from 0xGaspar, Kyntrin and tofu-ux and removed request for a team October 1, 2026 02:07
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-01T04:07:02.604585Z e16fca8 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Corvin says...

      _
    <(;\  .oO(oh no...)
     |/(\
      \(\\
      " "\\

"Caw... validation failed..."

CI Summary

Check Status
Dependency Audit ❌ cancelled
Integration (3 OS) ❌ cancelled
Lint (fmt + clippy) ❌ cancelled
Spec Validation ❌ cancelled
Tests (3 OS) ❌ cancelled

Powered by corvid-pet

@github-actions
github-actions Bot dismissed their stale review October 1, 2026 02:14

Superseded by updated review.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Corvin says...

      _
    <(;\  .oO(oh no...)
     |/(\
      \(\\
      " "\\

"I'm pecking through the errors..."

CI Summary

Check Status
Dependency Audit ✅ Passed
Integration (3 OS) ❌ skipped
Lint (fmt + clippy) ✅ Passed
Spec Validation ✅ Passed
Tests (3 OS) ❌ failure

Powered by corvid-pet

…needs

test (windows-latest) failed to compile on #538's first run:

  error[E0432]: unresolved import
    `windows_sys::Win32::System::JobObjects::CreateJobObjectW`
     --> src\lanes\execute.rs:659:76

windows-sys 0.59 compiles CreateJobObjectW only with the Win32_Security
feature, because its first parameter is a SECURITY_ATTRIBUTES pointer.
fledge declares only Win32_Foundation and Win32_System_JobObjects. On main
the missing feature arrived through feature unification: cap-primitives
3.4.6, under wasmtime-wasi 46's cap-std, enables Win32_Security on
windows-sys 0.59. wasmtime-wasi 49 dropped cap-std, so nothing turns it on
any more. With --no-default-features, main had no Win32_Security edge at
all, so a Windows build without the wasm feature was already broken.

Declare the feature fledge uses. It is a feature flag only: no new crate
and no Cargo.lock change. The CHANGELOG entry and the change record say so.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ce1c85f00

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Cargo.toml
Comment on lines +39 to +40
wasmtime = { version = "49.0.1", optional = true }
wasmtime-wasi = { version = "49.0.1", optional = true }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Raise the declared Rust version with Wasmtime

Wasmtime 49 requires Rust 1.96, but this crate still declares rust-version = "1.89" and enables the Wasmtime dependencies by default. Consequently, users on Rust 1.94 or 1.95—both of which could build the previous Wasmtime 46 dependency—will select this release based on its manifest and then fail during dependency resolution or compilation. Update the package's rust-version to 1.96 (and any associated toolchain metadata) so the published compatibility contract matches the new default dependency floor.

Useful? React with 👍 / 👎.

@github-actions
github-actions Bot dismissed their stale review October 1, 2026 02:47

Superseded by updated review.

github-actions[bot]
github-actions Bot previously approved these changes Oct 1, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Corvin says...

      _
    <(^\  .oO(Caw! ^v^)
     |/(\
      \(\\
      " "\\

"Caw! Your code sparkles like a dropped french fry."

CI Summary

Check Status
Dependency Audit ✅ Passed
Integration (3 OS) ✅ Passed
Lint (fmt + clippy) ✅ Passed
Spec Validation ✅ Passed
Tests (3 OS) ✅ Passed

Powered by corvid-pet

create_engine took wasmtime's default feature set. wasmtime 47 turned the
GC, exception-handling and typed function references proposals on by
default, and 49 added wide arithmetic, so the 46.0.3 to 49.0.1 upgrade
alone let a plugin load modules that 46.0.3 rejected at compile time.
Probe modules for each proposal are refused on main and accepted on
8ce1c85.

Turn all four off explicitly. No plugin that ran on 1.8.0 can depend on
them, and wasm32-wasip1 toolchains do not emit them by default. They are
not free surface: RUSTSEC-2026-0315, fixed in the same 49.0.1 release, let
call_ref and exception catch drop fuel accounting.

engine_keeps_wasmtime_46_feature_set compiles a baseline module (bulk
memory, sign extension, saturating truncation, multi-value, reference
types, tail calls) and asserts one module per proposal is refused. It
passes unchanged on main's 46.0.3, each refused module compiles under
49.0.1's defaults, and the test fails when the four calls are removed.

The CHANGELOG entry and the draft change record (rationale, acceptance
criteria, context, design, testing, tasks) say so. The record also notes a
pre-existing gap left alone: StoreLimits sets no table_elements limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3ZZAEiUP7xRJPozhZb6rL
github-actions[bot]
github-actions Bot previously approved these changes Oct 1, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Corvin says...

      _
    <(^\  .oO(Caw! ^v^)
     |/(\
      \(\\
      " "\\

"Caw! Found a shiny new spec!"

CI Summary

Check Status
Dependency Audit ✅ Passed
Integration (3 OS) ✅ Passed
Lint (fmt + clippy) ✅ Passed
Spec Validation ✅ Passed
Tests (3 OS) ✅ Passed

Powered by corvid-pet

…ask list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3ZZAEiUP7xRJPozhZb6rL
github-actions[bot]
github-actions Bot previously approved these changes Oct 1, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Corvin says...

      _
    <(^\  .oO(Caw! ^v^)
     |/(\
      \(\\
      " "\\

"Caw! Found a shiny new spec!"

CI Summary

Check Status
Dependency Audit ✅ Passed
Integration (3 OS) ✅ Passed
Lint (fmt + clippy) ✅ Passed
Spec Validation ✅ Passed
Tests (3 OS) ✅ Passed

Powered by corvid-pet

…dbox contract

`specsync change ship` refused the no-spec draft because src/plugin/wasm.rs is
production source and no declared module owned it. The change now declares
plugin-wasm (no_spec_change false) and carries a delta: REQ-plugin-wasm-001 to
006 for the sandbox contract, plus the five spec sentences they contradict.
testing.md maps each requirement to its evidence. The change stays a draft.

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

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Corvin says...

      _
    <(^\  .oO(Caw! ^v^)
     |/(\
      \(\\
      " "\\

"Caw! Found a shiny new spec!"

CI Summary

Check Status
Dependency Audit ✅ Passed
Integration (3 OS) ✅ Passed
Lint (fmt + clippy) ✅ Passed
Spec Validation ✅ Passed
Tests (3 OS) ✅ Passed

Powered by corvid-pet

This branch has not been deployed

No deployments
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