Skip to content

Commit

Permalink
Distinguish stack overflow from other traps (#388)
Browse files Browse the repository at this point in the history
  • Loading branch information
rossberg committed Jan 18, 2017
1 parent 0729a93 commit 5236d50
Show file tree
Hide file tree
Showing 12 changed files with 48 additions and 20 deletions.
15 changes: 13 additions & 2 deletions interpreter/host/js.ml
Expand Up @@ -85,6 +85,16 @@ let prefix =
" throw new Error(\"Wasm trap expected\");\n" ^
"}\n" ^
"\n" ^
"let StackOverflow;\n" ^
"try { (function f() { 1 + f() })() } catch (e) { StackOverflow = e.constructor }\n" ^
"\n" ^
"function assert_exhaustion(action) {\n" ^
" try { action() } catch (e) {\n" ^
" if (e instanceof StackOverflow) return;\n" ^
" }\n" ^
" throw new Error(\"Wasm resource exhaustion expected\");\n" ^
"}\n" ^
"\n" ^
"function assert_return(action, expected) {\n" ^
" let actual = action();\n" ^
" if (!Object.is(actual, expected)) {\n" ^
Expand Down Expand Up @@ -301,8 +311,9 @@ let of_assertion mods ass =
of_return_assertion mods act
(fun act_js -> "assert_return_nan(() => " ^ act_js ^ ")")
assert_return_nan
| AssertTrap (act, _) ->
let js act_js = "assert_trap(() => " ^ act_js ^ ")" in
| AssertTrap (act, _) | AssertExhaustion (act, _) ->
let name = match ass.it with AssertTrap _ -> "trap" | _ -> "exhaustion" in
let js act_js = "assert_" ^ name ^ "(() => " ^ act_js ^ ")" in
match of_action mods act with
| act_js, None -> js act_js ^ ";"
| act_js, Some (act_wrapper, ts) ->
Expand Down
9 changes: 9 additions & 0 deletions interpreter/host/run.ml
Expand Up @@ -97,6 +97,7 @@ let input_from get_script run =
| Import.Unknown (at, msg) -> error at "link failure" msg
| Eval.Link (at, msg) -> error at "link failure" msg
| Eval.Trap (at, msg) -> error at "runtime trap" msg
| Eval.Exhaustion (at, msg) -> error at "resource exhaustion" msg
| Eval.Crash (at, msg) -> error at "runtime crash" msg
| Encode.Code (at, msg) -> error at "encoding error" msg
| IO (at, msg) -> error at "i/o error" msg
Expand Down Expand Up @@ -377,6 +378,14 @@ let run_assertion ass =
| _ -> Assert.error ass.at "expected runtime error"
)

| AssertExhaustion (act, re) ->
trace ("Asserting exhaustion...");
(match run_action act with
| exception Eval.Exhaustion (_, msg) ->
assert_message ass.at "exhaustion" msg re
| _ -> Assert.error ass.at "expected exhaustion error"
)

let rec run_command cmd =
match cmd.it with
| Module (x_opt, def) ->
Expand Down
6 changes: 4 additions & 2 deletions interpreter/spec/eval.ml
Expand Up @@ -10,10 +10,12 @@ open Source
module Link = Error.Make ()
module Trap = Error.Make ()
module Crash = Error.Make ()
module Exhaustion = Error.Make ()

exception Link = Link.Error
exception Trap = Trap.Error
exception Crash = Crash.Error (* failure that cannot happen in valid code *)
exception Exhaustion = Exhaustion.Error

let memory_error at = function
| Memory.Bounds -> "out of bounds memory access"
Expand Down Expand Up @@ -281,7 +283,7 @@ let rec step (inst : instance) (c : config) : config =
vs, [Local (inst', c'.locals, c'.values, c'.instrs) @@ e.at]

| Invoke clos, vs when c.budget = 0 ->
vs, [Trapped "call stack exhausted" @@ e.at]
Exhaustion.error e.at "call stack exhausted"

| Invoke clos, vs ->
let FuncType (ins, out) = func_type_of clos in
Expand Down Expand Up @@ -322,7 +324,7 @@ let invoke (clos : closure) (vs : value list) : value list =
let inst = instance (empty_module @@ at) in
let c = config (List.rev vs) [Invoke clos @@ at] in
try List.rev (eval inst c)
with Stack_overflow -> Trap.error at "call stack exhausted"
with Stack_overflow -> Exhaustion.error at "call stack exhausted"

let eval_const (inst : instance) (const : const) : value =
let c = config [] (List.map plain const.it) in
Expand Down
1 change: 1 addition & 0 deletions interpreter/spec/eval.mli
Expand Up @@ -4,6 +4,7 @@ open Instance
exception Link of Source.region * string
exception Trap of Source.region * string
exception Crash of Source.region * string
exception Exhaustion of Source.region * string

val init : Ast.module_ -> extern list -> instance
val invoke : closure -> value list -> value list (* raises Trap *)
4 changes: 2 additions & 2 deletions interpreter/test/call.wast
Expand Up @@ -141,8 +141,8 @@
(assert_return (invoke "odd" (i64.const 200)) (i32.const 99))
(assert_return (invoke "odd" (i64.const 77)) (i32.const 44))

(assert_trap (invoke "runaway") "call stack exhausted")
(assert_trap (invoke "mutual-runaway") "call stack exhausted")
(assert_exhaustion (invoke "runaway") "call stack exhausted")
(assert_exhaustion (invoke "mutual-runaway") "call stack exhausted")


;; Invalid typing
Expand Down
4 changes: 2 additions & 2 deletions interpreter/test/call_indirect.wast
Expand Up @@ -219,8 +219,8 @@
(assert_return (invoke "odd" (i32.const 200)) (i32.const 99))
(assert_return (invoke "odd" (i32.const 77)) (i32.const 44))

(assert_trap (invoke "runaway") "call stack exhausted")
(assert_trap (invoke "mutual-runaway") "call stack exhausted")
(assert_exhaustion (invoke "runaway") "call stack exhausted")
(assert_exhaustion (invoke "mutual-runaway") "call stack exhausted")


;; Invalid typing
Expand Down
2 changes: 1 addition & 1 deletion interpreter/test/fac.wast
Expand Up @@ -82,4 +82,4 @@
(assert_return (invoke "fac-rec-named" (i64.const 25)) (i64.const 7034535277573963776))
(assert_return (invoke "fac-iter-named" (i64.const 25)) (i64.const 7034535277573963776))
(assert_return (invoke "fac-opt" (i64.const 25)) (i64.const 7034535277573963776))
(assert_trap (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted")
(assert_exhaustion (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted")
20 changes: 10 additions & 10 deletions interpreter/test/skip-stack-guard-page.wast
Expand Up @@ -2272,13 +2272,13 @@
)
)

(assert_trap (invoke "test-guard-page-skip" (i32.const 0)) "call stack exhausted")
(assert_trap (invoke "test-guard-page-skip" (i32.const 100)) "call stack exhausted")
(assert_trap (invoke "test-guard-page-skip" (i32.const 200)) "call stack exhausted")
(assert_trap (invoke "test-guard-page-skip" (i32.const 300)) "call stack exhausted")
(assert_trap (invoke "test-guard-page-skip" (i32.const 400)) "call stack exhausted")
(assert_trap (invoke "test-guard-page-skip" (i32.const 500)) "call stack exhausted")
(assert_trap (invoke "test-guard-page-skip" (i32.const 600)) "call stack exhausted")
(assert_trap (invoke "test-guard-page-skip" (i32.const 700)) "call stack exhausted")
(assert_trap (invoke "test-guard-page-skip" (i32.const 800)) "call stack exhausted")
(assert_trap (invoke "test-guard-page-skip" (i32.const 900)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 0)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 100)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 200)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 300)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 400)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 500)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 600)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 700)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 800)) "call stack exhausted")
(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 900)) "call stack exhausted")
2 changes: 2 additions & 0 deletions interpreter/text/arrange.ml
Expand Up @@ -422,6 +422,8 @@ let assertion mode ass =
Node ("assert_return_nan", [action act])
| AssertTrap (act, re) ->
Node ("assert_trap", [action act; Atom (string re)])
| AssertExhaustion (act, re) ->
Node ("assert_exhaustion", [action act; Atom (string re)])

let command mode cmd =
match cmd.it with
Expand Down
1 change: 1 addition & 0 deletions interpreter/text/lexer.mll
Expand Up @@ -311,6 +311,7 @@ rule token = parse
| "assert_return" { ASSERT_RETURN }
| "assert_return_nan" { ASSERT_RETURN_NAN }
| "assert_trap" { ASSERT_TRAP }
| "assert_exhaustion" { ASSERT_EXHAUSTION }
| "input" { INPUT }
| "output" { OUTPUT }

Expand Down
3 changes: 2 additions & 1 deletion interpreter/text/parser.mly
Expand Up @@ -157,7 +157,7 @@ let inline_type (c : context) ty at =
%token MODULE TABLE ELEM MEMORY DATA OFFSET IMPORT EXPORT TABLE
%token SCRIPT REGISTER INVOKE GET
%token ASSERT_MALFORMED ASSERT_INVALID ASSERT_SOFT_INVALID ASSERT_UNLINKABLE
%token ASSERT_RETURN ASSERT_RETURN_NAN ASSERT_TRAP
%token ASSERT_RETURN ASSERT_RETURN_NAN ASSERT_TRAP ASSERT_EXHAUSTION
%token INPUT OUTPUT
%token EOF

Expand Down Expand Up @@ -665,6 +665,7 @@ assertion :
| LPAR ASSERT_RETURN action const_list RPAR { AssertReturn ($3, $4) @@ at () }
| LPAR ASSERT_RETURN_NAN action RPAR { AssertReturnNaN $3 @@ at () }
| LPAR ASSERT_TRAP action TEXT RPAR { AssertTrap ($3, $4) @@ at () }
| LPAR ASSERT_EXHAUSTION action TEXT RPAR { AssertExhaustion ($3, $4) @@ at () }
cmd :
| action { Action $1 @@ at () }
Expand Down
1 change: 1 addition & 0 deletions interpreter/text/script.ml
Expand Up @@ -20,6 +20,7 @@ and assertion' =
| AssertReturn of action * Ast.literal list
| AssertReturnNaN of action
| AssertTrap of action * string
| AssertExhaustion of action * string

type command = command' Source.phrase
and command' =
Expand Down

0 comments on commit 5236d50

Please sign in to comment.