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

Commit b7c7ff9

Browse files
Merge pull request #174 from twitchyliquid64/validationfix
Improve validation for ELSE blocks, fixes #173
2 parents 01d3d33 + 1ea36af commit b7c7ff9

File tree

3 files changed

+193
-75
lines changed

3 files changed

+193
-75
lines changed

validate/error.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ func (e InvalidLabelError) Error() string {
5757
return fmt.Sprintf("invalid nesting depth %d", uint32(e))
5858
}
5959

60+
// UnmatchedIfValueErr is returned if an if block returns a value, but
61+
// no else block is present.
62+
type UnmatchedIfValueErr wasm.ValueType
63+
64+
func (e UnmatchedIfValueErr) Error() string {
65+
return fmt.Sprintf("if block returns value of type %v but no else present", wasm.ValueType(e))
66+
}
67+
6068
// InvalidTableIndexError is returned if a table is referenced with an
6169
// out-of-bounds index.
6270
type InvalidTableIndexError struct {

validate/validate.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,13 @@ func verifyBody(fn *wasm.FunctionSig, body *wasm.FunctionBody, module *wasm.Modu
117117
if err != nil {
118118
return vm, err
119119
}
120-
if frame == nil || frame.op == operators.Call {
120+
switch {
121+
// END should match with a IF/BLOCK/LOOP frame.
122+
case frame == nil || frame.op == operators.Call:
121123
return vm, UnmatchedOpError(op)
124+
// IF block with no else cannot have a result.
125+
case frame.op == operators.If && len(frame.endTypes) > 0:
126+
return vm, UnmatchedIfValueErr(frame.endTypes[0])
122127
}
123128
for _, t := range frame.endTypes {
124129
vm.pushOperand(t)

validate/validate_test.go

Lines changed: 179 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -583,25 +583,6 @@ func TestValidateBlockTypecheck(t *testing.T) {
583583
},
584584
err: UnmatchedOpError(operators.Loop),
585585
},
586-
{
587-
name: "dangling if",
588-
// (i32.const 0) (if) (block (nop))
589-
code: []byte{
590-
operators.I32Const, 0,
591-
operators.If, byte(wasm.BlockTypeEmpty),
592-
operators.Block, byte(wasm.BlockTypeEmpty), operators.Nop, operators.End,
593-
},
594-
err: UnmatchedOpError(operators.If),
595-
},
596-
{
597-
name: "dangling else",
598-
// (block (nop)) (else (nop))
599-
code: []byte{
600-
operators.Block, byte(wasm.BlockTypeEmpty), operators.Nop, operators.End,
601-
operators.Else, operators.Nop, operators.End,
602-
},
603-
err: UnmatchedOpError(operators.Else),
604-
},
605586
{
606587
name: "dangling end",
607588
// (block (nop)) (nop) (end)
@@ -879,31 +860,156 @@ func TestValidateBlockTypecheck(t *testing.T) {
879860
err: nil,
880861
},
881862
{
882-
name: "if i32-i32",
883-
// (i32.const 0) (if (result i32) (i32.const 0)) (drop)
863+
name: "brtable void-void",
864+
// (block (block (i32.const 0) (brtable 0 0 1)))
884865
code: []byte{
866+
operators.Block, byte(wasm.BlockTypeEmpty),
867+
operators.Block, byte(wasm.BlockTypeEmpty),
885868
operators.I32Const, 0,
886-
operators.If, byte(wasm.ValueTypeI32),
869+
operators.BrTable,
870+
/* target count */ 2,
871+
0, 0, 1,
872+
operators.End,
873+
operators.End,
874+
},
875+
err: nil,
876+
},
877+
{
878+
name: "brtable i64-i32",
879+
// (block (return i64) (i64.const 0) (block (return i64) (i32.const 0) (brtable 0 0 1)))
880+
code: []byte{
881+
operators.Block, byte(wasm.ValueTypeI64),
882+
operators.I64Const, 0,
883+
operators.Block, byte(wasm.ValueTypeI32),
887884
operators.I32Const, 0,
885+
operators.BrTable,
886+
/* target count */ 2,
887+
0, 0, 1,
888+
operators.End,
889+
operators.I32Const, 0,
890+
operators.End,
891+
},
892+
err: InvalidTypeError{wasm.ValueTypeI64, wasm.ValueTypeI32},
893+
},
894+
{
895+
name: "brtable invalid default branch",
896+
// (i32.const 0) (br_table 1)
897+
code: []byte{
898+
operators.I32Const, 0,
899+
operators.BrTable,
900+
/* target count */ 0,
901+
1,
902+
},
903+
err: InvalidLabelError(1),
904+
},
905+
{
906+
name: "brtable invalid entry branch",
907+
// (i32.const 0) (br_table 3 0)
908+
code: []byte{
909+
operators.I32Const, 0,
910+
operators.BrTable,
911+
/* target count */ 1,
912+
3, 0,
913+
},
914+
err: InvalidLabelError(3),
915+
},
916+
{
917+
name: "brtable default i32-i32",
918+
// (block (return i32) (i32.const 0) (i32.const 0) (br_table 0 0))
919+
code: []byte{
920+
operators.Block, byte(wasm.ValueTypeI32),
921+
operators.I32Const, 0,
922+
operators.I32Const, 0,
923+
operators.BrTable,
924+
/* target count */ 0,
925+
0,
888926
operators.End,
889927
operators.Drop,
890928
},
891929
err: nil,
892930
},
931+
}
932+
933+
for i := range tcs {
934+
tc := tcs[i]
935+
t.Run(tc.name, func(t *testing.T) {
936+
t.Parallel()
937+
938+
mod := wasm.Module{}
939+
sig := wasm.FunctionSig{Form: 0x60 /* Must always be 0x60 */}
940+
fn := wasm.FunctionBody{Module: &mod, Code: tc.code}
941+
942+
_, err := verifyBody(&sig, &fn, &mod)
943+
if err != tc.err {
944+
t.Fatalf("verify returned '%v', want '%v'", err, tc.err)
945+
}
946+
})
947+
}
948+
}
949+
950+
func TestValidateIfBlock(t *testing.T) {
951+
tcs := []struct {
952+
name string
953+
code []byte
954+
err error
955+
}{
893956
{
894-
name: "if else i32-i32-i32",
895-
// (i32.const 0) (if (result i32) (i32.const 0) (else (i32.const 1))) (drop)
957+
name: "if nominal",
958+
// (i32.const 0) (if (nop)
896959
code: []byte{
897960
operators.I32Const, 0,
898-
operators.If, byte(wasm.ValueTypeI32),
961+
operators.If, byte(wasm.BlockTypeEmpty),
962+
operators.Nop,
963+
operators.End,
964+
},
965+
err: nil,
966+
},
967+
{
968+
name: "if else nominal",
969+
// (i32.const 0) (if (nop) (else (nop))
970+
code: []byte{
899971
operators.I32Const, 0,
972+
operators.If, byte(wasm.BlockTypeEmpty),
973+
operators.Nop,
900974
operators.Else,
901-
operators.I32Const, 1,
975+
operators.Nop,
976+
operators.End,
977+
},
978+
err: nil,
979+
},
980+
{
981+
name: "if else with value nominal",
982+
// (i32.const 0) (if (result i64) (i64.const 1) (else (i64.const 2)) (drop)
983+
code: []byte{
984+
operators.I32Const, 0,
985+
operators.If, byte(wasm.ValueTypeI64),
986+
operators.I64Const, 1,
987+
operators.Else,
988+
operators.I64Const, 2,
902989
operators.End,
903990
operators.Drop,
904991
},
905992
err: nil,
906993
},
994+
{
995+
name: "dangling if",
996+
// (i32.const 0) (if) (block (nop))
997+
code: []byte{
998+
operators.I32Const, 0,
999+
operators.If, byte(wasm.BlockTypeEmpty),
1000+
operators.Block, byte(wasm.BlockTypeEmpty), operators.Nop, operators.End,
1001+
},
1002+
err: UnmatchedOpError(operators.If),
1003+
},
1004+
{
1005+
name: "dangling else",
1006+
// (block (nop)) (else (nop))
1007+
code: []byte{
1008+
operators.Block, byte(wasm.BlockTypeEmpty), operators.Nop, operators.End,
1009+
operators.Else, operators.Nop, operators.End,
1010+
},
1011+
err: UnmatchedOpError(operators.Else),
1012+
},
9071013
{
9081014
name: "if else i32-i32-i64",
9091015
// (i32.const 0) (if (result i32) (i32.const 0) (else (i64.const 1))) (drop)
@@ -919,96 +1025,95 @@ func TestValidateBlockTypecheck(t *testing.T) {
9191025
err: InvalidTypeError{wasm.ValueTypeI32, wasm.ValueTypeI64},
9201026
},
9211027
{
922-
name: "if void-i32",
923-
// (i32.const 0) (if (result i32) (i32.const 0)) (drop)
1028+
name: "if else i64-i32-i64",
1029+
// (i32.const 0) (if (result i64) (i32.const 0) (else (i64.const 1))) (drop)
9241030
code: []byte{
9251031
operators.I32Const, 0,
926-
operators.If, byte(wasm.BlockTypeEmpty),
1032+
operators.If, byte(wasm.ValueTypeI64),
9271033
operators.I32Const, 0,
1034+
operators.Else,
1035+
operators.I64Const, 1,
9281036
operators.End,
9291037
operators.Drop,
9301038
},
931-
err: UnbalancedStackErr(wasm.ValueTypeI32),
1039+
err: InvalidTypeError{wasm.ValueTypeI64, wasm.ValueTypeI32},
9321040
},
9331041
{
934-
name: "if i32-void",
935-
// (i32.const 0) (if (nop))
1042+
name: "if else i64-i32-i32",
1043+
// (i32.const 0) (if (result i64) (i32.const 0) (else (i64.const 1))) (drop)
9361044
code: []byte{
9371045
operators.I32Const, 0,
938-
operators.If, byte(wasm.ValueTypeI32),
939-
operators.Nop,
1046+
operators.If, byte(wasm.ValueTypeI64),
1047+
operators.I32Const, 0,
1048+
operators.Else,
1049+
operators.I32Const, 1,
9401050
operators.End,
1051+
operators.Drop,
9411052
},
942-
err: ErrStackUnderflow,
1053+
err: InvalidTypeError{wasm.ValueTypeI64, wasm.ValueTypeI32},
9431054
},
9441055
{
945-
name: "brtable void-void",
946-
// (block (block (i32.const 0) (brtable 0 0 1)))
1056+
name: "if void-i32",
1057+
// (i32.const 0) (if (result i32) (i32.const 0)) (drop)
9471058
code: []byte{
948-
operators.Block, byte(wasm.BlockTypeEmpty),
949-
operators.Block, byte(wasm.BlockTypeEmpty),
9501059
operators.I32Const, 0,
951-
operators.BrTable,
952-
/* target count */ 2,
953-
0, 0, 1,
954-
operators.End,
1060+
operators.If, byte(wasm.BlockTypeEmpty),
1061+
operators.I32Const, 0,
9551062
operators.End,
1063+
operators.Drop,
9561064
},
957-
err: nil,
1065+
err: UnbalancedStackErr(wasm.ValueTypeI32),
9581066
},
9591067
{
960-
name: "brtable i64-i32",
961-
// (block (return i64) (i64.const 0) (block (return i64) (i32.const 0) (brtable 0 0 1)))
1068+
name: "if i32-void",
1069+
// (i32.const 0) (if (result i32) (nop))
9621070
code: []byte{
963-
operators.Block, byte(wasm.ValueTypeI64),
964-
operators.I64Const, 0,
965-
operators.Block, byte(wasm.ValueTypeI32),
966-
operators.I32Const, 0,
967-
operators.BrTable,
968-
/* target count */ 2,
969-
0, 0, 1,
970-
operators.End,
9711071
operators.I32Const, 0,
1072+
operators.If, byte(wasm.ValueTypeI32),
1073+
operators.Nop,
9721074
operators.End,
9731075
},
974-
err: InvalidTypeError{wasm.ValueTypeI64, wasm.ValueTypeI32},
1076+
err: ErrStackUnderflow,
9751077
},
9761078
{
977-
name: "brtable invalid default branch",
978-
// (i32.const 0) (br_table 1)
1079+
name: "if i32 missing else",
1080+
// (i32.const 0) (if (nop))
9791081
code: []byte{
9801082
operators.I32Const, 0,
981-
operators.BrTable,
982-
/* target count */ 0,
983-
1,
1083+
operators.If, byte(wasm.ValueTypeI32),
1084+
operators.I32Const, 0,
1085+
operators.End,
1086+
operators.Drop,
9841087
},
985-
err: InvalidLabelError(1),
1088+
err: UnmatchedIfValueErr(wasm.ValueTypeI32),
9861089
},
9871090
{
988-
name: "brtable invalid entry branch",
989-
// (i32.const 0) (br_table 3 0)
1091+
name: "if with else missing main block result",
1092+
// (i32.const 0) (if (result i32) (nop) else (i32.const 0))
9901093
code: []byte{
9911094
operators.I32Const, 0,
992-
operators.BrTable,
993-
/* target count */ 1,
994-
3, 0,
1095+
operators.If, byte(wasm.ValueTypeI32),
1096+
operators.Nop,
1097+
operators.Else,
1098+
operators.I32Const, 0,
1099+
operators.End,
1100+
operators.Drop,
9951101
},
996-
err: InvalidLabelError(3),
1102+
err: ErrStackUnderflow,
9971103
},
9981104
{
999-
name: "brtable default i32-i32",
1000-
// (block (return i32) (i32.const 0) (i32.const 0) (br_table 0 0))
1105+
name: "if with else missing else block result",
1106+
// (i32.const 0) (if (result i32) (i32.const 0) else (nop))
10011107
code: []byte{
1002-
operators.Block, byte(wasm.ValueTypeI32),
10031108
operators.I32Const, 0,
1109+
operators.If, byte(wasm.ValueTypeI32),
10041110
operators.I32Const, 0,
1005-
operators.BrTable,
1006-
/* target count */ 0,
1007-
0,
1111+
operators.Else,
1112+
operators.Nop,
10081113
operators.End,
10091114
operators.Drop,
10101115
},
1011-
err: nil,
1116+
err: ErrStackUnderflow,
10121117
},
10131118
}
10141119

0 commit comments

Comments
 (0)