Disallow direct call of host functions (#723)

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
Takeshi Yoneda
2022-07-30 10:33:20 +09:00
committed by GitHub
parent ce2f447555
commit 02c23d55db
33 changed files with 808 additions and 602 deletions

View File

@@ -37,20 +37,12 @@ func main() {
r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.
// Add a module that uses offset parameters for multiple results.
// ... defined in WebAssembly.
// Add a module that uses offset parameters for multiple results defined in WebAssembly.
wasm, err := resultOffsetWasmFunctions(ctx, r)
if err != nil {
log.Panicln(err)
}
// ... defined in Go.
host, err := resultOffsetHostFunctions(ctx, r)
if err != nil {
log.Panicln(err)
}
// wazero enables WebAssembly 1.0 by default. Opt-in to other features:
runtimeWithMultiValue := wazero.NewRuntimeWithConfig(
wazero.NewRuntimeConfig().WithFeatureMultiValue(true),
@@ -66,13 +58,13 @@ func main() {
}
// ... defined in Go.
hostWithMultiValue, err := multiValueHostFunctions(ctx, runtimeWithMultiValue)
multiValueFromImportedHost, err := multiValueFromImportedHostWasmFunctions(ctx, runtimeWithMultiValue)
if err != nil {
log.Panicln(err)
}
// Call the function from each module and print the results to the console.
for _, mod := range []api.Module{wasm, host, wasmWithMultiValue, hostWithMultiValue} {
for _, mod := range []api.Module{wasm, wasmWithMultiValue, multiValueFromImportedHost} {
getAge := mod.ExportedFunction("call_get_age")
results, err := getAge.Call(ctx)
if err != nil {
@@ -83,36 +75,6 @@ func main() {
}
}
// resultOffsetHostFunctions defines host functions that illustrate multiple
// results using a technique compatible with any WebAssembly 1.0 runtime.
//
// To return a value in WASM written to a result parameter, you have to define
// memory and pass a location to write the result. At the end of your function,
// you load that location.
func resultOffsetHostFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
return r.NewModuleBuilder("result-offset/host").
// To use result parameters, we need scratch memory. Allocate the least
// possible: 1 page (64KB).
ExportMemoryWithMax("mem", 1, 1).
// get_age returns a result, while a second result is written to memory.
ExportFunction("get_age", func(ctx context.Context, m api.Module, resultOffsetAge uint32) (errno uint32) {
if m.Memory().WriteUint64Le(ctx, resultOffsetAge, 37) {
return 0
}
return 1 // overflow
}).
// Now, define a function that shows the Wasm mechanics returning
// something written to a result parameter. The caller provides a
// memory offset to the callee, so that it knows where to write the
// second result.
ExportFunction("call_get_age", func(ctx context.Context, m api.Module) (age uint64) {
resultOffsetAge := uint32(8) // arbitrary memory offset (in bytes)
_, _ = m.ExportedFunction("get_age").Call(ctx, uint64(resultOffsetAge))
age, _ = m.Memory().ReadUint64Le(ctx, resultOffsetAge)
return
}).Instantiate(ctx, r)
}
// resultOffsetWasm was generated by the following:
//
// cd testdata; wat2wasm --debug-names result_offset.wat
@@ -126,23 +88,6 @@ func resultOffsetWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Modul
return r.InstantiateModuleFromBinary(ctx, resultOffsetWasm)
}
// multiValueHostFunctions defines Wasm functions that illustrate multiple
// results using the "multiple-results" feature.
func multiValueHostFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
return r.NewModuleBuilder("multi-value/host").
// Define a function that returns two results
ExportFunction("get_age", func() (age uint64, errno uint32) {
age = 37
errno = 0
return
}).
// Now, define a function that returns only the first result.
ExportFunction("call_get_age", func(ctx context.Context, m api.Module) (age uint64) {
results, _ := m.ExportedFunction("get_age").Call(ctx)
return results[0]
}).Instantiate(ctx, r)
}
// multiValueWasm was generated by the following:
//
// cd testdata; wat2wasm --debug-names multi_value.wat
@@ -155,3 +100,21 @@ var multiValueWasm []byte
func multiValueWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
return r.InstantiateModuleFromBinary(ctx, multiValueWasm)
}
// multiValueWasm was generated by the following:
//
// cd testdata; wat2wasm --debug-names multi_value_imported.wat
//
//go:embed testdata/multi_value_imported.wasm
var multiValueFromImportedHostWasm []byte
func multiValueFromImportedHostWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
r.NewModuleBuilder("multi-value/host").
// Define a function that returns two results
ExportFunction("get_age", func() (age uint64, errno uint32) {
age = 37
errno = 0
return
}).Instantiate(ctx, r)
return r.InstantiateModuleFromBinary(ctx, multiValueFromImportedHostWasm)
}

View File

@@ -13,8 +13,7 @@ import (
func Test_main(t *testing.T) {
stdout, _ := maintester.TestMain(t, main, "multiple-results")
require.Equal(t, `result-offset/wasm: age=37
result-offset/host: age=37
multi-value/wasm: age=37
multi-value/host: age=37
multi-value/imported_host: age=37
`, stdout)
}

Binary file not shown.

View File

@@ -0,0 +1,12 @@
;; multiValueWasmFunctions defines Wasm functions that illustrate multiple
;; results using the "multiple-results" feature.
(module $multi-value/imported_host
;; Imports the `get_age` function from `multi-value/host` defined in the host.
(func $get_age (import "multi-value/host" "get_age") (result (;age;) i64 (;errno;) i32))
;; Now, define a function that returns only the first result.
(func (export "call_get_age") (result i64)
call $get_age ;; stack = [37, errno] result of get_age
drop ;; stack = [37]
)
)