This changes file descriptors from uint32 to int32 and the corresponding file table to reject negative values. This ensures invalid values aren't mistaken for very large descriptor entries, which can use a lot of memory as the table implementation isn't designed to be sparse. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html#tag_16_90 Signed-off-by: Adrian Cole <adrian@tetrate.io>
181 lines
4.7 KiB
Go
181 lines
4.7 KiB
Go
// Package goarch isolates code from runtime.GOARCH=wasm in a way that avoids
|
|
// cyclic dependencies when re-used from other packages.
|
|
package goarch
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
|
|
"github.com/tetratelabs/wazero/api"
|
|
"github.com/tetratelabs/wazero/internal/gojs/custom"
|
|
"github.com/tetratelabs/wazero/internal/gojs/util"
|
|
"github.com/tetratelabs/wazero/internal/wasm"
|
|
)
|
|
|
|
// StubFunction stubs functions not used in Go's main source tree.
|
|
// This traps (unreachable opcode) to ensure the function is never called.
|
|
func StubFunction(name string) *wasm.HostFunc {
|
|
return &wasm.HostFunc{
|
|
ExportName: name,
|
|
Name: name,
|
|
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
|
ParamNames: []string{"sp"},
|
|
Code: wasm.Code{GoFunc: api.GoModuleFunc(func(ctx context.Context, _ api.Module, stack []uint64) {})},
|
|
}
|
|
}
|
|
|
|
var le = binary.LittleEndian
|
|
|
|
type Stack interface {
|
|
// Name is the function name being invoked.
|
|
Name() string
|
|
|
|
Param(i int) uint64
|
|
|
|
// ParamBytes reads a byte slice, given its memory offset and length (stack
|
|
// positions i, i+1)
|
|
ParamBytes(mem api.Memory, i int) []byte
|
|
|
|
// ParamString reads a string, given its memory offset and length (stack
|
|
// positions i, i+1)
|
|
ParamString(mem api.Memory, i int) string
|
|
|
|
ParamInt32(i int) int32
|
|
|
|
ParamUint32(i int) uint32
|
|
|
|
// Refresh the stack from the current stack pointer (SP).
|
|
//
|
|
// Note: This is needed prior to storing a value when in an operation that
|
|
// can trigger a Go event handler.
|
|
Refresh(api.Module)
|
|
|
|
SetResult(i int, v uint64)
|
|
|
|
SetResultBool(i int, v bool)
|
|
|
|
SetResultI32(i int, v int32)
|
|
|
|
SetResultI64(i int, v int64)
|
|
|
|
SetResultUint32(i int, v uint32)
|
|
}
|
|
|
|
func NewStack(name string, mem api.Memory, sp uint32) Stack {
|
|
names := custom.NameSection[name]
|
|
s := &stack{name: name, paramCount: len(names.ParamNames), resultCount: len(names.ResultNames)}
|
|
s.refresh(mem, sp)
|
|
return s
|
|
}
|
|
|
|
type stack struct {
|
|
name string
|
|
paramCount, resultCount int
|
|
buf []byte
|
|
}
|
|
|
|
// Name implements Stack.Name
|
|
func (s *stack) Name() string {
|
|
return s.name
|
|
}
|
|
|
|
// Param implements Stack.Param
|
|
func (s *stack) Param(i int) (res uint64) {
|
|
pos := i << 3
|
|
res = le.Uint64(s.buf[pos:])
|
|
return
|
|
}
|
|
|
|
// ParamBytes implements Stack.ParamBytes
|
|
func (s *stack) ParamBytes(mem api.Memory, i int) (res []byte) {
|
|
offset := s.ParamUint32(i)
|
|
byteCount := s.ParamUint32(i + 1)
|
|
return util.MustRead(mem, s.name, i, offset, byteCount)
|
|
}
|
|
|
|
// ParamString implements Stack.ParamString
|
|
func (s *stack) ParamString(mem api.Memory, i int) string {
|
|
return string(s.ParamBytes(mem, i)) // safe copy of guest memory
|
|
}
|
|
|
|
// ParamInt32 implements Stack.ParamInt32
|
|
func (s *stack) ParamInt32(i int) int32 {
|
|
return int32(s.Param(i))
|
|
}
|
|
|
|
// ParamUint32 implements Stack.ParamUint32
|
|
func (s *stack) ParamUint32(i int) uint32 {
|
|
return uint32(s.Param(i))
|
|
}
|
|
|
|
// Refresh implements Stack.Refresh
|
|
func (s *stack) Refresh(mod api.Module) {
|
|
s.refresh(mod.Memory(), GetSP(mod))
|
|
}
|
|
|
|
func (s *stack) refresh(mem api.Memory, sp uint32) {
|
|
count := uint32(s.paramCount + s.resultCount)
|
|
buf, ok := mem.Read(sp+8, count<<3)
|
|
if !ok {
|
|
panic("out of memory reading stack")
|
|
}
|
|
s.buf = buf
|
|
}
|
|
|
|
// SetResult implements Stack.SetResult
|
|
func (s *stack) SetResult(i int, v uint64) {
|
|
pos := (s.paramCount + i) << 3
|
|
le.PutUint64(s.buf[pos:], v)
|
|
}
|
|
|
|
// SetResultBool implements Stack.SetResultBool
|
|
func (s *stack) SetResultBool(i int, v bool) {
|
|
if v {
|
|
s.SetResultUint32(i, 1)
|
|
} else {
|
|
s.SetResultUint32(i, 0)
|
|
}
|
|
}
|
|
|
|
// SetResultI32 implements Stack.SetResultI32
|
|
func (s *stack) SetResultI32(i int, v int32) {
|
|
s.SetResult(i, uint64(v))
|
|
}
|
|
|
|
// SetResultI64 implements Stack.SetResultI64
|
|
func (s *stack) SetResultI64(i int, v int64) {
|
|
s.SetResult(i, uint64(v))
|
|
}
|
|
|
|
// SetResultUint32 implements Stack.SetResultUint32
|
|
func (s *stack) SetResultUint32(i int, v uint32) {
|
|
s.SetResult(i, uint64(v))
|
|
}
|
|
|
|
// GetSP gets the stack pointer, which is needed prior to storing a value when
|
|
// in an operation that can trigger a Go event handler.
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L210-L213
|
|
func GetSP(mod api.Module) uint32 {
|
|
// Cheat by reading global[0] directly instead of through a function proxy.
|
|
// https://github.com/golang/go/blob/go1.20/src/runtime/rt0_js_wasm.s#L87-L90
|
|
return uint32(mod.(*wasm.ModuleInstance).GlobalVal(0))
|
|
}
|
|
|
|
func NewFunc(name string, goFunc Func) *wasm.HostFunc {
|
|
return util.NewFunc(name, (&stackFunc{name: name, f: goFunc}).Call)
|
|
}
|
|
|
|
type Func func(context.Context, api.Module, Stack)
|
|
|
|
type stackFunc struct {
|
|
name string
|
|
f Func
|
|
}
|
|
|
|
// Call implements the same method as defined on api.GoModuleFunction.
|
|
func (f *stackFunc) Call(ctx context.Context, mod api.Module, wasmStack []uint64) {
|
|
s := NewStack(f.name, mod.Memory(), uint32(wasmStack[0]))
|
|
f.f(ctx, mod, s)
|
|
}
|