From 6aeb289df0acae1213bdfc12fa0595652b9a9125 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 09:57:51 +0000 Subject: [PATCH] =?UTF-8?q?test:=20Cycle=2047=20=E2=80=94=20page=20boundar?= =?UTF-8?q?y=20exact/OOB,=20multiple=20runtimes,=20full=20message=20mirror?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - alloc_at_page_boundary_works: alloc + msg = exactly 65536, in bounds - alloc_one_past_page_boundary_drops: alloc + msg = 65537, OOB drops gracefully - multiple_runtimes_with_wasm_actors: two separate runtimes with WASM actors - guest_mirrors_full_message: sends full message (addr+payload) as payload All 204 tests pass. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski --- crates/wasm-actor/tests/wasm_actor.rs | 133 ++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 208fe40..0db5050 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -7145,4 +7145,137 @@ fn comprehensive_lifecycle_all_guest_types_200th() { rt.send_to(echo_addr, framed_msg(inbox.addr(), b"gone")).ok(); rt.tick(); assert!(inbox.try_recv().is_none(), "stopped actors should not deliver"); +} + +// ── Guest alloc returns pointer at exact page boundary ────────────────────── + +#[test] +fn alloc_at_page_boundary_works() { + // Guest alloc returns 65536 - 64 = 65472. With a 64-byte message, + // end = 65472 + 64 = 65536 = memory size. Exactly in bounds. + 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 65472) + (func (export "handle") (param $ptr i32) (param $len i32) + (call $send + (local.get $ptr) + (i32.add (local.get $ptr) (i32.const 32)) + (i32.sub (local.get $len) (i32.const 32)) + ) + ) + ) + "#; + 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 inbox = rt.new_inbox::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + // 32 (addr) + 32 (payload) = 64 bytes → fits exactly at 65472..65536 + let payload = vec![0xAB; 32]; + rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap(); + rt.tick(); + + let msg = inbox.try_recv().expect("exact page boundary should work"); + assert_eq!(msg.0, payload); +} + +// ── Guest alloc returns pointer 1 byte past page boundary — drops ─────────── + +#[test] +fn alloc_one_past_page_boundary_drops() { + // Guest alloc returns 65473. With 64-byte message: + // end = 65473 + 64 = 65537 > 65536. OOB, message dropped. + 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 65473) + (func (export "handle") (param i32 i32) + unreachable + ) + ) + "#; + 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 inbox = rt.new_inbox::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + let payload = vec![0xAB; 32]; // total msg = 64 bytes + rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap(); + rt.tick(); + + assert!(inbox.try_recv().is_none(), "OOB alloc should drop message"); +} + +// ── Multiple runtimes with WASM actors independently ──────────────────────── + +#[test] +fn multiple_runtimes_with_wasm_actors() { + let engine = SharedEngine::new().unwrap(); + + // Runtime 1 + let rt1 = Runtime::new(RuntimeConfig::default()); + let inbox1 = rt1.new_inbox::().unwrap(); + let actor1 = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap(); + let addr1 = rt1.spawn(actor1).unwrap(); + + // Runtime 2 + let rt2 = Runtime::new(RuntimeConfig::default()); + let inbox2 = rt2.new_inbox::().unwrap(); + let actor2 = WasmActorBuilder::new(engine, guest_wasm("double")).build().unwrap(); + let addr2 = rt2.spawn(actor2).unwrap(); + + rt1.send_to(addr1, framed_msg(inbox1.addr(), b"rt1")).unwrap(); + rt2.send_to(addr2, framed_msg(inbox2.addr(), b"rt2")).unwrap(); + rt1.tick(); + rt2.tick(); + + let msg1 = inbox1.try_recv().unwrap(); + assert_eq!(msg1.0, b"rt1"); + + let d1 = inbox2.try_recv().unwrap(); + let d2 = inbox2.try_recv().unwrap(); + assert_eq!(d1.0, b"rt2"); + assert_eq!(d2.0, b"rt2"); +} + +// ── Guest sends back exact copy of the full message (including address) ───── + +#[test] +fn guest_mirrors_full_message() { + // Guest sends back the entire message (address + payload) to the dest. + 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 4096) + (func (export "handle") (param $ptr i32) (param $len i32) + ;; Send the full message (including address header) as payload + (call $send (local.get $ptr) (local.get $ptr) (local.get $len)) + ) + ) + "#; + 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 inbox = rt.new_inbox::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + let original_msg = framed_msg(inbox.addr(), b"mirror"); + rt.send_to(addr, original_msg.clone()).unwrap(); + rt.tick(); + + let msg = inbox.try_recv().expect("should receive mirrored full message"); + // Payload = full original message (address + payload) + assert_eq!(msg.0, original_msg.0, "should receive exact copy of full message"); } \ No newline at end of file