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:
Takeshi Yoneda
2023-03-15 20:22:37 -07:00
committed by GitHub
parent 8464474e21
commit 8ab1615b53
17 changed files with 81 additions and 35 deletions

View File

@@ -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},

View File

@@ -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.

View File

@@ -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)

View File

@@ -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))

View File

@@ -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)
}

View File

@@ -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),

View File

@@ -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)

View File

@@ -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()))
}
})
}
}

View File

@@ -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 {

View File

@@ -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",
},
}

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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)
})

View File

@@ -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)
}

View File

@@ -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) {