Forbids empty name module imports (#1244)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io> Co-authored-by: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com>
This commit is contained in:
@@ -35,9 +35,9 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
{
|
||||
name: "empty",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("")
|
||||
return r.NewHostModuleBuilder("host")
|
||||
},
|
||||
expected: &wasm.Module{},
|
||||
expected: &wasm.Module{NameSection: &wasm.NameSection{ModuleName: "host"}},
|
||||
},
|
||||
{
|
||||
name: "only name",
|
||||
@@ -49,7 +49,7 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
{
|
||||
name: "WithFunc",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").
|
||||
return r.NewHostModuleBuilder("host").
|
||||
NewFunctionBuilder().WithFunc(uint32_uint32).Export("1")
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
@@ -63,13 +63,14 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
|
||||
ModuleName: "host",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "WithFunc WithName WithParameterNames",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").NewFunctionBuilder().
|
||||
return r.NewHostModuleBuilder("host").NewFunctionBuilder().
|
||||
WithFunc(uint32_uint32).
|
||||
WithName("get").WithParameterNames("x").
|
||||
Export("1")
|
||||
@@ -86,13 +87,14 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "get"}},
|
||||
LocalNames: []*wasm.NameMapAssoc{{Index: 0, NameMap: wasm.NameMap{{Index: 0, Name: "x"}}}},
|
||||
ModuleName: "host",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "WithFunc WithName WithResultNames",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").NewFunctionBuilder().
|
||||
return r.NewHostModuleBuilder("host").NewFunctionBuilder().
|
||||
WithFunc(uint32_uint32).
|
||||
WithName("get").WithResultNames("x").
|
||||
Export("1")
|
||||
@@ -109,13 +111,14 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "get"}},
|
||||
ResultNames: []*wasm.NameMapAssoc{{Index: 0, NameMap: wasm.NameMap{{Index: 0, Name: "x"}}}},
|
||||
ModuleName: "host",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "WithFunc overwrites existing",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").
|
||||
return r.NewHostModuleBuilder("host").
|
||||
NewFunctionBuilder().WithFunc(uint32_uint32).Export("1").
|
||||
NewFunctionBuilder().WithFunc(uint64_uint32).Export("1")
|
||||
},
|
||||
@@ -130,6 +133,7 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
|
||||
ModuleName: "host",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -137,7 +141,7 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
name: "WithFunc twice",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
// Intentionally out of order
|
||||
return r.NewHostModuleBuilder("").
|
||||
return r.NewHostModuleBuilder("host").
|
||||
NewFunctionBuilder().WithFunc(uint64_uint32).Export("2").
|
||||
NewFunctionBuilder().WithFunc(uint32_uint32).Export("1")
|
||||
},
|
||||
@@ -154,13 +158,14 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}},
|
||||
ModuleName: "host",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "WithGoFunction",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").
|
||||
return r.NewHostModuleBuilder("host").
|
||||
NewFunctionBuilder().
|
||||
WithGoFunction(gofunc1, []api.ValueType{i32}, []api.ValueType{i32}).
|
||||
Export("1")
|
||||
@@ -178,13 +183,14 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
|
||||
ModuleName: "host",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "WithGoFunction WithName WithParameterNames",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").NewFunctionBuilder().
|
||||
return r.NewHostModuleBuilder("host").NewFunctionBuilder().
|
||||
WithGoFunction(gofunc1, []api.ValueType{i32}, []api.ValueType{i32}).
|
||||
WithName("get").WithParameterNames("x").
|
||||
Export("1")
|
||||
@@ -203,13 +209,14 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "get"}},
|
||||
LocalNames: []*wasm.NameMapAssoc{{Index: 0, NameMap: wasm.NameMap{{Index: 0, Name: "x"}}}},
|
||||
ModuleName: "host",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "WithGoFunction overwrites existing",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").
|
||||
return r.NewHostModuleBuilder("host").
|
||||
NewFunctionBuilder().
|
||||
WithGoFunction(gofunc1, []api.ValueType{i32}, []api.ValueType{i32}).
|
||||
Export("1").
|
||||
@@ -230,6 +237,7 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
|
||||
ModuleName: "host",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -237,7 +245,7 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
name: "WithGoFunction twice",
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
// Intentionally out of order
|
||||
return r.NewHostModuleBuilder("").
|
||||
return r.NewHostModuleBuilder("host").
|
||||
NewFunctionBuilder().
|
||||
WithGoFunction(gofunc2, []api.ValueType{i64}, []api.ValueType{i32}).
|
||||
Export("2").
|
||||
@@ -261,6 +269,7 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}},
|
||||
ModuleName: "host",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -306,7 +315,7 @@ func TestNewHostModuleBuilder_Compile_Errors(t *testing.T) {
|
||||
{
|
||||
name: "error compiling", // should fail due to missing result.
|
||||
input: func(rt Runtime) HostModuleBuilder {
|
||||
return rt.NewHostModuleBuilder("").NewFunctionBuilder().
|
||||
return rt.NewHostModuleBuilder("host").NewFunctionBuilder().
|
||||
WithFunc(&wasm.HostFunc{
|
||||
ExportNames: []string{"fn"},
|
||||
ResultTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestFunctionListenerFactory(t *testing.T) {
|
||||
// Define a module with two functions
|
||||
bin := binaryencoding.EncodeModule(&wasm.Module{
|
||||
TypeSection: []wasm.FunctionType{{}},
|
||||
ImportSection: []wasm.Import{{}},
|
||||
ImportSection: []wasm.Import{{Module: "host"}},
|
||||
FunctionSection: []wasm.Index{0, 0},
|
||||
CodeSection: []wasm.Code{
|
||||
// fn1
|
||||
@@ -70,7 +70,7 @@ func TestFunctionListenerFactory(t *testing.T) {
|
||||
r := wazero.NewRuntime(ctx)
|
||||
defer r.Close(ctx) // This closes everything this Runtime created.
|
||||
|
||||
_, err := r.NewHostModuleBuilder("").NewFunctionBuilder().WithFunc(func() {}).Export("").Instantiate(ctx)
|
||||
_, err := r.NewHostModuleBuilder("host").NewFunctionBuilder().WithFunc(func() {}).Export("").Instantiate(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Ensure the imported function was converted to a listener.
|
||||
|
||||
@@ -155,7 +155,7 @@ func testUserDefinedPrimitiveHostFunc(t *testing.T, r wazero.Runtime) {
|
||||
type f64 float64
|
||||
|
||||
const fn = "fn"
|
||||
hostCompiled, err := r.NewHostModuleBuilder("").NewFunctionBuilder().
|
||||
hostCompiled, err := r.NewHostModuleBuilder("host").NewFunctionBuilder().
|
||||
WithFunc(func(u1 u32, u2 u64, f1 f32, f2 f64) u64 {
|
||||
return u64(u1) + u2 + u64(math.Float32bits(float32(f1))) + u64(math.Float64bits(float64(f2)))
|
||||
}).Export(fn).Compile(testCtx)
|
||||
@@ -164,7 +164,7 @@ func testUserDefinedPrimitiveHostFunc(t *testing.T, r wazero.Runtime) {
|
||||
_, err = r.InstantiateModule(testCtx, hostCompiled, wazero.NewModuleConfig())
|
||||
require.NoError(t, err)
|
||||
|
||||
proxyBin := proxy.NewModuleBinary("", hostCompiled)
|
||||
proxyBin := proxy.NewModuleBinary("host", hostCompiled)
|
||||
|
||||
mod, err := r.Instantiate(testCtx, proxyBin)
|
||||
require.NoError(t, err)
|
||||
@@ -312,7 +312,7 @@ func testHostFuncMemory(t *testing.T, r wazero.Runtime) {
|
||||
return 0
|
||||
}
|
||||
|
||||
host, err := r.NewHostModuleBuilder("").
|
||||
host, err := r.NewHostModuleBuilder("host").
|
||||
NewFunctionBuilder().WithFunc(storeInt).Export("store_int").
|
||||
Instantiate(testCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
(module $test
|
||||
(import "" "store_int"
|
||||
(import "host" "store_int"
|
||||
(func $store_int (param $offset i32) (param $val i64) (result (;errno;) i32)))
|
||||
(memory $memory 1 1)
|
||||
(export "memory" (memory $memory))
|
||||
|
||||
@@ -86,6 +86,10 @@ func requireNoDiff(wasmBin []byte, checkMemory bool, requireNoError func(err err
|
||||
defer interpreter.Close(ctx)
|
||||
|
||||
compilerCompiled, err := compiler.CompileModule(ctx, wasmBin)
|
||||
if err != nil && strings.Contains(err.Error(), "has an empty module name") {
|
||||
// This is the limitation wazero imposes to allow special-casing of anonymous modules.
|
||||
return
|
||||
}
|
||||
requireNoError(err)
|
||||
|
||||
interpreterCompiled, err := interpreter.CompileModule(ctx, wasmBin)
|
||||
@@ -127,6 +131,11 @@ func requireNoDiff(wasmBin []byte, checkMemory bool, requireNoError func(err err
|
||||
func ensureDummyImports(r wazero.Runtime, origin *wasm.Module, requireNoError func(err error)) (skip bool) {
|
||||
impMods := make(map[string][]wasm.Import)
|
||||
for _, imp := range origin.ImportSection {
|
||||
if imp.Module == "" {
|
||||
// Importing empty modules are forbidden as future work will allow multiple anonymous modules.
|
||||
skip = true
|
||||
return
|
||||
}
|
||||
impMods[imp.Module] = append(impMods[imp.Module], imp)
|
||||
}
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ func Test888(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
_, err := r.Instantiate(ctx, imported)
|
||||
_, err := r.InstantiateWithConfig(ctx, imported, wazero.NewModuleConfig().WithName("host"))
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = r.InstantiateWithConfig(ctx, getWasmBinary(t, 888),
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
(module
|
||||
(import "" "s" (memory (;0;) 0 5))
|
||||
(import "" "" (global (;0;) funcref))
|
||||
(import "host" "s" (memory (;0;) 0 5))
|
||||
(import "host" "" (global (;0;) funcref))
|
||||
(global (;1;) (mut f64) f64.const -0x1.1ff6861000008p-652 (;=-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006019210420568196;))
|
||||
(global (;2;) (mut funcref) global.get 0)
|
||||
(global (;3;) (mut i32) i32.const 1000)
|
||||
|
||||
@@ -44,7 +44,12 @@ func TestCallContext_String(t *testing.T) {
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expected, m.String())
|
||||
sm := s.Module(m.Name())
|
||||
if sm != nil {
|
||||
require.Equal(t, tc.expected, s.Module(m.Name()).String())
|
||||
} else {
|
||||
require.Zero(t, len(m.Name()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
@@ -84,7 +85,7 @@ func NewHostModule(
|
||||
if moduleName != "" {
|
||||
m = &Module{NameSection: &NameSection{ModuleName: moduleName}}
|
||||
} else {
|
||||
m = &Module{}
|
||||
return nil, errors.New("a module name must not be empty")
|
||||
}
|
||||
|
||||
if exportCount := uint32(len(nameToGoFunc)); exportCount > 0 {
|
||||
|
||||
@@ -22,18 +22,18 @@ func swap(ctx context.Context, x, y uint32) (uint32, uint32) {
|
||||
}
|
||||
|
||||
func TestNewHostModule(t *testing.T) {
|
||||
swapName := "swap"
|
||||
t.Run("empty name not allowed", func(t *testing.T) {
|
||||
_, err := NewHostModule("", nil, nil, api.CoreFeaturesV2)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
swapName := "swap"
|
||||
tests := []struct {
|
||||
name, moduleName string
|
||||
nameToGoFunc map[string]interface{}
|
||||
funcToNames map[string]*HostFuncNames
|
||||
expected *Module
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
expected: &Module{},
|
||||
},
|
||||
{
|
||||
name: "only name",
|
||||
moduleName: "test",
|
||||
@@ -159,15 +159,17 @@ func TestNewHostModule_Errors(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "not a function",
|
||||
moduleName: "modname",
|
||||
nameToGoFunc: map[string]interface{}{"fn": t},
|
||||
funcToNames: map[string]*HostFuncNames{"fn": {}},
|
||||
expectedErr: "func[.fn] kind != func: ptr",
|
||||
expectedErr: "func[modname.fn] kind != func: ptr",
|
||||
},
|
||||
{
|
||||
name: "function has multiple results",
|
||||
moduleName: "yetanother",
|
||||
nameToGoFunc: map[string]interface{}{"fn": func() (uint32, uint32) { return 0, 0 }},
|
||||
funcToNames: map[string]*HostFuncNames{"fn": {}},
|
||||
expectedErr: "func[.fn] multiple result types invalid as feature \"multi-value\" is disabled",
|
||||
expectedErr: "func[yetanother.fn] multiple result types invalid as feature \"multi-value\" is disabled",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -459,6 +459,9 @@ func (m *Module) validateMemory(memory *Memory, globals []GlobalType, _ api.Core
|
||||
func (m *Module) validateImports(enabledFeatures api.CoreFeatures) error {
|
||||
for i := range m.ImportSection {
|
||||
imp := &m.ImportSection[i]
|
||||
if imp.Module == "" {
|
||||
return fmt.Errorf("import[%d] has an empty module name", i)
|
||||
}
|
||||
switch imp.Type {
|
||||
case ExternTypeGlobal:
|
||||
if !imp.DescGlobal.Mutable {
|
||||
|
||||
@@ -572,6 +572,12 @@ func TestModule_validateImports(t *testing.T) {
|
||||
expectedErr string
|
||||
}{
|
||||
{name: "empty import section"},
|
||||
{
|
||||
name: "reject empty named module",
|
||||
enabledFeatures: api.CoreFeaturesV1,
|
||||
i: &Import{Module: "", Name: "n", Type: ExternTypeFunc, DescFunc: 0},
|
||||
expectedErr: "import[0] has an empty module name",
|
||||
},
|
||||
{
|
||||
name: "func",
|
||||
enabledFeatures: api.CoreFeaturesV1,
|
||||
|
||||
@@ -105,17 +105,17 @@ func TestNewStore(t *testing.T) {
|
||||
|
||||
func TestStore_Instantiate(t *testing.T) {
|
||||
s := newStore()
|
||||
m, err := NewHostModule("", map[string]interface{}{"fn": func() {}}, map[string]*HostFuncNames{"fn": {}}, api.CoreFeaturesV1)
|
||||
m, err := NewHostModule("foo", map[string]interface{}{"fn": func() {}}, map[string]*HostFuncNames{"fn": {}}, api.CoreFeaturesV1)
|
||||
require.NoError(t, err)
|
||||
|
||||
sysCtx := sys.DefaultContext(nil)
|
||||
mod, err := s.Instantiate(testCtx, m, "", sysCtx, []FunctionTypeID{0})
|
||||
mod, err := s.Instantiate(testCtx, m, "bar", sysCtx, []FunctionTypeID{0})
|
||||
require.NoError(t, err)
|
||||
defer mod.Close(testCtx)
|
||||
|
||||
t.Run("CallContext defaults", func(t *testing.T) {
|
||||
require.Equal(t, s.nameToNode[""].module, mod.module)
|
||||
require.Equal(t, s.nameToNode[""].module.Memory, mod.memory)
|
||||
require.Equal(t, s.nameToNode["bar"].module, mod.module)
|
||||
require.Equal(t, s.nameToNode["bar"].module.Memory, mod.memory)
|
||||
require.Equal(t, s, mod.s)
|
||||
require.Equal(t, sysCtx, mod.Sys)
|
||||
})
|
||||
|
||||
@@ -68,6 +68,8 @@ type Runtime interface {
|
||||
// _, err := r.NewHostModuleBuilder("env").
|
||||
// NewFunctionBuilder().WithFunc(hello).Export("hello").
|
||||
// Instantiate(ctx, r)
|
||||
//
|
||||
// Note: empty `moduleName` is not allowed.
|
||||
NewHostModuleBuilder(moduleName string) HostModuleBuilder
|
||||
|
||||
// CompileModule decodes the WebAssembly binary (%.wasm) or errs if invalid.
|
||||
@@ -172,6 +174,9 @@ type runtime struct {
|
||||
|
||||
// Module implements Runtime.Module.
|
||||
func (r *runtime) Module(moduleName string) api.Module {
|
||||
if len(moduleName) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.store.Module(moduleName)
|
||||
}
|
||||
|
||||
|
||||
@@ -413,7 +413,7 @@ func TestRuntime_Instantiate_ErrorOnStart(t *testing.T) {
|
||||
panic(errors.New("ice cream"))
|
||||
}
|
||||
|
||||
host, err := r.NewHostModuleBuilder("").
|
||||
host, err := r.NewHostModuleBuilder("host").
|
||||
NewFunctionBuilder().WithFunc(start).Export("start").
|
||||
Instantiate(testCtx)
|
||||
require.NoError(t, err)
|
||||
@@ -455,6 +455,12 @@ func TestRuntime_InstantiateModule_WithName(t *testing.T) {
|
||||
|
||||
require.Nil(t, internal.Module("0"))
|
||||
require.Equal(t, internal.Module("2"), m2)
|
||||
|
||||
// Empty name module shouldn't be returned via Module() for future optimization.
|
||||
_, err = r.InstantiateModule(testCtx, base, NewModuleConfig().WithName(""))
|
||||
require.NoError(t, err)
|
||||
ret := internal.Module("")
|
||||
require.Nil(t, ret)
|
||||
}
|
||||
|
||||
func TestRuntime_InstantiateModule_ExitError(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user