Files
wazero/internal/gojs/goarch/wasm.go
Crypt Keeper 40341af448 fs: returns EBADF on negative file descriptor (#1391)
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>
2023-04-21 16:08:35 +02:00

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