Skip to content
This repository was archived by the owner on May 11, 2020. It is now read-only.

Commit 7bc5f1f

Browse files
twitchyliquid64sbinet
authored andcommitted
validate: test suite & fixes for validate package
* Refactor validate module to use spec-defined algorithm #143 * Start implementing test suite for validate package * Disable validate debugging, add doc strings for error types * Implement more tests for branching, locals, typechecking * Add tests for call, call_indirect * Add tests for memory,*_global,*_local * Add missing validation for unmatched frames at end of function * Add license header, Errorf -> Fatalf
1 parent cbbdf08 commit 7bc5f1f

File tree

4 files changed

+1323
-18
lines changed

4 files changed

+1323
-18
lines changed

validate/error.go

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
ops "github.com/go-interpreter/wagon/wasm/operators"
1313
)
1414

15+
// Error wraps validation errors with information about where the error
16+
// was encountered.
1517
type Error struct {
1618
Offset int // Byte offset in the bytecode vector where the error occurs.
1719
Function int // Index into the function index space for the offending function.
@@ -22,53 +24,91 @@ func (e Error) Error() string {
2224
return fmt.Sprintf("error while validating function %d at offset %d: %v", e.Function, e.Offset, e.Err)
2325
}
2426

27+
// ErrStackUnderflow is returned if an instruction consumes a value, but there
28+
// are no values on the stack.
2529
var ErrStackUnderflow = errors.New("validate: stack underflow")
2630

31+
// InvalidImmediateError is returned if the immediate value provided
32+
// is invalid for the given instruction.
2733
type InvalidImmediateError struct {
2834
ImmType string
2935
OpName string
3036
}
3137

3238
func (e InvalidImmediateError) Error() string {
33-
return fmt.Sprintf("invalid immediate for op %s at (should be %s)", e.OpName, e.ImmType)
39+
return fmt.Sprintf("invalid immediate for op %s (should be %s)", e.OpName, e.ImmType)
3440
}
3541

42+
// UnmatchedOpError is returned if a block does not have a corresponding
43+
// end instruction, or if an else instruction is encountered outside of
44+
// an if block.
3645
type UnmatchedOpError byte
3746

3847
func (e UnmatchedOpError) Error() string {
3948
n1, _ := ops.New(byte(e))
4049
return fmt.Sprintf("encountered unmatched %s", n1.Name)
4150
}
4251

52+
// InvalidTypeError is returned if a branch is encountered which points to
53+
// a block that does not exist.
4354
type InvalidLabelError uint32
4455

4556
func (e InvalidLabelError) Error() string {
4657
return fmt.Sprintf("invalid nesting depth %d", uint32(e))
4758
}
4859

60+
// InvalidTableIndexError is returned if a table is referenced with an
61+
// out-of-bounds index.
62+
type InvalidTableIndexError struct {
63+
table string
64+
index uint32
65+
}
66+
67+
func (e InvalidTableIndexError) Error() string {
68+
return fmt.Sprintf("invalid index %d for %s", e.index, e.table)
69+
}
70+
71+
// InvalidLocalIndexError is returned if a local variable index is referenced
72+
// which does not exist.
4973
type InvalidLocalIndexError uint32
5074

5175
func (e InvalidLocalIndexError) Error() string {
5276
return fmt.Sprintf("invalid index for local variable %d", uint32(e))
5377
}
5478

79+
// InvalidTypeError is returned if there is a mismatch between the type(s)
80+
// an operator or function accepts, and the value provided.
5581
type InvalidTypeError struct {
5682
Wanted wasm.ValueType
5783
Got wasm.ValueType
5884
}
5985

60-
func (e InvalidTypeError) Error() string {
61-
return fmt.Sprintf("invalid type, got: %v, wanted: %v", e.Got, e.Wanted)
86+
func valueTypeStr(v wasm.ValueType) string {
87+
switch v {
88+
case noReturn:
89+
return "void"
90+
case unknownType:
91+
return "anytype"
92+
default:
93+
return v.String()
94+
}
6295
}
6396

64-
type InvalidElementIndexError uint32
65-
66-
func (e InvalidElementIndexError) Error() string {
67-
return fmt.Sprintf("invalid element index %d", uint32(e))
97+
func (e InvalidTypeError) Error() string {
98+
return fmt.Sprintf("invalid type, got: %v, wanted: %v", valueTypeStr(e.Got), valueTypeStr(e.Wanted))
6899
}
69100

101+
// NoSectionError is returned if a section does not exist.
70102
type NoSectionError wasm.SectionID
71103

72104
func (e NoSectionError) Error() string {
73105
return fmt.Sprintf("reference to non existant section (id %d) in module", wasm.SectionID(e))
74106
}
107+
108+
// UnbalancedStackErr is returned if there are too many items on the stack
109+
// than is valid for the current block or function.
110+
type UnbalancedStackErr wasm.ValueType
111+
112+
func (e UnbalancedStackErr) Error() string {
113+
return fmt.Sprintf("unbalanced stack (top of stack is %s)", valueTypeStr(wasm.ValueType(e)))
114+
}

validate/validate.go

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// package validate provides functions for validating WebAssembly modules.
5+
// Package validate provides functions for validating WebAssembly modules.
66
package validate
77

88
import (
@@ -11,6 +11,7 @@ import (
1111
"io"
1212

1313
"github.com/go-interpreter/wagon/wasm"
14+
"github.com/go-interpreter/wagon/wasm/operators"
1415
ops "github.com/go-interpreter/wagon/wasm/operators"
1516
)
1617

@@ -23,7 +24,9 @@ func verifyBody(fn *wasm.FunctionSig, body *wasm.FunctionBody, module *wasm.Modu
2324

2425
ctrlFrames: []frame{
2526
// The outermost frame is the function itself.
26-
{endTypes: fn.ReturnTypes},
27+
// endTypes is not populated as function return types
28+
// are validated separately.
29+
{op: operators.Call},
2730
},
2831
curFunc: fn,
2932
}
@@ -109,17 +112,16 @@ func verifyBody(fn *wasm.FunctionSig, body *wasm.FunctionBody, module *wasm.Modu
109112
vm.pushFrame(op, frame.endTypes, frame.endTypes)
110113

111114
case ops.End:
115+
// Block 'return' type is validated in popFrame().
112116
frame, err := vm.popFrame()
113117
if err != nil {
114118
return vm, err
115119
}
116-
if frame == nil {
120+
if frame == nil || frame.op == operators.Call {
117121
return vm, UnmatchedOpError(op)
118122
}
119-
for _, ret := range frame.endTypes {
120-
if ret != noReturn {
121-
vm.pushOperand(ret)
122-
}
123+
for _, t := range frame.endTypes {
124+
vm.pushOperand(t)
123125
}
124126

125127
case ops.Br:
@@ -322,7 +324,7 @@ func verifyBody(fn *wasm.FunctionSig, body *wasm.FunctionBody, module *wasm.Modu
322324
case ops.I32Load, ops.I64Load, ops.F32Load, ops.F64Load, ops.I32Load8s, ops.I32Load8u, ops.I32Load16s, ops.I32Load16u, ops.I64Load8s, ops.I64Load8u, ops.I64Load16s, ops.I64Load16u, ops.I64Load32s, ops.I64Load32u, ops.I32Store, ops.I64Store, ops.F32Store, ops.F64Store, ops.I32Store8, ops.I32Store16, ops.I64Store8, ops.I64Store16, ops.I64Store32:
323325
// read memory_immediate
324326
// flags
325-
_, err := vm.fetchVarUint()
327+
align, err := vm.fetchVarUint()
326328
if err != nil {
327329
return vm, err
328330
}
@@ -332,13 +334,32 @@ func verifyBody(fn *wasm.FunctionSig, body *wasm.FunctionBody, module *wasm.Modu
332334
return vm, err
333335
}
334336

337+
switch op {
338+
case ops.I32Load8s, ops.I32Load8u, ops.I64Load8s, ops.I64Load8u:
339+
if align > 1 {
340+
return vm, InvalidImmediateError{OpName: opStruct.Name, ImmType: "naturally aligned"}
341+
}
342+
case ops.I32Load16s, ops.I32Load16u, ops.I64Load16s, ops.I64Load16u:
343+
if align > 2 {
344+
return vm, InvalidImmediateError{OpName: opStruct.Name, ImmType: "naturally aligned"}
345+
}
346+
case ops.I32Load, ops.I64Load32s, ops.I64Load32u, ops.F32Load:
347+
if align > 4 {
348+
return vm, InvalidImmediateError{OpName: opStruct.Name, ImmType: "naturally aligned"}
349+
}
350+
case ops.I64Load, ops.F64Load:
351+
if align > 8 {
352+
return vm, InvalidImmediateError{OpName: opStruct.Name, ImmType: "naturally aligned"}
353+
}
354+
}
355+
335356
case ops.CurrentMemory, ops.GrowMemory:
336357
memIndex, err := vm.fetchByte()
337358
if err != nil {
338359
return vm, err
339360
}
340361
if memIndex != 0x00 {
341-
return vm, errors.New("validate: memory index must be 0")
362+
return vm, InvalidTableIndexError{"memory", uint32(memIndex)}
342363
}
343364

344365
case ops.Call:
@@ -393,7 +414,7 @@ func verifyBody(fn *wasm.FunctionSig, body *wasm.FunctionBody, module *wasm.Modu
393414
return vm, err
394415
}
395416
if tableIndex != 0x00 {
396-
return vm, errors.New("validate: table index in call_indirect must be 0")
417+
return vm, InvalidTableIndexError{"table", uint32(tableIndex)}
397418
}
398419

399420
if index >= uint32(len(module.Types.Entries)) {
@@ -456,6 +477,34 @@ func verifyBody(fn *wasm.FunctionSig, body *wasm.FunctionBody, module *wasm.Modu
456477
}
457478
}
458479

480+
switch len(fn.ReturnTypes) {
481+
case 0:
482+
op, err := vm.popOperand()
483+
switch {
484+
case !vm.topFrameUnreachable() && err == nil:
485+
return vm, UnbalancedStackErr(op.Type)
486+
case err == ErrStackUnderflow:
487+
default:
488+
return vm, err
489+
}
490+
case 1:
491+
op, err := vm.popOperand()
492+
if err != nil {
493+
return vm, err
494+
}
495+
if !op.Equal(fn.ReturnTypes[0]) {
496+
return vm, InvalidTypeError{fn.ReturnTypes[0], op.Type}
497+
}
498+
}
499+
500+
f, err := vm.popFrame()
501+
if err != nil {
502+
return vm, err
503+
}
504+
if f.op != operators.Call {
505+
return vm, UnmatchedOpError(f.op)
506+
}
507+
459508
return vm, nil
460509
}
461510

0 commit comments

Comments
 (0)