use rwasm::{
instruction_set, ExecutionEngine, ImportLinker, ImportName, InstructionSet, RwasmModuleBuilder,
RwasmStore, StoreTr, TrapCode, TypedCaller, Value,
};
use rwasm_fuel_policy::SyscallFuelParams;
use std::sync::Arc;
use wasmparser::ValType;
fn execute(code_section: InstructionSet) -> Result<(), TrapCode> {
let module = RwasmModuleBuilder::new(code_section).build();
let mut store = RwasmStore::new(
Arc::new(ImportLinker::default()),
(),
rwasm::always_failing_syscall_handler,
Some(1_000_000),
None,
);
ExecutionEngine::new().execute(&mut store, &module, &[], &mut [])
}
#[test]
fn local_get_beyond_the_stack_traps_instead_of_reading_out_of_bounds() {
let code = instruction_set! {
StackCheck(16)
LocalGet(0x0800_0000)
Drop
Return
};
assert_eq!(execute(code), Err(TrapCode::StackOverflow));
}
#[test]
fn local_set_beyond_the_stack_traps_instead_of_writing_out_of_bounds() {
let code = instruction_set! {
StackCheck(16)
I32Const(0xdead_beefu32 as i32)
LocalSet(0x0800_0000)
Return
};
assert_eq!(execute(code), Err(TrapCode::StackOverflow));
}
#[test]
fn local_tee_beyond_the_stack_traps_instead_of_writing_out_of_bounds() {
let code = instruction_set! {
StackCheck(16)
I32Const(0xdead_beefu32 as i32)
LocalTee(0x0800_0000)
Drop
Return
};
assert_eq!(execute(code), Err(TrapCode::StackOverflow));
}
#[test]
fn dropping_below_the_stack_base_traps() {
let code = instruction_set! {
StackCheck(16)
Drop
Return
};
assert_eq!(execute(code), Err(TrapCode::StackOverflow));
}
#[test]
fn bulk_drop_below_the_stack_base_traps() {
let code = instruction_set! {
StackCheck(16)
BulkDrop(0x0800_0000)
Return
};
assert_eq!(execute(code), Err(TrapCode::StackOverflow));
}
#[test]
fn popping_below_the_stack_base_traps() {
let code = instruction_set! {
StackCheck(16)
I32Const(1)
I32Add
Return
};
assert_eq!(execute(code), Err(TrapCode::StackOverflow));
}
#[test]
fn syscall_with_underflowing_params_traps_before_reaching_the_host() {
const EXIT_IDX: u32 = 75;
fn halting_handler(
caller: &mut TypedCaller<'_, Vec<i32>>,
_idx: u32,
params: &[Value],
_result: &mut [Value],
) -> Result<(), TrapCode> {
caller
.data_mut()
.extend(params.iter().map(|p| p.i32().unwrap_or(i32::MIN)));
Err(TrapCode::ExecutionHalted)
}
let mut import_linker = ImportLinker::default();
import_linker.insert_function(
ImportName::new("fluentbase_v1preview", "_exit"),
EXIT_IDX,
SyscallFuelParams::default(),
&[ValType::I32],
&[],
);
let code = instruction_set! {
StackCheck(16)
Call(EXIT_IDX)
Return
};
let module = RwasmModuleBuilder::new(code).build();
let mut store = RwasmStore::new(
Arc::new(import_linker),
Vec::<i32>::new(),
halting_handler,
Some(1_000_000),
None,
);
let result = ExecutionEngine::new().execute(&mut store, &module, &[], &mut []);
assert_eq!(result, Err(TrapCode::StackOverflow));
assert!(
store.data().is_empty(),
"the host must not have been called: {:?}",
store.data()
);
}
#[test]
fn a_trap_caused_by_an_underflow_is_reported_as_the_underflow() {
let code = instruction_set! {
StackCheck(16)
I32Const(0)
I32DivU
Return
};
assert_eq!(execute(code), Err(TrapCode::StackOverflow));
}
#[test]
fn pushing_past_the_reserved_stack_window_traps() {
let mut code = instruction_set! { StackCheck(1) };
for _ in 0..(rwasm::N_MAX_STACK_SIZE + 1) {
code.op_i32_const(1);
}
code.op_return();
assert_eq!(execute(code), Err(TrapCode::StackOverflow));
}