From 6943f4eb3512b90b551a7865b89b19d7f23353d5 Mon Sep 17 00:00:00 2001 From: Thomas Letan Date: Mon, 20 Feb 2023 14:21:43 +0100 Subject: [PATCH] WASM: Parameterize `stack_size_limit` instead of hardcoding it to 300 The callstack in the lib_webassembly interpreter is limited to 300, where 300 is hardcoded at the creation of the config parameter driving the execution of a webassembly code. With this patch, we turn this into a parameter that the caller can modify. The value chosen by the PVM remains 300 for now, for backward compatibility reasons. Once the notion of WASM PVM revision is merged, it will become possible to modify this parameter and makes it more permissive. Gitlab-Issue: https://gitlab.com/tezos/tezos/-/issues/4888 --- src/lib_scoru_wasm/constants.ml | 2 ++ src/lib_scoru_wasm/wasm_vm.ml | 41 +++++++++++++++++++------- src/lib_webassembly/exec/eval.ml | 49 ++++++++++++++++++------------- src/lib_webassembly/exec/eval.mli | 4 +++ 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/src/lib_scoru_wasm/constants.ml b/src/lib_scoru_wasm/constants.ml index 5e5a3d356e2a..3f51cf9c497e 100644 --- a/src/lib_scoru_wasm/constants.ml +++ b/src/lib_scoru_wasm/constants.ml @@ -64,3 +64,5 @@ let too_many_reboot_flag_key = kernel can do with a given inbox. *) let reboot_counter_key = Durable.key_of_string_exn "/readonly/kernel/env/reboot_counter" + +let stack_size_limit = 300 diff --git a/src/lib_scoru_wasm/wasm_vm.ml b/src/lib_scoru_wasm/wasm_vm.ml index aef0ca026461..0b241153ed66 100644 --- a/src/lib_scoru_wasm/wasm_vm.ml +++ b/src/lib_scoru_wasm/wasm_vm.ml @@ -115,7 +115,7 @@ let save_fallback_kernel durable = Constants.kernel_fallback_key else Lwt.return durable -let unsafe_next_tick_state host_function_registry +let unsafe_next_tick_state ~stack_size_limit host_function_registry ({buffers; durable; tick_state; _} as pvm_state) = let open Lwt_syntax in let return ?(status = Running) ?(durable = durable) state = @@ -234,6 +234,7 @@ let unsafe_next_tick_state host_function_registry (* Clear the values and the locals in the frame. *) let config = Wasm.Eval.config + ~stack_size_limit host_function_registry self (Tezos_lazy_containers.Lazy_vector.Int32Vector.empty ()) @@ -257,6 +258,7 @@ let unsafe_next_tick_state host_function_registry | Init {self; ast_module; init_kont; module_reg} -> let* init_kont = Wasm.Eval.init_step + ~stack_size_limit ~filter_exports:true ~check_module_exports:Exports_memory_0 ~module_reg @@ -307,10 +309,11 @@ let exn_to_stuck pvm_state exn = in Lwt.return (Stuck wasm_error) -let next_tick_state host_function_registry pvm_state = +let next_tick_state ~stack_size_limit host_function_registry pvm_state = let open Lwt_syntax in Lwt.catch - (fun () -> unsafe_next_tick_state host_function_registry pvm_state) + (fun () -> + unsafe_next_tick_state ~stack_size_limit host_function_registry pvm_state) (fun exn -> let+ tick_state = exn_to_stuck pvm_state exn in (pvm_state.durable, tick_state, Failing)) @@ -395,10 +398,12 @@ let clean_up_input_buffer buffers = (** [compute_step pvm_state] does one computation step on [pvm_state]. Returns the new state. *) -let compute_step_with_host_functions registry pvm_state = +let compute_step_with_host_functions ~stack_size_limit registry pvm_state = let open Lwt_syntax in (* Calculate the next tick state. *) - let* durable, tick_state, status = next_tick_state registry pvm_state in + let* durable, tick_state, status = + next_tick_state ~stack_size_limit registry pvm_state + in let current_tick = Z.succ pvm_state.current_tick in let last_top_level_call = next_last_top_level_call pvm_state status in let reboot_counter = next_reboot_counter pvm_state status in @@ -419,7 +424,10 @@ let compute_step_with_host_functions registry pvm_state = return pvm_state let compute_step pvm_state = - compute_step_with_host_functions Host_funcs.all pvm_state + compute_step_with_host_functions + ~stack_size_limit:Constants.stack_size_limit + Host_funcs.all + pvm_state let compute_step_with_debug ~write_debug pvm_state = let registry = @@ -427,7 +435,10 @@ let compute_step_with_debug ~write_debug pvm_state = | Builtins.Printer _ -> Host_funcs.all_debug ~write_debug | Noop -> Host_funcs.all in - compute_step_with_host_functions registry pvm_state + compute_step_with_host_functions + ~stack_size_limit:Constants.stack_size_limit + registry + pvm_state let input_request pvm_state = match pvm_state.tick_state with @@ -499,6 +510,7 @@ let compute_step_many_until ?(max_steps = 1L) ?reveal_builtins ?(write_debug = Builtins.Noop) should_continue = let open Lwt.Syntax in assert (max_steps > 0L) ; + let stack_size_limit = Constants.stack_size_limit in let host_function_registry = match write_debug with | Builtins.Printer _ -> Host_funcs.all_debug ~write_debug @@ -517,8 +529,14 @@ let compute_step_many_until ?(max_steps = 1L) ?reveal_builtins let* res = reveal_builtins.reveal_metadata () in reveal_step (Bytes.of_string res) pvm_state | _ -> - compute_step_with_host_functions host_function_registry pvm_state) - | None -> compute_step_with_host_functions host_function_registry + compute_step_with_host_functions + ~stack_size_limit + host_function_registry + pvm_state) + | None -> + compute_step_with_host_functions + ~stack_size_limit + host_function_registry in let rec go steps_left pvm_state = let* continue = should_continue pvm_state in @@ -547,7 +565,10 @@ let compute_step_many_until ?(max_steps = 1L) ?reveal_builtins (* Make sure we perform at least 1 step. The assertion above ensures that we were asked to perform at least 1. *) let* pvm_state = - compute_step_with_host_functions host_function_registry pvm_state + compute_step_with_host_functions + ~stack_size_limit + host_function_registry + pvm_state in go (Int64.pred max_steps) pvm_state in diff --git a/src/lib_webassembly/exec/eval.ml b/src/lib_webassembly/exec/eval.ml index 3fba1c6e559a..735d2a56320a 100644 --- a/src/lib_webassembly/exec/eval.ml +++ b/src/lib_webassembly/exec/eval.ml @@ -432,7 +432,7 @@ let buffers ?(input = Input_buffer.alloc ()) ?(output = default_output_buffer ()) () = {input; output} -let config host_funcs ?frame_arity inst vs es = +let config ~stack_size_limit host_funcs ?frame_arity inst vs es = let frame = frame inst (Vector.empty ()) in let label_kont = label_kont @@ -444,7 +444,7 @@ let config host_funcs ?frame_arity inst vs es = { step_kont = SK_Start (frame_stack, Vector.empty ()); host_funcs; - stack_size_limit = 300; + stack_size_limit; } let plain e = Plain e.it @@ e.at @@ -1630,9 +1630,10 @@ let reveal_step reveal module_reg payload = (* Functions & Constants *) -let invoke ~module_reg ~caller ?(input = Input_buffer.alloc ()) - ?(output = default_output_buffer ()) ?(durable = Durable_storage.empty) - ?(init = false) host_funcs (func : func_inst) (vs : value list) : +let invoke ?(stack_size_limit = 300) ~module_reg ~caller + ?(input = Input_buffer.alloc ()) ?(output = default_output_buffer ()) + ?(durable = Durable_storage.empty) ?(init = false) host_funcs + (func : func_inst) (vs : value list) : (Durable_storage.t * value list) Lwt.t = let at = match func with Func.AstFunc (_, _, f) -> f.at | _ -> no_region in let (FuncType (ins, out)) = Func.type_of func in @@ -1652,6 +1653,7 @@ let invoke ~module_reg ~caller ?(input = Input_buffer.alloc ()) let n = Vector.num_elements out in let c = config + ~stack_size_limit host_funcs ~frame_arity:n inst @@ -1669,9 +1671,10 @@ let invoke ~module_reg ~caller ?(input = Input_buffer.alloc ()) type eval_const_kont = EC_Next of config | EC_Stop of value -let eval_const_kont inst (const : const) = +let eval_const_kont ~stack_size_limit inst (const : const) = let c = config + ~stack_size_limit (Host_funcs.empty ()) inst (Vector.empty ()) @@ -1711,8 +1714,8 @@ let create_memory (mem : memory) : memory_inst = type create_global_kont = global_type * eval_const_kont -let create_global_kont inst glob = - (glob.it.gtype, eval_const_kont inst glob.it.ginit) +let create_global_kont ~stack_size_limit inst glob = + (glob.it.gtype, eval_const_kont ~stack_size_limit inst glob.it.ginit) let create_global_completed (gtype, kont) = match eval_const_completed kont with @@ -1972,11 +1975,11 @@ let create_elem_completed : create_elem_kont -> elem_inst option = fun kont -> if tick_map_completed kont then Some (ref kont.map.destination) else None -let create_elem_step ~module_reg buffers inst : +let create_elem_step ~stack_size_limit ~module_reg buffers inst : create_elem_kont -> create_elem_kont Lwt.t = fun tick -> tick_map_step - (eval_const_kont inst) + (eval_const_kont ~stack_size_limit inst) (fun x -> match eval_const_completed x with | Some x -> Some (as_ref x) @@ -2026,11 +2029,13 @@ let section_next_init_kont : {exports = NameMap.create (); exports_memory_0 = false} ) let section_inner_kont : - type kont a b. module_key -> (kont, a, b) init_section -> a -> kont = - fun self sec x -> + type kont a b. + stack_size_limit:int -> module_key -> (kont, a, b) init_section -> a -> kont + = + fun ~stack_size_limit self sec x -> match sec with | Func -> Either.Left x - | Global -> create_global_kont self x + | Global -> create_global_kont ~stack_size_limit self x | Table -> Left x | Memory -> Left x @@ -2086,8 +2091,9 @@ type memory_export_rules = Exports_memory_0 | No_memory_export_rules exception Missing_memory_0_export -let init_step ~filter_exports ?(check_module_exports = No_memory_export_rules) - ~module_reg ~self buffers host_funcs (m : module_) = function +let init_step ~stack_size_limit ~filter_exports + ?(check_module_exports = No_memory_export_rules) ~module_reg ~self buffers + host_funcs (m : module_) = function | IK_Start exts -> (* Initialize as empty module. *) update_module_ref module_reg self empty_module_inst ; @@ -2124,7 +2130,7 @@ let init_step ~filter_exports ?(check_module_exports = No_memory_export_rules) | IK_Aggregate (inst0, sec, tick) -> let+ tick = tick_map_step - (section_inner_kont self sec) + (section_inner_kont ~stack_size_limit self sec) (section_inner_completed sec) (section_inner_step module_reg self buffers sec) tick @@ -2169,7 +2175,7 @@ let init_step ~filter_exports ?(check_module_exports = No_memory_export_rules) tick_map_step create_elem_kont create_elem_completed - (create_elem_step ~module_reg buffers self) + (create_elem_step ~stack_size_limit ~module_reg buffers self) tick in IK_Elems (inst0, tick) @@ -2216,7 +2222,9 @@ let init_step ~filter_exports ?(check_module_exports = No_memory_export_rules) | IK_Join_admin (inst0, tick) -> ( match join_completed tick with | Some res -> - Lwt.return (IK_Eval (config host_funcs self (Vector.empty ()) res)) + Lwt.return + (IK_Eval + (config ~stack_size_limit host_funcs self (Vector.empty ()) res)) | None -> let+ tick = join_step tick in IK_Join_admin (inst0, tick)) @@ -2230,14 +2238,15 @@ let init_step ~filter_exports ?(check_module_exports = No_memory_export_rules) IK_Eval config | IK_Stop -> raise (Init_step_error Init_step) -let init ~module_reg ~self buffers host_funcs (m : module_) (exts : extern list) - : module_inst Lwt.t = +let init ?(stack_size_limit = 300) ~module_reg ~self buffers host_funcs + (m : module_) (exts : extern list) : module_inst Lwt.t = let open Lwt.Syntax in let rec go = function | IK_Stop -> resolve_module_ref module_reg self | kont -> let* kont = init_step + ~stack_size_limit ~filter_exports:false ~module_reg ~self diff --git a/src/lib_webassembly/exec/eval.mli b/src/lib_webassembly/exec/eval.mli index 743fb626358b..b2b9e851b266 100644 --- a/src/lib_webassembly/exec/eval.mli +++ b/src/lib_webassembly/exec/eval.mli @@ -239,6 +239,7 @@ exception Missing_memory_0_export @raise Invalid_argument if called with [IK_Stop]. There is no transition from the terminal state. *) val init_step : + stack_size_limit:int -> filter_exports:bool -> ?check_module_exports:memory_export_rules -> module_reg:module_reg -> @@ -250,6 +251,7 @@ val init_step : init_kont Lwt.t val init : + ?stack_size_limit:int -> module_reg:module_reg -> self:module_key -> buffers -> @@ -259,6 +261,7 @@ val init : module_inst Lwt.t (* raises Link, Trap *) val invoke : + ?stack_size_limit:int -> module_reg:module_reg -> caller:module_key -> ?input:Input_buffer.t -> @@ -313,6 +316,7 @@ val reveal_step : config Lwt.t val config : + stack_size_limit:int -> Host_funcs.registry -> ?frame_arity:int32 (* The number of values returned by the computation *) -> module_key -> -- GitLab