Skip to content

runtime: give clear error when a wasmexport function is called before module initializationย #71240

Open
@snaffi

Description

Go version

go version go1.24rc1 linux/arm64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE='on'
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/.jbdevcontainer/go-build'
GODEBUG=''
GOENV='/.jbdevcontainer/config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3652802248=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='arm64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/IdeaProjects/wasm-1-24/go.mod'
GOMODCACHE='/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/.jbdevcontainer/config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_arm64'
GOVCS=''
GOVERSION='go1.24rc1'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

I'm trying to call simple wasm exported Go function from wasmtime and wazero runtimes.

wasm.go

GOOS=wasip1 GOARCH=wasm go build -o out.wasm -buildmode=c-shared ./wasm/

//go:build wasm

package main

import "fmt"

//go:wasmexport SuperFunction
func SuperFunction(x, y int32) int32 {
	return x + y
} // OK

func main() {

}

wasmtime_main.go

package main

import (
	"context"
	_ "embed"
	"fmt"
	"log"
	"os"
	"unsafe"

	"github.com/bytecodealliance/wasmtime-go"
)

func memoryLimit(megabytes uint32) uint32 {
	const wazeroPageSize = 64
	kilobytes := megabytes * 1024
	return kilobytes / wazeroPageSize
}

//go:embed out.wasm
var wasmModule []byte

func main() {
	// Almost all operations in wasmtime require a contextual `store`
	// argument to share, so create that first
	engine := wasmtime.NewEngine()
	//store.SetWasi()

	// Once we have our binary `wasm` we can compile that into a `*Module`
	// which represents compiled JIT code.
	module, err := wasmtime.NewModule(engine, wasmModule)
	if err != nil {
		log.Fatal(err)
	}

	// Create a linker with WASI functions defined within it
	linker := wasmtime.NewLinker(engine)
	err = linker.DefineWasi()
	if err != nil {
		log.Fatal(err)
	}

	// Configure WASI imports to write stdout into a file, and then create
	// a `Store` using this wasi configuration.
	wasiConfig := wasmtime.NewWasiConfig()
	store := wasmtime.NewStore(engine)
	store.SetWasi(wasiConfig)
	instance, err := linker.Instantiate(store, module)
	if err != nil {
		log.Fatal(err)
	}

	// After we've instantiated we can lookup our `run` function and call
	// it.
	run := instance.GetFunc(store, "SuperFunction")
	_, err = run.Call(store, 96, 55)
	if err != nil {
		log.Fatal(err)
	}
}

wazero_main.go

package main

import (
	"context"
	_ "embed"
	"fmt"
	"log"
	"os"
	"unsafe"

	"github.com/tetratelabs/wazero"
	"github.com/tetratelabs/wazero/api"
	"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
)

func main() {
	ctx := context.Background()
	runtime := wazero.NewRuntimeWithConfig(
		ctx,
		wazero.
			NewRuntimeConfig().
			WithCoreFeatures(api.CoreFeaturesV2).
			WithMemoryLimitPages(memoryLimit(256)),
	)
	_, err := wasi_snapshot_preview1.Instantiate(ctx, runtime)
	if err != nil {
		panic(err)
	}

	builder := runtime.NewHostModuleBuilder("env")
	wasi_snapshot_preview1.NewFunctionExporter().ExportFunctions(builder)
	_, err = builder.Instantiate(ctx)
	if err != nil {
		panic(err)
	}

	compiledModule, err := runtime.CompileModule(ctx, wasmModule)
	if err != nil {
		panic(fmt.Errorf("can't compile WASM module: %w", err))
	}

	mod, err := runtime.InstantiateModule(
		ctx,
		compiledModule,
		wazero.
			NewModuleConfig().
			WithStdout(os.Stdout).
			WithStderr(os.Stderr),
	)
	if err != nil {
		panic(fmt.Errorf("can't instantiate module: %w", err))
	}

	funcName := "SuperFunction"
	targetFunc := mod.ExportedFunction(funcName)
	if targetFunc == nil {
		panic(fmt.Errorf("target function not found: %s", funcName))
	}

	mod.Memory().Grow(2)
	res, err := targetFunc.Call(ctx, 96, 55)
	//res, err := targetFunc.Call(ctx)
	fmt.Println(res, err)

	fmt.Println(targetFunc.Definition().DebugName())
	fmt.Println(targetFunc.Definition().Name())
	fmt.Println(targetFunc.Definition().ParamTypes())
	fmt.Println(targetFunc.Definition().ParamNames())

}

What did you see happen?

I got out of bounds memory access on wazero and wasmtime runtime

What did you expect to see?

I expect function execution without any error.

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.arch-wasmWebAssembly issuescompiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    Projects

    • Status

      No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions