nordisk/heimdall
crates/heimdall-wasm/tests/guest_output.rs
// SPDX-License-Identifier: AGPL-3.0-only
//! The value `run` returns is untrusted too: an oversized note or reason, a
//! result pointer outside memory, a bad variant tag or invalid UTF-8 are all
//! refusals. Heimdall is a defensive sandbox.
mod common;
use common::*;
use heimdall_wasm::RunLimits;
/// Returns a note (or reason) of `len` zero bytes from a buffer it allocates.
fn text_of_len(kit: &Kit, refuse: bool) -> heimdall_wasm::Guest {
let f = if refuse { "$refuse" } else { "$ok" };
kit.guest(
if refuse { "big-reason" } else { "big-note" },
&Wat::new(&format!(
"(call {f} (call $realloc (i32.const 0) (i32.const 0) (i32.const 1) (i32.load (local.get $in))) (i32.load (local.get $in)))"
))
.text(),
)
}
#[test]
fn a_note_at_the_text_limit_is_accepted() {
let kit = Kit::new("note-ok");
let limits = RunLimits {
max_text_bytes: 1000,
..RunLimits::default()
};
let o = text_of_len(&kit, false).run(&1000u32.to_le_bytes(), &none(), &limits);
assert!(o.verdict.is_accepted(), "{}", o.verdict);
}
#[test]
fn a_huge_note_is_refused() {
let kit = Kit::new("note-huge");
let o =
text_of_len(&kit, false).run(&(8u32 << 20).to_le_bytes(), &none(), &RunLimits::default());
assert_trapped(&o, "note");
}
#[test]
fn a_huge_refusal_reason_is_refused_as_a_trap_not_passed_on() {
let kit = Kit::new("reason-huge");
let o =
text_of_len(&kit, true).run(&(1u32 << 20).to_le_bytes(), &none(), &RunLimits::default());
assert_trapped(&o, "reason");
assert!(
o.verdict.reason().unwrap().len() < 1000,
"the huge reason leaked into the verdict"
);
}
#[test]
fn a_note_that_is_not_utf8_is_refused() {
let kit = Kit::new("note-utf8");
let g = kit.guest(
"badutf8",
&Wat::new("(call $ok (i32.const 16) (i32.const 3))")
.extra(r#"(data (i32.const 16) "\ff\fe\fd")"#)
.text(),
);
assert_trapped(&g.run(b"", &none(), &RunLimits::default()), "UTF-8");
}
#[test]
fn a_result_pointer_outside_memory_is_refused() {
let kit = Kit::new("ret-oob");
let g = kit.guest("retoob", &Wat::new("(i32.const 0xfffffff0)").text());
assert_trapped(&g.run(b"", &none(), &RunLimits::default()), "out of bounds");
}
#[test]
fn a_note_pointer_outside_memory_is_refused() {
let kit = Kit::new("note-oob");
let g = kit.guest(
"noteoob",
&Wat::new("(call $ok (i32.const 0xffff0000) (i32.const 10))").text(),
);
assert_trapped(&g.run(b"", &none(), &RunLimits::default()), "out of bounds");
}
#[test]
fn a_result_tag_that_is_neither_ok_nor_err_is_refused() {
let kit = Kit::new("bad-tag");
let g = kit.guest(
"badtag",
&Wat::new("(drop (call $ok (i32.const 0) (i32.const 0))) (i32.store8 (i32.const 1024) (i32.const 7)) (i32.const 1024)").text(),
);
assert_trapped(&g.run(b"", &none(), &RunLimits::default()), "discriminant");
}
#[test]
fn a_misaligned_result_pointer_is_refused() {
let kit = Kit::new("misaligned");
let g = kit.guest("misaligned", &Wat::new("(i32.const 1025)").text());
assert_trapped(&g.run(b"", &none(), &RunLimits::default()), "align");
}