From c7f8a54b8af2fcb3eccde905dbf45a82f1369b98 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 09:21:08 +0000 Subject: [PATCH] =?UTF-8?q?test:=20Cycle=2022=20=E2=80=94=20OOB=20memory.f?= =?UTF-8?q?ill,=20inline=20spawn+send,=20sequential=20build=20independence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski --- crates/wasm-actor/tests/wasm_actor.rs | 111 ++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 89111ac..5ab1a3a 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -3668,3 +3668,114 @@ fn all_guest_modules_work_in_same_runtime() { assert_eq!(echo_count, 1, "echo should send 1 copy"); assert_eq!(double_count, 2, "double should send 2 copies"); } + +// ── OOB memory.fill: trap, actor survives ─────────────────────────────────── + +#[test] +fn guest_oob_memory_fill_traps_actor_survives() { + // Guest tries to fill past the end of memory. WASM traps on OOB bulk ops. + 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 256) + (func (export "handle") (param i32 i32) + ;; Fill starting at 65530, length 100 — overflows 65536 boundary + (memory.fill (i32.const 65530) (i32.const 0) (i32.const 100)) + ) + ) + "#; + 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(); + + rt.send_to(addr, ByteMessage(vec![1])).unwrap(); + rt.tick(); // OOB memory.fill traps + + // Actor survives + rt.send_to(addr, ByteMessage(vec![2])).unwrap(); + rt.tick(); +} + +// ── Native spawns WASM + sends via ctx.send, delivers in same tick ────────── + +struct WasmSpawnerInline { + engine: SharedEngine, + wasm_bytes: Vec, + inbox_addr: ActorAddress, +} + +#[derive(Clone)] +struct SpawnCmd; + +impl ActorInterface for WasmSpawnerInline { + type Incoming = SpawnCmd; + type Response = (); + fn handle(&mut self, ctx: &Ctx, _msg: SpawnCmd) { + let actor = WasmActorBuilder::new(self.engine.clone(), self.wasm_bytes.clone()) + .build() + .unwrap(); + let wasm_addr = ctx.spawn(actor).unwrap(); + let msg = framed_msg(&self.inbox_addr, b"inline-spawn"); + let _ = ctx.send(wasm_addr, msg); + } +} + +#[test] +fn native_spawns_wasm_and_sends_in_same_handler() { + // A native actor spawns a WASM actor and sends a message to it in the + // same handler call. The runtime's tick phases should handle this: + // phase 4 drains spawns, phase 5 delivers pending_local. + let engine = SharedEngine::new().unwrap(); + let wasm_bytes = guest_wasm("echo"); + + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + + let spawner = WasmSpawnerInline { + engine: engine.clone(), + wasm_bytes: wasm_bytes.clone(), + inbox_addr: *inbox.addr(), + }; + let spawner_addr = rt.spawn(spawner).unwrap(); + + rt.send_to(spawner_addr, SpawnCmd).unwrap(); + rt.tick(); // spawner handles SpawnCmd: spawns WASM, sends to it + rt.tick(); // WASM actor processes the message, echoes to inbox + + let received = inbox.try_recv().expect("inline spawn + send should work"); + assert_eq!(received.0, b"inline-spawn"); +} + +// ── Build from same bytes multiple times: no interference ─────────────────── + +#[test] +fn build_many_actors_from_same_bytes_sequentially() { + // Build 10 actors sequentially from the same engine + bytes. + // Each should be completely independent. + let engine = SharedEngine::new().unwrap(); + let wasm_bytes = guest_wasm("echo"); + + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + + for i in 0u8..10 { + let actor = WasmActorBuilder::new(engine.clone(), wasm_bytes.clone()) + .build() + .unwrap(); + let addr = rt.spawn(actor).unwrap(); + + rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap(); + rt.tick(); + + let received = inbox.try_recv().expect("sequential build should work"); + assert_eq!(received.0, vec![i]); + + rt.stop_actor(addr).unwrap(); + rt.tick(); + rt.tick(); + } +}