| 28cc603f Andy McCall ↶ |
1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
use std::path::Path; |
| 6 |
use std::process::Command; |
| 7 |
|
| 8 |
const IMPORTS: &str = r#" |
| 9 |
(import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) |
| 10 |
(import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32))) |
| 11 |
(import "wasi_snapshot_preview1" "path_open" |
| 12 |
(func $path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32))) |
| 13 |
(import "wasi_snapshot_preview1" "args_sizes_get" (func $args_sizes_get (param i32 i32) (result i32))) |
| 14 |
(memory (export "memory") 1) |
| 15 |
"#; |
| 16 |
|
| 17 |
|
| 18 |
fn module(dir: &Path, path: &str, data: &str, body: &str) { |
| 19 |
let text = format!("(module {IMPORTS} {data} (func (export \"_start\") (local $e i32) {body}))"); |
| 20 |
let bytes = wat::parse_str(&text).unwrap_or_else(|e| panic!("{path}: {e}")); |
| 21 |
let file = dir.join(path); |
| 22 |
std::fs::create_dir_all(file.parent().unwrap()).unwrap(); |
| 23 |
std::fs::write(file, bytes).unwrap(); |
| 24 |
} |
| 25 |
|
| 26 |
|
| 27 |
fn print(fd: i32, len: i32) -> String { |
| 28 |
format!("(i32.store (i32.const 0) (i32.const 16)) (i32.store (i32.const 4) (i32.const {len})) (drop (call $fd_write (i32.const {fd}) (i32.const 0) (i32.const 1) (i32.const 8)))") |
| 29 |
} |
| 30 |
|
| 31 |
#[test] |
| 32 |
fn wasi_modules_run_confined_in_the_wasm_tier() { |
| 5cf1e8ae Andy McCall ↶ |
33 |
confined(false); |
| 34 |
} |
| 35 |
|
| 36 |
|
| 37 |
|
| 38 |
#[test] |
| 39 |
fn wasi_modules_run_confined_in_process() { |
| 40 |
confined(true); |
| 41 |
} |
| 42 |
|
| 43 |
fn confined(in_process: bool) { |
| 28cc603f Andy McCall ↶ |
44 |
let dir = tempfile::tempdir().unwrap(); |
| 45 |
let home = dir.path().join("home"); |
| 46 |
let work = dir.path().join("work"); |
| 47 |
let tiers = Command::new(env!("CARGO_BIN_EXE_flow")).arg("--home").arg(&home).arg("tiers").output().unwrap(); |
| 48 |
assert!(String::from_utf8_lossy(&tiers.stdout).contains("wasm"), "{}", String::from_utf8_lossy(&tiers.stdout)); |
| 49 |
|
| 50 |
|
| 51 |
module( |
| 52 |
&work, |
| 53 |
"mods/hello.wasm", |
| 54 |
r#"(data (i32.const 16) "hello from wasm\n")"#, |
| 55 |
&format!( |
| 56 |
"{} (drop (call $args_sizes_get (i32.const 100) (i32.const 104))) (if (i32.ne (i32.load (i32.const 100)) (i32.const 2)) (then (call $proc_exit (i32.const 7))))", |
| 57 |
print(1, 16) |
| 58 |
), |
| 59 |
); |
| 60 |
|
| 61 |
module(&work, "mods/each/a.wasm", r#"(data (i32.const 16) "module a\n")"#, &print(1, 9)); |
| 62 |
module(&work, "mods/each/b.wasm", r#"(data (i32.const 16) "module b\n")"#, &format!("{} (call $proc_exit (i32.const 3))", print(2, 9))); |
| 63 |
|
| 64 |
module( |
| 65 |
&work, |
| 66 |
"mods/writer.wasm", |
| 67 |
r#"(data (i32.const 64) "made.txt") (data (i32.const 16) "made in wasm\n")"#, |
| 68 |
"(local.set $e (call $path_open (i32.const 3) (i32.const 0) (i32.const 64) (i32.const 8) (i32.const 9) (i64.const 0x1fffffff) (i64.const 0x1fffffff) (i32.const 0) (i32.const 200))) |
| 69 |
(if (local.get $e) (then (call $proc_exit (i32.const 10)))) |
| 70 |
(i32.store (i32.const 0) (i32.const 16)) (i32.store (i32.const 4) (i32.const 13)) |
| 71 |
(drop (call $fd_write (i32.load (i32.const 200)) (i32.const 0) (i32.const 1) (i32.const 8)))", |
| 72 |
); |
| 73 |
|
| 74 |
module( |
| 75 |
&work, |
| 76 |
"mods/escape.wasm", |
| 77 |
r#"(data (i32.const 64) "../escaped")"#, |
| 78 |
"(local.set $e (call $path_open (i32.const 3) (i32.const 0) (i32.const 64) (i32.const 10) (i32.const 9) (i64.const 0x1fffffff) (i64.const 0x1fffffff) (i32.const 0) (i32.const 200))) |
| 79 |
(if (i32.eqz (local.get $e)) (then (call $proc_exit (i32.const 9))))", |
| 80 |
); |
| 81 |
|
| 82 |
module( |
| 83 |
&work, |
| 84 |
"mods/greedy.wasm", |
| 85 |
"", |
| 86 |
"(if (i32.ne (memory.grow (i32.const 8192)) (i32.const -1)) (then (call $proc_exit (i32.const 9))))", |
| 87 |
); |
| 88 |
module(&work, "mods/trap.wasm", "", "unreachable"); |
| 89 |
|
| 67851a86 Andy McCall ↶ |
90 |
|
| 91 |
std::fs::write(work.join("README"), "top\n").unwrap(); |
| 28cc603f Andy McCall ↶ |
92 |
std::fs::create_dir_all(work.join(".flow")).unwrap(); |
| 93 |
std::fs::write( |
| 94 |
work.join(".flow/pipeline.kdl"), |
| 95 |
r#"schema-version 1 |
| 96 |
sandbox "w" { |
| 97 |
runtime "wasm" |
| 98 |
memory-limit "256MiB" |
| 99 |
} |
| 100 |
job "hello" { |
| 101 |
sandbox "w" |
| 102 |
exec "/src/mods/hello.wasm" "one" |
| 103 |
} |
| 104 |
job "each" { |
| 105 |
sandbox "w" |
| 106 |
exec "mods/each/*.wasm" |
| 107 |
} |
| 108 |
job "writer" { |
| 109 |
sandbox "w" |
| 110 |
exec "mods/writer.wasm" |
| 111 |
} |
| 112 |
job "escape" { |
| 113 |
sandbox "w" |
| 114 |
exec "mods/escape.wasm" |
| 115 |
} |
| 116 |
job "greedy" { |
| 117 |
sandbox "w" |
| 118 |
exec "mods/greedy.wasm" |
| 119 |
} |
| 120 |
job "trap" { |
| 121 |
sandbox "w" |
| 122 |
exec "mods/trap.wasm" |
| 123 |
} |
| 124 |
"#, |
| 125 |
) |
| 126 |
.unwrap(); |
| 5cf1e8ae Andy McCall ↶ |
127 |
let mut flow = Command::new(env!("CARGO_BIN_EXE_flow")); |
| 128 |
if in_process { |
| 129 |
flow.env("FLOW_WASM_IN_PROCESS", "1"); |
| 130 |
} |
| 131 |
let run = flow.arg("--home").arg(&home).arg("run").current_dir(&work).output().unwrap(); |
| 28cc603f Andy McCall ↶ |
132 |
let out = String::from_utf8_lossy(&run.stdout).to_string() + &String::from_utf8_lossy(&run.stderr); |
| 133 |
for want in [ |
| 134 |
"hello from wasm", |
| 135 |
"── hello passed", |
| 136 |
"running mods/each/a.wasm", |
| 137 |
"module a", |
| 138 |
"mods/each/b.wasm: exit 3", |
| 139 |
"── each failed (exit 3)", |
| 140 |
"── writer passed", |
| 141 |
"── escape passed", |
| 142 |
"── greedy passed", |
| 143 |
"mods/trap.wasm trapped", |
| 144 |
"── trap failed (exit 134)", |
| 145 |
] { |
| 146 |
assert!(out.contains(want), "{want:?} not in:\n{out}"); |
| 147 |
} |
| 148 |
assert!(!dir.path().join("escaped").exists() && !work.join("escaped").exists(), "a module wrote outside the workspace"); |
| 149 |
assert_eq!(run.status.code(), Some(1), "two jobs failed:\n{out}"); |
| 150 |
} |