// Copyright 2017 The go-interpreter Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package exec import ( "errors" "math" ) // ErrOutOfBoundsMemoryAccess is the error value used while trapping the VM // when it detects an out of bounds access to the linear memory. var ErrOutOfBoundsMemoryAccess = errors.New("exec: out of bounds memory access") func (vm *VM) fetchBaseAddr() int { return int(vm.fetchUint32() + uint32(vm.popInt32())) } // inBounds returns true when the next vm.fetchBaseAddr() + offset // indices are in bounds accesses to the linear memory. func (vm *VM) inBounds(offset int) bool { addr := endianess.Uint32(vm.ctx.code[vm.ctx.pc:]) + uint32(vm.ctx.stack[len(vm.ctx.stack)-1]) return int(addr)+offset < len(vm.memory) } // curMem returns a slice to the memeory segment pointed to by // the current base address on the bytecode stream. func (vm *VM) curMem() []byte { return vm.memory[vm.fetchBaseAddr():] } func (vm *VM) i32Load() { if !vm.inBounds(3) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushUint32(endianess.Uint32(vm.curMem())) } func (vm *VM) i32Load8s() { if !vm.inBounds(0) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushInt32(int32(int8(vm.memory[vm.fetchBaseAddr()]))) } func (vm *VM) i32Load8u() { if !vm.inBounds(0) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushUint32(uint32(uint8(vm.memory[vm.fetchBaseAddr()]))) } func (vm *VM) i32Load16s() { if !vm.inBounds(1) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushInt32(int32(int16(endianess.Uint16(vm.curMem())))) } func (vm *VM) i32Load16u() { if !vm.inBounds(1) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushUint32(uint32(endianess.Uint16(vm.curMem()))) } func (vm *VM) i64Load() { if !vm.inBounds(7) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushUint64(endianess.Uint64(vm.curMem())) } func (vm *VM) i64Load8s() { if !vm.inBounds(0) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushInt64(int64(int8(vm.memory[vm.fetchBaseAddr()]))) } func (vm *VM) i64Load8u() { if !vm.inBounds(0) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushUint64(uint64(uint8(vm.memory[vm.fetchBaseAddr()]))) } func (vm *VM) i64Load16s() { if !vm.inBounds(1) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushInt64(int64(int16(endianess.Uint16(vm.curMem())))) } func (vm *VM) i64Load16u() { if !vm.inBounds(1) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushUint64(uint64(endianess.Uint16(vm.curMem()))) } func (vm *VM) i64Load32s() { if !vm.inBounds(3) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushInt64(int64(int32(endianess.Uint32(vm.curMem())))) } func (vm *VM) i64Load32u() { if !vm.inBounds(3) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushUint64(uint64(endianess.Uint32(vm.curMem()))) } func (vm *VM) f32Store() { v := math.Float32bits(vm.popFloat32()) if !vm.inBounds(3) { panic(ErrOutOfBoundsMemoryAccess) } endianess.PutUint32(vm.curMem(), v) } func (vm *VM) f32Load() { if !vm.inBounds(3) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushFloat32(math.Float32frombits(endianess.Uint32(vm.curMem()))) } func (vm *VM) f64Store() { v := math.Float64bits(vm.popFloat64()) if !vm.inBounds(7) { panic(ErrOutOfBoundsMemoryAccess) } endianess.PutUint64(vm.curMem(), v) } func (vm *VM) f64Load() { if !vm.inBounds(7) { panic(ErrOutOfBoundsMemoryAccess) } vm.pushFloat64(math.Float64frombits(endianess.Uint64(vm.curMem()))) } func (vm *VM) i32Store() { v := vm.popUint32() if !vm.inBounds(3) { panic(ErrOutOfBoundsMemoryAccess) } endianess.PutUint32(vm.curMem(), v) } func (vm *VM) i32Store8() { v := byte(uint8(vm.popUint32())) if !vm.inBounds(0) { panic(ErrOutOfBoundsMemoryAccess) } vm.memory[vm.fetchBaseAddr()] = v } func (vm *VM) i32Store16() { v := uint16(vm.popUint32()) if !vm.inBounds(1) { panic(ErrOutOfBoundsMemoryAccess) } endianess.PutUint16(vm.curMem(), v) } func (vm *VM) i64Store() { v := vm.popUint64() if !vm.inBounds(7) { panic(ErrOutOfBoundsMemoryAccess) } endianess.PutUint64(vm.curMem(), v) } func (vm *VM) i64Store8() { v := byte(uint8(vm.popUint64())) if !vm.inBounds(0) { panic(ErrOutOfBoundsMemoryAccess) } vm.memory[vm.fetchBaseAddr()] = v } func (vm *VM) i64Store16() { v := uint16(vm.popUint64()) if !vm.inBounds(1) { panic(ErrOutOfBoundsMemoryAccess) } endianess.PutUint16(vm.curMem(), v) } func (vm *VM) i64Store32() { v := uint32(vm.popUint64()) if !vm.inBounds(3) { panic(ErrOutOfBoundsMemoryAccess) } endianess.PutUint32(vm.curMem(), v) } func (vm *VM) currentMemory() { _ = vm.fetchInt8() // reserved (https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/BinaryEncoding.md#memory-related-operators-described-here) vm.pushInt32(int32(len(vm.memory) / wasmPageSize)) } func (vm *VM) growMemory() { _ = vm.fetchInt8() // reserved (https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/BinaryEncoding.md#memory-related-operators-described-here) curLen := len(vm.memory) / wasmPageSize n := vm.popInt32() vm.memory = append(vm.memory, make([]byte, n*wasmPageSize)...) vm.pushInt32(int32(curLen)) }