diff --git a/interpreter/README.md b/interpreter/README.md index 0cda408c..ed300410 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -395,6 +395,36 @@ The `input` and `output` meta commands determine the requested file format from The interpreter supports a "dry" mode (flag `-d`), in which modules are only validated. In this mode, all actions and assertions are ignored. It also supports an "unchecked" mode (flag `-u`), in which module definitions are not validated before use. + +### Spectest host module + +When running scripts, the interpreter predefines a simple host module named `"spectest"` that has the following module type: +``` +(module + (global (export "global_i32") i32) + (global (export "global_i64") i64) + (global (export "global_f32") f32) + (global (export "global_f64") f64) + + (table (export "table") 10 20 funcref) + + (memory (export "memory") 1 2) + + (func (export "print")) + (func (export "print_i32") (param i32)) + (func (export "print_i64") (param i64)) + (func (export "print_f32") (param f32)) + (func (export "print_f64") (param f64)) + (func (export "print_i32_f32") (param i32 f32)) + (func (export "print_f64_f64") (param f64 f64)) +) +``` +The `print` functions are assumes to print their respective argument values to stdout (followed by a newline) and can be used to produce observable output. + +Note: This module predates the `register` command and should no longer be needed for new tests. +We might remove it in the future, so consider it deprecated. + + ### Binary Scripts The grammar of binary scripts is a subset of the grammar for general scripts: diff --git a/interpreter/host/spectest.ml b/interpreter/host/spectest.ml index 8d67b3b7..77b71160 100644 --- a/interpreter/host/spectest.ml +++ b/interpreter/host/spectest.ml @@ -38,13 +38,15 @@ let lookup name t = match Utf8.encode name, t with | "print", _ -> ExternFunc (func print (FuncType ([], []))) | "print_i32", _ -> ExternFunc (func print (FuncType ([NumType I32Type], []))) + | "print_i64", _ -> ExternFunc (func print (FuncType ([NumType I64Type], []))) + | "print_f32", _ -> ExternFunc (func print (FuncType ([NumType F32Type], []))) + | "print_f64", _ -> ExternFunc (func print (FuncType ([NumType F64Type], []))) | "print_i32_f32", _ -> ExternFunc (func print (FuncType ([NumType I32Type; NumType F32Type], []))) | "print_f64_f64", _ -> ExternFunc (func print (FuncType ([NumType F64Type; NumType F64Type], []))) - | "print_f32", _ -> ExternFunc (func print (FuncType ([NumType F32Type], []))) - | "print_f64", _ -> ExternFunc (func print (FuncType ([NumType F64Type], []))) | "global_i32", _ -> ExternGlobal (global (GlobalType (NumType I32Type, Immutable))) + | "global_i64", _ -> ExternGlobal (global (GlobalType (NumType I64Type, Immutable))) | "global_f32", _ -> ExternGlobal (global (GlobalType (NumType F32Type, Immutable))) | "global_f64", _ -> ExternGlobal (global (GlobalType (NumType F64Type, Immutable))) | "table", _ -> ExternTable table diff --git a/test/core/binary-leb128.wast b/test/core/binary-leb128.wast index 6e015e3d..1d672195 100644 --- a/test/core/binary-leb128.wast +++ b/test/core/binary-leb128.wast @@ -853,7 +853,7 @@ "\41\00" ;; i32.const 0 "\41\03" ;; i32.const 3 "\36" ;; i32.store - "\03" ;; alignment 2 + "\02" ;; alignment 2 "\82\80\80\80\10" ;; offset 2 with unused bits set "\0b" ;; end ) diff --git a/test/core/binary.wast b/test/core/binary.wast index 238aaa91..7ba4064b 100644 --- a/test/core/binary.wast +++ b/test/core/binary.wast @@ -160,6 +160,19 @@ "\41\00\0b\00\00" ;; (i32.const 0) with no elements ) +;; Type section with signed LEB128 encoded type +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01" ;; Type section id + "\05" ;; Type section length + "\01" ;; Types vector length + "\e0\7f" ;; Malformed functype, -0x20 in signed LEB128 encoding + "\00\00" + ) + "integer representation too long" +) + ;; Unsigned LEB128 must not be overlong (assert_malformed (module binary @@ -929,7 +942,24 @@ "zero byte expected" ) -;; No more than 2^32 locals. +;; Local number is unsigned 32 bit +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\80\80\80\80\10\7f" ;; 0x100000000 i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "integer too large" +) + +;; No more than 2^32-1 locals. (assert_malformed (module binary "\00asm" "\01\00\00\00" @@ -946,6 +976,24 @@ "too many locals" ) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\06\01\60\02\7f\7f\00" ;; Type section: (param i32 i32) + "\03\02\01\00" ;; Function section + "\0a\1c\01" ;; Code section + + ;; function 0 + "\1a\04" + "\80\80\80\80\04\7f" ;; 0x40000000 i32 + "\80\80\80\80\04\7e" ;; 0x40000000 i64 + "\80\80\80\80\04\7d" ;; 0x40000000 f32 + "\80\80\80\80\04\7c" ;; 0x40000000 f64 + "\0b" ;; end + ) + "too many locals" +) + ;; Local count can be 0. (module binary "\00asm" "\01\00\00\00" @@ -1528,10 +1576,25 @@ "\09\07\02" ;; elem with inconsistent segment count (2 declared, 1 given) "\00\41\00\0b\01\00" ;; elem 0 ;; "\00\41\00\0b\01\00" ;; elem 1 (missed) - "\0a\04\01" ;; code section - "\02\00\0b" ;; function body ) - "malformed elements segment kind" + "unexpected end" +) + +;; 2 elem segment declared, 1.5 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\07\02" ;; elem with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\00" ;; elem 0 + "\00\41\00" ;; elem 1 (partial) + ;; "\0b\01\00" ;; elem 1 (missing part) + ) + "unexpected end" ) ;; 1 elem segment declared, 2 given diff --git a/test/core/br_table.wast b/test/core/br_table.wast index a9649645..734593d6 100644 --- a/test/core/br_table.wast +++ b/test/core/br_table.wast @@ -1488,6 +1488,16 @@ )) "type mismatch" ) +(assert_invalid + (module (func + (block (result i32) + (block (result i64) + (br_table 0 1 (i32.const 0) (i32.const 0)) + ) + ) + )) + "type mismatch" +) (assert_invalid (module (func $type-index-void-vs-i32 @@ -1576,6 +1586,31 @@ "type mismatch" ) +(assert_invalid + (module + (func (param i32) (result i32) + (loop (result i32) + (block (result i32) + (br_table 0 1 (i32.const 1) (local.get 0)) + ) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (param i32) (result i32) + (block (result i32) + (loop (result i32) + (br_table 0 1 (i32.const 1) (local.get 0)) + ) + ) + ) + ) + "type mismatch" +) + (assert_invalid (module (func $meet-bottom (param i32) (result externref) diff --git a/test/core/data.wast b/test/core/data.wast index f4d91463..263ef4e3 100644 --- a/test/core/data.wast +++ b/test/core/data.wast @@ -296,6 +296,82 @@ "unknown memory" ) +;; Data segment with memory index 1 (only memory 0 available) +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\00" ;; memory 0 + "\0b\07\01" ;; data section + "\02\01\41\00\0b" ;; data segment 0 for memory 1 + "\00" ;; empty vec(byte) + ) + "unknown memory 1" +) + +;; Data segment with memory index 0 (no memory section) +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\0b\06\01" ;; data section + "\00\41\00\0b" ;; data segment 0 for memory 0 + "\00" ;; empty vec(byte) + ) + "unknown memory 0" +) + +;; Data segment with memory index 1 (no memory section) +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\0b\07\01" ;; data section + "\02\01\41\00\0b" ;; data segment 0 for memory 1 + "\00" ;; empty vec(byte) + ) + "unknown memory 1" +) + +;; Data segment with memory index 1 and vec(byte) as above, +;; only memory 0 available. +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\00" ;; memory 0 + "\0b\45\01" ;; data section + "\02" ;; active segment + "\01" ;; memory index + "\41\00\0b" ;; offset constant expression + "\3e" ;; vec(byte) length + "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f" + "\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + "\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f" + "\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d" + ) + "unknown memory 1" +) + +;; Data segment with memory index 1 and specially crafted vec(byte) after. +;; This is to detect incorrect validation where memory index is interpreted +;; as a flag followed by "\41" interpreted as the size of vec(byte) +;; with the expected number of bytes following. +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\0b\45\01" ;; data section + "\02" ;; active segment + "\01" ;; memory index + "\41\00\0b" ;; offset constant expression + "\3e" ;; vec(byte) length + "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f" + "\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + "\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f" + "\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d" + ) + "unknown memory 1" +) + + ;; Invalid offsets (assert_invalid diff --git a/test/core/exports.wast b/test/core/exports.wast index a099172a..8d535571 100644 --- a/test/core/exports.wast +++ b/test/core/exports.wast @@ -25,10 +25,18 @@ (module $Other1) (assert_return (invoke $Func "e" (i32.const 42)) (i32.const 43)) +(assert_invalid + (module (export "a" (func 0))) + "unknown function" +) (assert_invalid (module (func) (export "a" (func 1))) "unknown function" ) +(assert_invalid + (module (import "spectest" "print_i32" (func (param i32))) (export "a" (func 1))) + "unknown function" +) (assert_invalid (module (func) (export "a" (func 0)) (export "a" (func 0))) "duplicate export name" @@ -74,10 +82,18 @@ (module $Other2) (assert_return (get $Global "e") (i32.const 42)) +(assert_invalid + (module (export "a" (global 0))) + "unknown global" +) (assert_invalid (module (global i32 (i32.const 0)) (export "a" (global 1))) "unknown global" ) +(assert_invalid + (module (import "spectest" "global_i32" (global i32)) (export "a" (global 1))) + "unknown global" +) (assert_invalid (module (global i32 (i32.const 0)) (export "a" (global 0)) (export "a" (global 0))) "duplicate export name" @@ -121,10 +137,18 @@ (; TODO: access table ;) +(assert_invalid + (module (export "a" (table 0))) + "unknown table" +) (assert_invalid (module (table 0 funcref) (export "a" (table 1))) "unknown table" ) +(assert_invalid + (module (import "spectest" "table" (table 10 20 funcref)) (export "a" (table 1))) + "unknown table" +) (assert_invalid (module (table 0 funcref) (export "a" (table 0)) (export "a" (table 0))) "duplicate export name" @@ -169,10 +193,18 @@ (; TODO: access memory ;) +(assert_invalid + (module (export "a" (memory 0))) + "unknown memory" +) (assert_invalid (module (memory 0) (export "a" (memory 1))) "unknown memory" ) +(assert_invalid + (module (import "spectest" "memory" (memory 1 2)) (export "a" (memory 1))) + "unknown memory" +) (assert_invalid (module (memory 0) (export "a" (memory 0)) (export "a" (memory 0))) "duplicate export name" diff --git a/test/core/global.wast b/test/core/global.wast index c98037ae..0e7a966e 100644 --- a/test/core/global.wast +++ b/test/core/global.wast @@ -255,6 +255,11 @@ "global is immutable" ) +(assert_invalid + (module (import "spectest" "global_i32" (global i32)) (func (global.set 0 (i32.const 1)))) + "global is immutable" +) + ;; mutable globals can be exported (module (global (mut f32) (f32.const 0)) (export "a" (global 0))) (module (global (export "a") (mut f32) (f32.const 0))) @@ -372,6 +377,68 @@ "malformed mutability" ) +;; global.get with invalid index +(assert_invalid + (module (func (result i32) (global.get 0))) + "unknown global" +) + +(assert_invalid + (module + (global i32 (i32.const 0)) + (func (result i32) (global.get 1)) + ) + "unknown global" +) + +(assert_invalid + (module + (import "spectest" "global_i32" (global i32)) + (func (result i32) (global.get 1)) + ) + "unknown global" +) + +(assert_invalid + (module + (import "spectest" "global_i32" (global i32)) + (global i32 (i32.const 0)) + (func (result i32) (global.get 2)) + ) + "unknown global" +) + +;; global.set with invalid index +(assert_invalid + (module (func (i32.const 0) (global.set 0))) + "unknown global" +) + +(assert_invalid + (module + (global i32 (i32.const 0)) + (func (i32.const 0) (global.set 1)) + ) + "unknown global" +) + +(assert_invalid + (module + (import "spectest" "global_i32" (global i32)) + (func (i32.const 0) (global.set 1)) + ) + "unknown global" +) + +(assert_invalid + (module + (import "spectest" "global_i32" (global i32)) + (global i32 (i32.const 0)) + (func (i32.const 0) (global.set 2)) + ) + "unknown global" +) + (assert_invalid (module diff --git a/test/core/imports.wast b/test/core/imports.wast index c3a011d7..cfe79489 100644 --- a/test/core/imports.wast +++ b/test/core/imports.wast @@ -10,6 +10,7 @@ (func (export "func-i64->i64") (param i64) (result i64) (local.get 0)) (global (export "global-i32") i32 (i32.const 55)) (global (export "global-f32") f32 (f32.const 44)) + (global (export "global-mut-i64") (mut i64) (i64.const 66)) (table (export "table-10-inf") 10 funcref) (table (export "table-10-20") 10 20 funcref) (memory (export "memory-2-inf") 2) @@ -231,6 +232,7 @@ (module (import "test" "global-i32" (global i32))) (module (import "test" "global-f32" (global f32))) +(module (import "test" "global-mut-i64" (global (mut i64)))) (assert_unlinkable (module (import "test" "unknown" (global i32))) @@ -241,6 +243,55 @@ "unknown import" ) +(assert_unlinkable + (module (import "test" "global-i32" (global i64))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (global f32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (global f64))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (global (mut i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-f32" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-f32" (global i64))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-f32" (global f64))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-f32" (global (mut f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-mut-i64" (global (mut i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-mut-i64" (global (mut f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-mut-i64" (global (mut f64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-mut-i64" (global i64))) + "incompatible import type" +) + (assert_unlinkable (module (import "test" "func" (global i32))) "incompatible import type" diff --git a/test/core/local_get.wast b/test/core/local_get.wast index ab564cbe..6acab398 100644 --- a/test/core/local_get.wast +++ b/test/core/local_get.wast @@ -198,29 +198,29 @@ ;; Invalid local index (assert_invalid - (module (func $unbound-local (local i32 i64) (local.get 3))) + (module (func $unbound-local (local i32 i64) (local.get 3) drop)) "unknown local" ) (assert_invalid - (module (func $large-local (local i32 i64) (local.get 14324343))) + (module (func $large-local (local i32 i64) (local.get 14324343) drop)) "unknown local" ) (assert_invalid - (module (func $unbound-param (param i32 i64) (local.get 2))) + (module (func $unbound-param (param i32 i64) (local.get 2) drop)) "unknown local" ) (assert_invalid - (module (func $large-param (param i32 i64) (local.get 714324343))) + (module (func $large-param (param i32 i64) (local.get 714324343) drop)) "unknown local" ) (assert_invalid - (module (func $unbound-mixed (param i32) (local i32 i64) (local.get 3))) + (module (func $unbound-mixed (param i32) (local i32 i64) (local.get 3) drop)) "unknown local" ) (assert_invalid - (module (func $large-mixed (param i64) (local i32 i64) (local.get 214324343))) + (module (func $large-mixed (param i64) (local i32 i64) (local.get 214324343) drop)) "unknown local" ) diff --git a/test/core/local_tee.wast b/test/core/local_tee.wast index dde7321e..a158f0c7 100644 --- a/test/core/local_tee.wast +++ b/test/core/local_tee.wast @@ -595,45 +595,45 @@ "type mismatch" ) - -;; Invalid local index - (assert_invalid - (module (func $unbound-local (local i32 i64) (local.get 3))) - "unknown local" + (module (func $type-mixed-arg-num-vs-num (param f32) (local i32) (local.tee 1 (f32.const 0)))) + "type mismatch" ) (assert_invalid - (module (func $large-local (local i32 i64) (local.get 14324343))) - "unknown local" + (module (func $type-mixed-arg-num-vs-num (param i64 i32) (local f32) (local.tee 1 (f32.const 0)))) + "type mismatch" +) +(assert_invalid + (module (func $type-mixed-arg-num-vs-num (param i64) (local f64 i64) (local.tee 1 (i64.const 0)))) + "type mismatch" ) + +;; Invalid local index + (assert_invalid - (module (func $unbound-param (param i32 i64) (local.get 2))) + (module (func $unbound-local (local i32 i64) (local.tee 3 (i32.const 0)) drop)) "unknown local" ) (assert_invalid - (module (func $large-param (local i32 i64) (local.get 714324343))) + (module (func $large-local (local i32 i64) (local.tee 14324343 (i32.const 0)) drop)) "unknown local" ) (assert_invalid - (module (func $unbound-mixed (param i32) (local i32 i64) (local.get 3))) + (module (func $unbound-param (param i32 i64) (local.tee 2 (i32.const 0)) drop)) "unknown local" ) (assert_invalid - (module (func $large-mixed (param i64) (local i32 i64) (local.get 214324343))) + (module (func $large-param (param i32 i64) (local.tee 714324343 (i32.const 0)) drop)) "unknown local" ) (assert_invalid - (module (func $type-mixed-arg-num-vs-num (param f32) (local i32) (local.tee 1 (f32.const 0)))) - "type mismatch" -) -(assert_invalid - (module (func $type-mixed-arg-num-vs-num (param i64 i32) (local f32) (local.tee 1 (f32.const 0)))) - "type mismatch" + (module (func $unbound-mixed (param i32) (local i32 i64) (local.tee 3 (i32.const 0)) drop)) + "unknown local" ) (assert_invalid - (module (func $type-mixed-arg-num-vs-num (param i64) (local f64 i64) (local.tee 1 (i64.const 0)))) - "type mismatch" + (module (func $large-mixed (param i64) (local i32 i64) (local.tee 214324343 (i32.const 0)) drop)) + "unknown local" )