Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sandbox): add -Zsandbox unstable flag #66

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

weihanglo
Copy link
Owner

@weihanglo weihanglo commented Oct 19, 2024

What does this PR try to resolve?

This is an experiment for rust-lang/rust-project-goals#108.

An unstable [sandbox] config table is added behind the -Zsandbox unstable flag:

  • sandbox.runner: Target platform sandboxed build scripts built for
  • sandbox.target: Runner that executes artifacts of build scripts

Here is an example .cargo/config.toml:

[sandbox]
target = "wasm32-wasip1"
runner = [
  "wasmtime",
  "--wasi",
  "inherit-env=y",
  "--dir",
  "/path/to/my/package",
]

Also an example main.rs:

use std::path::PathBuf;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;

fn main() {
    println!("cargo:warning=Hello from sandbox wasm");
    // Access to Cargo internal environment variables
    //
    // Require `--env` or `-S inherit-env=y`
    let cargo_env = std::env::var("CARGO").unwrap();
    println!("cargo:warning=CARGO={cargo_env}");

    // Access to inherited enviroment variables
    //
    // Require `-S inherit-env=y`
    let shell = std::env::var("SHELL").unwrap();
    println!("cargo:warning=SHELL={shell}");

    // Files access under a given directory
    //
    // Require `--dir $CARGO_MANIFEST_DIR`
    let ts_file = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
        .join("target")
        .join("wasi-timestamp");
    let timestamp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_millis();
    std::fs::write(&ts_file, format!("{timestamp}").as_bytes()).unwrap();
    let ts = std::fs::read_to_string(ts_file).unwrap();
    println!("cargo:warning=current timestamp is {ts}");

    // Spawn process
    //
    // Unsupported 😭😭😭
    std::process::Command::new(cargo_env).status().unwrap();
}

What did we achieve in this experiment?

As you can see, we can easily swap to any sandbox runner with a custom target.

We use wasm32-wasip1 above as an example,
as it is the one with smaller footprint, very cross-platform, and pretty popular in the Rust community.
However, it turns out that `wasm32-wasip1 doesn't support POSIX process spawning.
Use cases of process spawning in build scripts are essential, such as

  • Calling pkg-config to find system libraries.
  • Invoking compilers to build non Rust code, e.g., C compiler, Protobuf compiler.
  • Invoking cargo metadata to get crate metadata.
  • Retrieving version control information via git.

According to the design axioms,

Restrcting process spawning is the top one axiom.
We made that with wasm32-wasip1,
but have no way to opt-out.
This contradicts to the "ensuring -sys crates be built" axiom.

As a result, it is unlikely to use wasm32-wasip1 as a default sandbox environment with this experiment.

Other possibilities

Have talked to some other folks, there are some potential route we could take if we chose wasi as a default sandbox environment.

The offical build-rs crate to the rescue

The Cargo team recently adopted the build-rs crate.
It is going to be the official crate providing API for writing build scripts.
We could take the advantage of it, telling everyone instead of using std::process::Command, use something like build_rs::Command. So that Cargo could have a full control over how a build script spawning processes.

While it sounds ideal, this doesn't help the current situation because

  • It doesn't help old crates.
  • It's hard to convince people to change their build scripts for trying an unstable feature.
  • Using build-rs in build scripts may increase build time; people may refuse to use.
  • I myself tend to have a big switch to turn on and off, which is easier to try a new feature.

A Cargo-flavored wasi standard library

There was a discussion in the GSoC "sandboxed proc-macro with wasm" about shipping a custom verion of the standard library for sandboxed wasm. For Cargo's build script, we could potentially ship a wasm32-wasi-cargo target. The std in this custom target could intercept any exec call or process spawning. Then it calls back to the host process (which is Cargo in our case) to determine how to handle process execution. The host process could either reject, or run the external program and post back the result.

This idea sounds pretty hacky and need more investigations of the communication mechanism between the host process and the wasm runtime. Perhaps via sockets, The WebAssembly Component Model, other host function call mechanism. There is also WASIX project which supprots fork/exec though it is currently not a WASI standard not even a proposal.

Continue with other more mature sandbox runtime choices

Since one of major design space is the user interafce of sandbox configuration,
we could leave off sandbox runtimes and explore more on the configuration side.

We could, for example, use docker or eBPF as a temporary default runtime, and explore how the configuration should look like.
We may want to take a look at the configuration of Cackle-rs as a starting point. By doing so, we wouldn't block on waiting for wasm runtime to being more mature.

* `sandbox.runner`: Target platform sandboxed build scripts built for
* `sandbox.target`: Runner that executes artifacts of build scripts

Example config:

```toml
[sandbox]
target = "wasm32-wasip1"
runner = [
  "wasmtime",
  "--wasi",
  "inherit-env=y",
  "--dir",
  "/path/to/my/package",
]
```
Add support for `[sandbox]` to specify a target platform
and a custom build script runner.

To help the external runner finds and executes the executable produced
by a build script, it is assumed that only one executable was produced.
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