spacewasm

Project Url: nasa/spacewasm
Introduction: A flight-compliant WebAssembly interpreter.
More: Author   ReportBugs   
Tags:

SpaceWasm

license

SpaceWasm is an implementation of the WebAssembly specification meant to interpret Wasm binary on-board spacecraft. It is developed at NASA JPL Robotics.

SpaceWasm is the interpreter behind F´'s Svc::WasmSequencer, released in F´ v4.4.0. F´ is JPL's open-source flight-software framework and flew on the Ingenuity Mars Helicopter.

  1. Command sequencing: High-level spacecraft activities are typically encoded outside of the embedded flight-software in command sequences. These activities can include anything from driving the Mars rover and operating its arm, to checking temperature ranges are nominal. Curiosity has executed more than 76,000 unique sequences on Mars. Historically, the form and capability of sequences has varied from mission to mission, even within JPL. Several deep-space missions use VML. Curiosity and Perseverance share a couple sequencing languages, and Europa Clipper has another. JWST uses JavaScript and Euclid uses MicroPython. SpaceWasm implements an industry standard, providing consolidation.

  2. Sandboxing: The cost and time of flight-software development is high due to its constrained requirements and scope. Validating a new flight-software capability often involves validating interactions with the entire system. This extends the V&V timeline and increases competition for testbed resources, which makes it hard to get new autonomy software into flight. WebAssembly gives the opportunity for untrusted or low-trust executables to make their way on-board in a way that flight-software can restrict access and compute time as well as monitor health and safety.

    On EO-1, the Autonomous Sciencecraft Experiment encountered two anomalies in flight. CPU load hit 100% against 80% on the testbed, which starved a memory-scrub task and triggered a fault response. Later, a new algorithm overwrote planner memory and the processor was restarted. Running those algorithms as separate Wasm modules prevents both failures. Flight software gives each module a fuel budget (REQ-5) and regains control when it's spent, so the memory scrub still runs. A load or store outside a module's linear memory traps instead of overwriting planner memory.

  3. Portability: WebAssembly provides well-defined interfaces and sandboxing that make transferring to another platform trivial. This includes platforms that flight computers typically target like PowerPC (the RAD750 on Perseverance), SPARC, ARM (the Snapdragon on Ingenuity), and RISC-V.

  4. Tooling: Standardizing to WebAssembly opens doors into a wide community of rich tooling and research! Most common languages (C, C++, Rust, Go, and others) can target WebAssembly and thus run in SpaceWasm. Despite the name, WebAssembly is not tied to the web.

Overview

This software comes with two major components:

  1. Decoder/Validator:

    Reads the Wasm binary in chunks and decodes it to an executable form. The decoder will use a fixed amount of memory and is intended to be measured per-Wasm binary on the ground using spacewasm validate, which reports the heap a module's load takes, section by section with --verbose.

    WebAssembly is validated during the decoding process and does not require another pass of the bytecode.

  2. Interpreter:

    A Wasm interpreter that can operate on linear memory and interface with hooks from the embedding.

SpaceWasm does not execute direct WebAssembly bytecode. Wasm bytecode is meant to be small and structured in a way to validate easily. These properties however make it slow to execute in-place. During the decoding process of Wasm instructions, SpaceWasm converts bytecode into another intermediate representation (IR) which includes properties better suited for interpretation. Read more about the IR in the specification.

Requirements

The requirements of SpaceWasm are levied from similar work produced by DLR.

See requirements.

SpaceWasm uses the Rust 2024 edition; the minimum supported Rust version is 1.87 (matching rust-version in Cargo.toml). Older toolchains fail to build with an edition2024 error.

Embedding

Embedding the interpreter refers to instantiating it and providing implementations for the functions that are imported into the module. Typically, the set of functions imported by the module are fixed and should be specified at compile time both for the Wasm module and the embedder.

For a reference embedding, see F´'s Svc::WasmSequencer (SDD), which drives SpaceWasm through the C API.

Dynamic Allocation

SpaceWasm has a unique dynamic memory allocation model. All of its design choices stem from requirements levied by common flight-software standards. Dynamic allocation follows the following rules:

  1. All allocations occur over a discrete number of fixed size blocks called pages. These pages are distinct from Wasm's linear memory pages.
  2. Deallocation cannot precede allocation.
  3. Sub-regions inside pages cannot grow or shrink, sizes should be fixed ahead of time.
  4. Memory usage must be deterministic.
  5. Any allocation failures must not result in panic.

The standard Rust allocation does not meet these constraints even with custom allocators. To that end, SpaceWasm provides its own data structures that guarantee these properties. These data-structures contain the bulk of the unsafe Rust semantics; the remainder lives in the performance-critical operand-stack, linear-memory, and IR access paths and the C FFI.

[!NOTE] These limitations are only enforced on the implementation of the interpreter and not on the Wasm bytecode it is made to interpret.

Wasm linear memory pages are allocated outside of dynamic memory pages.

[!IMPORTANT] A consequence of this model is that a partially loaded module cannot be rolled back. Unlike the WebAssembly specification, module instantiation is not atomic: a failed module load leaves the engine unusable, and the embedder must discard and reconstruct it. See the API documentation for Module::new and spacewasm_load_module for details.

Streaming

Peak memory usage is often an important constraint on small systems found on spacecraft. Many Wasm interpreters require the Wasm binary to be given in one linear blob to the interpreter. This is typically fine for systems where the same regions of memory may be reused for different purposes. Flight software on spacecraft generally assign fixed portions of memory for certain purposes. Therefore, requiring the entire Wasm binary to fit into a single chunk of memory is not feasible.

SpaceWasm is highly optimized to reduce peak memory usage and not require deallocation after allocation required for streaming. To this end, there are certain constraints imposed on the WebAssembly specification.

SpaceWasm supports decoding and compiling Wasm binary in a single pass via a streaming mechanism. Chunks of the Wasm binary may be provided to the interpreter as they are read/requested from the filesystem. The stream must provide chunks synchronously.

Ground Tooling

The spacewasm command, from cargo install spacewasm_tools (or cargo binstall spacewasm_tools for a prebuilt binary), is the ground side of the interpreter: a Cargo linker that holds wasm-opt to exactly the proposals a release implements and splits debug info into a symbol file, a validator that loads a module as the flight interpreter would, and a resolver from the PCs a backtrace reports to source lines.

It also runs arbitrary modules that adhere to the WASI 0.1 (wasip1) spec in a sandboxed environment, with flags to mount host directories and set environment variables:

# compile example from crates/spacewasm_tools/tests/wasi/
$ clang --target=wasm32-wasip1 -mcpu=mvp hello_universe.c -o hello_universe.wasm

# rewrite what this release does not implement
$ spacewasm lower hello_universe.wasm

$ spacewasm run hello_universe.wasm
hello universe!

spacewasm trace runs a module's exports and prints the last instructions each executed, for debugging the interpreter itself; make trace feeds it a fuzzer seed. See the spacewasm_tools README for every command.

Interpreter Limitations

This Wasm interpreter imposes additional constraints beyond the WebAssembly specification to support resource-constrained spacecraft environments.

See our IR SPEC for the full list of limitations.

These constraints enable deterministic memory usage and efficient execution in resource-constrained environments while maintaining compatibility with most standard WebAssembly modules.

Limits for Wasm Module Producers

Because SpaceWasm compiles bytecode into a fixed-width IR that is typically larger than the original bytecode, the practical ceiling on raw module size is bounded by the IR code-page limit in the table below (~8 GiB of IR). This is far larger than any module expected on flight hardware; the binding constraint in practice is the peak memory configured for the streaming decoder, which is measured per-module on the ground with spacewasm validate: it loads the module against the flight configuration (--max-code-pages, --decoder-limits, --heap-page-size, --heap-pages) and reports the IR it compiles to and the global heap pages its load takes.

Here are a couple of limitations that may be relevant to developers of Wasm modules.

Limit Value Notes
Wasm page size 64 KiB / 1 B Custom-Page-Sizes proposal is supported
Linear memory pages 4 GiB Per the Wasm 1.0 spec. A module declaring more (or a max above this) is rejected. Note that the embedding will definitely limit this but it is dependent on how the interpreter is deployed.
IR Code 8 GiB Compiled IR, not raw bytecode. This limit is across all modules in the store. The IR / Bytecode ratio is printed by example as the "compilation ratio". It is difficult to estimate this upfront because it varies on the types of instructions used.
Function parameters 255 32-bit words Per function.
Local variables 65,535 32-bit words Per function.
Import name length 32 bytes Applies to each import's module name and field name; a longer name is rejected at decode time.
Custom section name 32 bytes The name of each custom section (e.g. the name section); a longer name is rejected at decode time.

Benchmarking

SpaceWasm is tested against the Coremark benchmark to trace performance regression. See coremark for more information.

Testing

Unit & Integration Tests

cargo test

The unit tests check for regressions on the unsafe container abstractions provided by SpaceWasm due to unique alloc usage. There are also simple unit tests that cover all Wasm instructions without needing full WAST execution.

The integration tests are spectests from the Wasm 1.0 MVP suite which was curated in https://github.com/WasmEdge/wasmedge-spectest. These tests validate the integrity of the Wasm interpreter against the specification.

Fuzzing

SpaceWasm includes a comprehensive fuzzing infrastructure using libfuzzer and wasm-smith.

# Run fuzzer
make fuzz

# Differentially test the validator against wasmi (SpaceWasm vs a reference)
make fuzz-validate-differential

# Analyze crashes with execution traces
make trace CRASH=fuzz/artifacts/no_traps/crash-xxx

Feature Support Matrix

Below is a table of the implemented and planned WebAssembly proposals with links to their tracking issue / implementation pull-request.

Feature Status
Wasm MVP All Versions
Mutable globals All Versions
Custom page sizes ≥0.2.0
Bulk memory operations Planned
Sign-extension operators ≥0.8.0
Non-trapping float-to-int conversions ≥0.8.0
SIMD Under Consideration
Multi-value Under Consideration
Multiple memories Under Consideration

Currently, all other proposals are not implemented, planned or considered.

Credits & Acknowledgments

Portions of this project are adapted from the open-source projects:

  • rust-lang/rust, which is dual-licensed under MIT OR Apache License 2.0.
  • DLR-FT/wasm-interpreter, which is licensed under the Apache License 2.0.
  • Wasmtime, which is licensed under the Apache License 2.0 with LLVM-exception.
  • WABT, which is licensed under the Apache License 2.0.
  • wasmedge-spectest, which is licensed under MIT.
  • WebAssembly Testsuite, which is licensed under the Apache License 2.0.
  • Coremark, which is licensed under the COREMARK ACCEPTABLE USE AGREEMENT.
  • Wasm Coremark, which provides no upstream license file; the wrapped CoreMark payload is governed by the COREMARK ACCEPTABLE USE AGREEMENT.
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools
AI Daily Digest