From 4b2a17e4e309935e680c6ee3200e457b9b975eca Mon Sep 17 00:00:00 2001 From: Andrew Steurer <94206073+asteurer@users.noreply.github.com> Date: Fri, 15 May 2026 14:24:01 -0500 Subject: [PATCH 1/2] fix: add defer pinner.Unpin() Signed-off-by: Andrew Steurer <94206073+asteurer@users.noreply.github.com> --- crates/go/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/go/src/lib.rs b/crates/go/src/lib.rs index bf754f81b..4caf53a04 100644 --- a/crates/go/src/lib.rs +++ b/crates/go/src/lib.rs @@ -1326,7 +1326,10 @@ func {camel}({go_params}) {go_results} {{ ( if abi::guest_export_needs_post_return(resolve, func) { - format!("{PINNER} := &runtime.Pinner{{}}") + format!( + "{PINNER} := &runtime.Pinner{{}} + defer {PINNER}.Unpin()" + ) } else { String::new() }, From 9ca1f92a919e704d8fc48bb5fb558fa664b4a201 Mon Sep 17 00:00:00 2001 From: Andrew Steurer <94206073+asteurer@users.noreply.github.com> Date: Wed, 20 May 2026 12:31:29 -0500 Subject: [PATCH 2/2] feat: add test Signed-off-by: Andrew Steurer <94206073+asteurer@users.noreply.github.com> --- .../async/return-string/runner.go | 35 +++++++++++++++++++ .../runtime-async/async/return-string/test.go | 5 +++ .../async/return-string/test.wit | 14 ++++++++ 3 files changed, 54 insertions(+) create mode 100644 tests/runtime-async/async/return-string/runner.go create mode 100644 tests/runtime-async/async/return-string/test.go create mode 100644 tests/runtime-async/async/return-string/test.wit diff --git a/tests/runtime-async/async/return-string/runner.go b/tests/runtime-async/async/return-string/runner.go new file mode 100644 index 000000000..3038171f3 --- /dev/null +++ b/tests/runtime-async/async/return-string/runner.go @@ -0,0 +1,35 @@ +//@ wasmtime-flags = '-Wcomponent-model-async' + +package export_wit_world + +import ( + "fmt" + "runtime" + + test "wit_component/my_test_i" +) + +/* + +This tests for pinner leaks in generated Go code for async exported +function that return heap-allocated types (strings, lists, etc.). Without +`pinner.Unpin()`, the `runtime.Pinner` object goes out of scope after +the function returns with pinned pointers still alive. When GC finalizes +the Pinner, it panics: + +``` +panic: runtime error: runtime.Pinner: found leaking pinned pointer; +forgot to call Unpin()? +``` +*/ + +func Run() { + // Perform a heap allocation + got := test.ReturnString() + if got != "hello" { + panic(fmt.Sprintf("expected \"hello\", got %q", got)) + } + + // Force GC to finalize any leaked Pinners + runtime.GC() +} diff --git a/tests/runtime-async/async/return-string/test.go b/tests/runtime-async/async/return-string/test.go new file mode 100644 index 000000000..2342e68d1 --- /dev/null +++ b/tests/runtime-async/async/return-string/test.go @@ -0,0 +1,5 @@ +package export_my_test_i + +func ReturnString() string { + return "hello" +} diff --git a/tests/runtime-async/async/return-string/test.wit b/tests/runtime-async/async/return-string/test.wit new file mode 100644 index 000000000..943d8cea7 --- /dev/null +++ b/tests/runtime-async/async/return-string/test.wit @@ -0,0 +1,14 @@ +package my:test; + +interface i { + return-string: async func() -> string; +} + +world test { + export i; +} + +world runner { + import i; + export run: async func(); +} \ No newline at end of file