diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 2125369..de08c19 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -277,6 +277,43 @@ fn handle_trap_drops_message_actor_survives() { rt.tick(); } +// ── Bounds safety: alloc pointer near end of linear memory ──────────────────── + +#[test] +#[ignore] // BUG: actor.rs:44-46 has no bounds check — actor gets poisoned instead of surviving +fn alloc_near_end_of_memory_drops_message_actor_survives() { + // Guest alloc returns 65500 (near end of 1-page / 65536-byte memory). + // A 100-byte message means ptr+len = 65600, which exceeds memory bounds. + // The actor should drop the message and survive — same as any other + // allocation failure — rather than being permanently killed. + let wat = r#" + (module + (import "swactor" "send" (func $send (param i32 i32 i32))) + (memory (export "memory") 1) + (func (export "alloc") (param i32) (result i32) + i32.const 65500 ;; near end of 64KiB memory + ) + (func (export "handle") (param i32 i32) + ;; should never be reached if bounds check works + ) + ) + "#; + let wasm = wat::parse_str(wat).unwrap(); + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, wasm).build().unwrap(); + + let rt = Runtime::new(RuntimeConfig::default()); + let addr = rt.spawn(actor).unwrap(); + + // Send a message whose length exceeds the remaining space at ptr 65500 + rt.send_to(addr, ByteMessage(vec![0u8; 100])).unwrap(); + rt.tick(); + + // The actor should still be alive — send another message and tick without panic + rt.send_to(addr, ByteMessage(vec![1u8; 10])).unwrap(); + rt.tick(); +} + // ── Integration: WasmActor alongside a native Rust actor ───────────────────── #[derive(Clone)]