Skip to main content

soil_client/executor/wasmtime/
runtime.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Defines the compiled Wasm runtime that uses Wasmtime internally.
8
9use crate::executor::wasmtime::{
10	host::HostState,
11	instance_wrapper::{EntryPoint, InstanceWrapper, MemoryWrapper},
12	util::{self, replace_strategy_if_broken},
13};
14
15use crate::executor::common::{
16	error::{Error, Result, WasmError},
17	runtime_blob::RuntimeBlob,
18	util::checked_range,
19	wasm_runtime::{HeapAllocStrategy, WasmInstance, WasmModule},
20};
21use parking_lot::Mutex;
22use std::{
23	path::{Path, PathBuf},
24	sync::{
25		atomic::{AtomicBool, Ordering},
26		Arc,
27	},
28};
29use subsoil::allocator::{AllocationStats, FreeingBumpHeapAllocator};
30use subsoil::runtime_interface::unpack_ptr_and_len;
31use subsoil::wasm_interface::{HostFunctions, Pointer, WordSize};
32use wasmtime::{AsContext, Cache, CacheConfig, Engine, Memory};
33
34const MAX_INSTANCE_COUNT: u32 = 64;
35
36#[derive(Default)]
37pub(crate) struct StoreData {
38	/// This will only be set when we call into the runtime.
39	pub(crate) host_state: Option<HostState>,
40	/// This will be always set once the store is initialized.
41	pub(crate) memory: Option<Memory>,
42}
43
44impl StoreData {
45	/// Returns a mutable reference to the host state.
46	pub fn host_state_mut(&mut self) -> Option<&mut HostState> {
47		self.host_state.as_mut()
48	}
49
50	/// Returns the host memory.
51	pub fn memory(&self) -> Memory {
52		self.memory.expect("memory is always set; qed")
53	}
54}
55
56pub(crate) type Store = wasmtime::Store<StoreData>;
57
58enum Strategy {
59	RecreateInstance(InstanceCreator),
60}
61
62struct InstanceCreator {
63	engine: Engine,
64	instance_pre: Arc<wasmtime::InstancePre<StoreData>>,
65	instance_counter: Arc<InstanceCounter>,
66}
67
68impl InstanceCreator {
69	fn instantiate(&mut self) -> Result<InstanceWrapper> {
70		InstanceWrapper::new(&self.engine, &self.instance_pre, self.instance_counter.clone())
71	}
72}
73
74/// A handle for releasing an instance acquired by [`InstanceCounter::acquire_instance`].
75pub(crate) struct ReleaseInstanceHandle {
76	counter: Arc<InstanceCounter>,
77}
78
79impl Drop for ReleaseInstanceHandle {
80	fn drop(&mut self) {
81		{
82			let mut counter = self.counter.counter.lock();
83			*counter = counter.saturating_sub(1);
84		}
85
86		self.counter.wait_for_instance.notify_one();
87	}
88}
89
90/// Keeps track on the number of parallel instances.
91///
92/// The runtime cache keeps track on the number of parallel instances. The maximum number in the
93/// cache is less than what we have configured as [`MAX_INSTANCE_COUNT`] for wasmtime. However, the
94/// cache will create on demand instances if required. This instance counter will ensure that we are
95/// blocking when we are trying to create too many instances.
96#[derive(Default)]
97pub(crate) struct InstanceCounter {
98	counter: Mutex<u32>,
99	wait_for_instance: parking_lot::Condvar,
100}
101
102impl InstanceCounter {
103	/// Acquire an instance.
104	///
105	/// Blocks if there is no free instance available.
106	///
107	/// The returned [`ReleaseInstanceHandle`] should be dropped when the instance isn't used
108	/// anymore.
109	pub fn acquire_instance(self: Arc<Self>) -> ReleaseInstanceHandle {
110		let mut counter = self.counter.lock();
111
112		while *counter >= MAX_INSTANCE_COUNT {
113			self.wait_for_instance.wait(&mut counter);
114		}
115		*counter += 1;
116
117		ReleaseInstanceHandle { counter: self.clone() }
118	}
119}
120
121/// A `WasmModule` implementation using wasmtime to compile the runtime module to machine code
122/// and execute the compiled code.
123pub struct WasmtimeRuntime {
124	engine: Engine,
125	instance_pre: Arc<wasmtime::InstancePre<StoreData>>,
126	instantiation_strategy: InternalInstantiationStrategy,
127	instance_counter: Arc<InstanceCounter>,
128}
129
130impl WasmModule for WasmtimeRuntime {
131	fn new_instance(&self) -> Result<Box<dyn WasmInstance>> {
132		let strategy = match self.instantiation_strategy {
133			InternalInstantiationStrategy::Builtin => Strategy::RecreateInstance(InstanceCreator {
134				engine: self.engine.clone(),
135				instance_pre: self.instance_pre.clone(),
136				instance_counter: self.instance_counter.clone(),
137			}),
138		};
139
140		Ok(Box::new(WasmtimeInstance { strategy }))
141	}
142}
143
144/// A `WasmInstance` implementation that reuses compiled module and spawns instances
145/// to execute the compiled code.
146pub struct WasmtimeInstance {
147	strategy: Strategy,
148}
149
150impl WasmtimeInstance {
151	fn call_impl(
152		&mut self,
153		method: &str,
154		data: &[u8],
155		allocation_stats: &mut Option<AllocationStats>,
156	) -> Result<Vec<u8>> {
157		match &mut self.strategy {
158			Strategy::RecreateInstance(ref mut instance_creator) => {
159				let mut instance_wrapper = instance_creator.instantiate()?;
160				let heap_base = instance_wrapper.extract_heap_base()?;
161				let entrypoint = instance_wrapper.resolve_entrypoint(method)?;
162				let allocator = FreeingBumpHeapAllocator::new(heap_base);
163
164				perform_call(data, &mut instance_wrapper, entrypoint, allocator, allocation_stats)
165			},
166		}
167	}
168}
169
170impl WasmInstance for WasmtimeInstance {
171	fn call_with_allocation_stats(
172		&mut self,
173		method: &str,
174		data: &[u8],
175	) -> (Result<Vec<u8>>, Option<AllocationStats>) {
176		let mut allocation_stats = None;
177		let result = self.call_impl(method, data, &mut allocation_stats);
178		(result, allocation_stats)
179	}
180}
181
182/// Prepare a directory structure and a config file to enable wasmtime caching.
183///
184/// In case of an error the caching will not be enabled.
185fn setup_wasmtime_caching(
186	cache_path: &Path,
187	config: &mut wasmtime::Config,
188) -> std::result::Result<(), String> {
189	use std::fs;
190
191	let wasmtime_cache_root = cache_path.join("wasmtime");
192	fs::create_dir_all(&wasmtime_cache_root)
193		.map_err(|err| format!("cannot create the dirs to cache: {}", err))?;
194
195	let mut cache_config = CacheConfig::new();
196	cache_config.with_directory(cache_path);
197
198	let cache =
199		Cache::new(cache_config).map_err(|err| format!("failed to initiate Cache: {err:?}"))?;
200
201	config.cache(Some(cache));
202
203	Ok(())
204}
205
206fn common_config(semantics: &Semantics) -> std::result::Result<wasmtime::Config, WasmError> {
207	let mut config = wasmtime::Config::new();
208	config.cranelift_opt_level(wasmtime::OptLevel::SpeedAndSize);
209	config.cranelift_nan_canonicalization(semantics.canonicalize_nans);
210
211	let profiler = match std::env::var_os("WASMTIME_PROFILING_STRATEGY") {
212		Some(os_string) if os_string == "jitdump" => wasmtime::ProfilingStrategy::JitDump,
213		Some(os_string) if os_string == "perfmap" => wasmtime::ProfilingStrategy::PerfMap,
214		None => wasmtime::ProfilingStrategy::None,
215		Some(_) => {
216			// Remember if we have already logged a warning due to an unknown profiling strategy.
217			static UNKNOWN_PROFILING_STRATEGY: AtomicBool = AtomicBool::new(false);
218			// Make sure that the warning will not be relogged regularly.
219			if !UNKNOWN_PROFILING_STRATEGY.swap(true, Ordering::Relaxed) {
220				log::warn!("WASMTIME_PROFILING_STRATEGY is set to unknown value, ignored.");
221			}
222			wasmtime::ProfilingStrategy::None
223		},
224	};
225	config.profiler(profiler);
226
227	let native_stack_max = match semantics.deterministic_stack_limit {
228		Some(DeterministicStackLimit { native_stack_max, .. }) => native_stack_max,
229
230		// In `wasmtime` 0.35 the default stack size limit was changed from 1MB to 512KB.
231		//
232		// This broke at least one parachain which depended on the original 1MB limit,
233		// so here we restore it to what it was originally.
234		None => 1024 * 1024,
235	};
236
237	config.max_wasm_stack(native_stack_max as usize);
238
239	config.parallel_compilation(semantics.parallel_compilation);
240
241	// Be clear and specific about the extensions we support. If an update brings new features
242	// they should be introduced here as well.
243	config.wasm_reference_types(semantics.wasm_reference_types);
244	config.wasm_simd(semantics.wasm_simd);
245	config.wasm_relaxed_simd(semantics.wasm_simd);
246	config.wasm_bulk_memory(semantics.wasm_bulk_memory);
247	config.wasm_multi_value(semantics.wasm_multi_value);
248	config.wasm_multi_memory(false);
249	config.wasm_threads(false);
250	config.wasm_memory64(false);
251	config.wasm_tail_call(false);
252	config.wasm_extended_const(false);
253
254	let (use_pooling, use_cow) = match semantics.instantiation_strategy {
255		InstantiationStrategy::PoolingCopyOnWrite => (true, true),
256		InstantiationStrategy::Pooling => (true, false),
257		InstantiationStrategy::RecreateInstanceCopyOnWrite => (false, true),
258		InstantiationStrategy::RecreateInstance => (false, false),
259	};
260
261	const WASM_PAGE_SIZE: u64 = 65536;
262
263	config.memory_init_cow(use_cow);
264	config.memory_guaranteed_dense_image_size(match semantics.heap_alloc_strategy {
265		HeapAllocStrategy::Dynamic { maximum_pages } => {
266			maximum_pages.map(|p| p as u64 * WASM_PAGE_SIZE).unwrap_or(u64::MAX)
267		},
268		HeapAllocStrategy::Static { .. } => u64::MAX,
269	});
270
271	if use_pooling {
272		const MAX_WASM_PAGES: u64 = 0x10000;
273
274		let memory_pages = match semantics.heap_alloc_strategy {
275			HeapAllocStrategy::Dynamic { maximum_pages } => {
276				maximum_pages.map(|p| p as u64).unwrap_or(MAX_WASM_PAGES)
277			},
278			HeapAllocStrategy::Static { .. } => MAX_WASM_PAGES,
279		};
280
281		let mut pooling_config = wasmtime::PoolingAllocationConfig::default();
282		pooling_config
283			.max_unused_warm_slots(4)
284			// Pooling needs a bunch of hard limits to be set; if we go over
285			// any of these then the instantiation will fail.
286			//
287			// Current minimum values for kusama (as of 2022-04-14):
288			//   size: 32384
289			//   table_elements: 1249
290			//   memory_pages: 2070
291			.max_core_instance_size(512 * 1024)
292			.table_elements(8192)
293			.max_memory_size(memory_pages as usize * WASM_PAGE_SIZE as usize)
294			.total_tables(MAX_INSTANCE_COUNT)
295			.total_memories(MAX_INSTANCE_COUNT)
296			// This determines how many instances of the module can be
297			// instantiated in parallel from the same `Module`.
298			.total_core_instances(MAX_INSTANCE_COUNT);
299
300		config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling(pooling_config));
301	}
302
303	Ok(config)
304}
305
306/// Knobs for deterministic stack height limiting.
307///
308/// The WebAssembly standard defines a call/value stack but it doesn't say anything about its
309/// size except that it has to be finite. The implementations are free to choose their own notion
310/// of limit: some may count the number of calls or values, others would rely on the host machine
311/// stack and trap on reaching a guard page.
312///
313/// This obviously is a source of non-determinism during execution. This feature can be used
314/// to instrument the code so that it will count the depth of execution in some deterministic
315/// way (the machine stack limit should be so high that the deterministic limit always triggers
316/// first).
317///
318/// The deterministic stack height limiting feature allows to instrument the code so that it will
319/// count the number of items that may be on the stack. This counting will only act as an rough
320/// estimate of the actual stack limit in wasmtime. This is because wasmtime measures it's stack
321/// usage in bytes.
322///
323/// The actual number of bytes consumed by a function is not trivial to compute  without going
324/// through full compilation. Therefore, it's expected that `native_stack_max` is greatly
325/// overestimated and thus never reached in practice. The stack overflow check introduced by the
326/// instrumentation and that relies on the logical item count should be reached first.
327///
328/// See [here][stack_height] for more details of the instrumentation
329///
330/// [stack_height]: https://github.com/paritytech/wasm-instrument/blob/master/src/stack_limiter/mod.rs
331#[derive(Clone)]
332pub struct DeterministicStackLimit {
333	/// A number of logical "values" that can be pushed on the wasm stack. A trap will be triggered
334	/// if exceeded.
335	///
336	/// A logical value is a local, an argument or a value pushed on operand stack.
337	pub logical_max: u32,
338	/// The maximum number of bytes for stack used by wasmtime JITed code.
339	///
340	/// It's not specified how much bytes will be consumed by a stack frame for a given wasm
341	/// function after translation into machine code. It is also not quite trivial.
342	///
343	/// Therefore, this number should be chosen conservatively. It must be so large so that it can
344	/// fit the [`logical_max`](Self::logical_max) logical values on the stack, according to the
345	/// current instrumentation algorithm.
346	///
347	/// This value cannot be 0.
348	pub native_stack_max: u32,
349}
350
351/// The instantiation strategy to use for the WASM executor.
352///
353/// All of the CoW strategies (with `CopyOnWrite` suffix) are only supported when either:
354///   a) we're running on Linux,
355///   b) we're running on an Unix-like system and we're precompiling
356///      our module beforehand and instantiating from a file.
357///
358/// If the CoW variant of a strategy is unsupported the executor will
359/// fall back to the non-CoW equivalent.
360#[non_exhaustive]
361#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
362pub enum InstantiationStrategy {
363	/// Pool the instances to avoid initializing everything from scratch
364	/// on each instantiation. Use copy-on-write memory when possible.
365	///
366	/// This is the fastest instantiation strategy.
367	PoolingCopyOnWrite,
368
369	/// Recreate the instance from scratch on every instantiation.
370	/// Use copy-on-write memory when possible.
371	RecreateInstanceCopyOnWrite,
372
373	/// Pool the instances to avoid initializing everything from scratch
374	/// on each instantiation.
375	Pooling,
376
377	/// Recreate the instance from scratch on every instantiation. Very slow.
378	RecreateInstance,
379}
380
381enum InternalInstantiationStrategy {
382	Builtin,
383}
384
385#[derive(Clone)]
386pub struct Semantics {
387	/// The instantiation strategy to use.
388	pub instantiation_strategy: InstantiationStrategy,
389
390	/// Specifying `Some` will enable deterministic stack height. That is, all executor
391	/// invocations will reach stack overflow at the exactly same point across different wasmtime
392	/// versions and architectures.
393	///
394	/// This is achieved by a combination of running an instrumentation pass on input code and
395	/// configuring wasmtime accordingly.
396	///
397	/// Since this feature depends on instrumentation, it can be set only if runtime is
398	/// instantiated using the runtime blob, e.g. using [`create_runtime`].
399	// I.e. if [`CodeSupplyMode::Verbatim`] is used.
400	pub deterministic_stack_limit: Option<DeterministicStackLimit>,
401
402	/// Controls whether wasmtime should compile floating point in a way that doesn't allow for
403	/// non-determinism.
404	///
405	/// By default, the wasm spec allows some local non-determinism wrt. certain floating point
406	/// operations. Specifically, those operations that are not defined to operate on bits (e.g.
407	/// fneg) can produce NaN values. The exact bit pattern for those is not specified and may
408	/// depend on the particular machine that executes wasmtime generated JITed machine code. That
409	/// is a source of non-deterministic values.
410	///
411	/// The classical runtime environment for Substrate allowed it and punted this on the runtime
412	/// developers. For PVFs, we want to ensure that execution is deterministic though. Therefore,
413	/// for PVF execution this flag is meant to be turned on.
414	pub canonicalize_nans: bool,
415
416	/// Configures wasmtime to use multiple threads for compiling.
417	pub parallel_compilation: bool,
418
419	/// The heap allocation strategy to use.
420	pub heap_alloc_strategy: HeapAllocStrategy,
421
422	/// Enables WASM Multi-Value proposal
423	pub wasm_multi_value: bool,
424
425	/// Enables WASM Bulk Memory Operations proposal
426	pub wasm_bulk_memory: bool,
427
428	/// Enables WASM Reference Types proposal
429	pub wasm_reference_types: bool,
430
431	/// Enables WASM Fixed-Width SIMD proposal
432	pub wasm_simd: bool,
433}
434
435#[derive(Clone)]
436pub struct Config {
437	/// The WebAssembly standard requires all imports of an instantiated module to be resolved,
438	/// otherwise, the instantiation fails. If this option is set to `true`, then this behavior is
439	/// overridden and imports that are requested by the module and not provided by the host
440	/// functions will be resolved using stubs. These stubs will trap upon a call.
441	pub allow_missing_func_imports: bool,
442
443	/// A directory in which wasmtime can store its compiled artifacts cache.
444	pub cache_path: Option<PathBuf>,
445
446	/// Tuning of various semantics of the wasmtime executor.
447	pub semantics: Semantics,
448}
449
450enum CodeSupplyMode<'a> {
451	/// The runtime is instantiated using the given runtime blob.
452	Fresh(RuntimeBlob),
453
454	/// The runtime is instantiated using a precompiled module at the given path.
455	///
456	/// This assumes that the code is already prepared for execution and the same `Config` was
457	/// used.
458	///
459	/// We use a `Path` here instead of simply passing a byte slice to allow `wasmtime` to
460	/// map the runtime's linear memory on supported platforms in a copy-on-write fashion.
461	Precompiled(&'a Path),
462
463	/// The runtime is instantiated using a precompiled module with the given bytes.
464	///
465	/// This assumes that the code is already prepared for execution and the same `Config` was
466	/// used.
467	PrecompiledBytes(&'a [u8]),
468}
469
470/// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to
471/// machine code, which can be computationally heavy.
472///
473/// The `H` generic parameter is used to statically pass a set of host functions which are exposed
474/// to the runtime.
475pub fn create_runtime<H>(
476	blob: RuntimeBlob,
477	config: Config,
478) -> std::result::Result<WasmtimeRuntime, WasmError>
479where
480	H: HostFunctions,
481{
482	// SAFETY: this is safe because it doesn't use `CodeSupplyMode::Precompiled`.
483	unsafe { do_create_runtime::<H>(CodeSupplyMode::Fresh(blob), config) }
484}
485
486/// The same as [`create_runtime`] but takes a path to a precompiled artifact,
487/// which makes this function considerably faster than [`create_runtime`].
488///
489/// # Safety
490///
491/// The caller must ensure that the compiled artifact passed here was:
492///   1) produced by [`prepare_runtime_artifact`],
493///   2) written to the disk as a file,
494///   3) was not modified,
495///   4) will not be modified while any runtime using this artifact is alive, or is being
496///      instantiated.
497///
498/// Failure to adhere to these requirements might lead to crashes and arbitrary code execution.
499///
500/// It is ok though if the compiled artifact was created by code of another version or with
501/// different configuration flags. In such case the caller will receive an `Err` deterministically.
502pub unsafe fn create_runtime_from_artifact<H>(
503	compiled_artifact_path: &Path,
504	config: Config,
505) -> std::result::Result<WasmtimeRuntime, WasmError>
506where
507	H: HostFunctions,
508{
509	do_create_runtime::<H>(CodeSupplyMode::Precompiled(compiled_artifact_path), config)
510}
511
512/// The same as [`create_runtime`] but takes the bytes of a precompiled artifact,
513/// which makes this function considerably faster than [`create_runtime`],
514/// but slower than the more optimized [`create_runtime_from_artifact`].
515/// This is especially slow on non-Linux Unix systems. Useful in very niche cases.
516///
517/// # Safety
518///
519/// The caller must ensure that the compiled artifact passed here was:
520///   1) produced by [`prepare_runtime_artifact`],
521///   2) was not modified,
522///
523/// Failure to adhere to these requirements might lead to crashes and arbitrary code execution.
524///
525/// It is ok though if the compiled artifact was created by code of another version or with
526/// different configuration flags. In such case the caller will receive an `Err` deterministically.
527pub unsafe fn create_runtime_from_artifact_bytes<H>(
528	compiled_artifact_bytes: &[u8],
529	config: Config,
530) -> std::result::Result<WasmtimeRuntime, WasmError>
531where
532	H: HostFunctions,
533{
534	do_create_runtime::<H>(CodeSupplyMode::PrecompiledBytes(compiled_artifact_bytes), config)
535}
536
537/// # Safety
538///
539/// This is only unsafe if called with [`CodeSupplyMode::Artifact`]. See
540/// [`create_runtime_from_artifact`] to get more details.
541unsafe fn do_create_runtime<H>(
542	code_supply_mode: CodeSupplyMode<'_>,
543	mut config: Config,
544) -> std::result::Result<WasmtimeRuntime, WasmError>
545where
546	H: HostFunctions,
547{
548	replace_strategy_if_broken(&mut config.semantics.instantiation_strategy);
549
550	let mut wasmtime_config = common_config(&config.semantics)?;
551	if let Some(ref cache_path) = config.cache_path {
552		if let Err(reason) = setup_wasmtime_caching(cache_path, &mut wasmtime_config) {
553			log::warn!(
554				"failed to setup wasmtime cache. Performance may degrade significantly: {}.",
555				reason,
556			);
557		}
558	}
559
560	let engine = Engine::new(&wasmtime_config)
561		.map_err(|e| WasmError::Other(format!("cannot create the wasmtime engine: {:#}", e)))?;
562
563	let (module, instantiation_strategy) = match code_supply_mode {
564		CodeSupplyMode::Fresh(blob) => {
565			let blob = prepare_blob_for_compilation(blob, &config.semantics)?;
566			let serialized_blob = blob.clone().serialize();
567
568			let module = wasmtime::Module::new(&engine, &serialized_blob)
569				.map_err(|e| WasmError::Other(format!("cannot create module: {:#}", e)))?;
570
571			match config.semantics.instantiation_strategy {
572				InstantiationStrategy::Pooling
573				| InstantiationStrategy::PoolingCopyOnWrite
574				| InstantiationStrategy::RecreateInstance
575				| InstantiationStrategy::RecreateInstanceCopyOnWrite => {
576					(module, InternalInstantiationStrategy::Builtin)
577				},
578			}
579		},
580		CodeSupplyMode::Precompiled(compiled_artifact_path) => {
581			// SAFETY: The unsafety of `deserialize_file` is covered by this function. The
582			//         responsibilities to maintain the invariants are passed to the caller.
583			//
584			//         See [`create_runtime_from_artifact`] for more details.
585			let module = wasmtime::Module::deserialize_file(&engine, compiled_artifact_path)
586				.map_err(|e| WasmError::Other(format!("cannot deserialize module: {:#}", e)))?;
587
588			(module, InternalInstantiationStrategy::Builtin)
589		},
590		CodeSupplyMode::PrecompiledBytes(compiled_artifact_bytes) => {
591			// SAFETY: The unsafety of `deserialize` is covered by this function. The
592			//         responsibilities to maintain the invariants are passed to the caller.
593			//
594			//         See [`create_runtime_from_artifact_bytes`] for more details.
595			let module = wasmtime::Module::deserialize(&engine, compiled_artifact_bytes)
596				.map_err(|e| WasmError::Other(format!("cannot deserialize module: {:#}", e)))?;
597
598			(module, InternalInstantiationStrategy::Builtin)
599		},
600	};
601
602	let mut linker = wasmtime::Linker::new(&engine);
603	crate::executor::wasmtime::imports::prepare_imports::<H>(
604		&mut linker,
605		&module,
606		config.allow_missing_func_imports,
607	)?;
608
609	let instance_pre = linker
610		.instantiate_pre(&module)
611		.map_err(|e| WasmError::Other(format!("cannot preinstantiate module: {:#}", e)))?;
612
613	Ok(WasmtimeRuntime {
614		engine,
615		instance_pre: Arc::new(instance_pre),
616		instantiation_strategy,
617		instance_counter: Default::default(),
618	})
619}
620
621fn prepare_blob_for_compilation(
622	mut blob: RuntimeBlob,
623	semantics: &Semantics,
624) -> std::result::Result<RuntimeBlob, WasmError> {
625	if let Some(DeterministicStackLimit { logical_max, .. }) = semantics.deterministic_stack_limit {
626		blob = blob.inject_stack_depth_metering(logical_max)?;
627	}
628
629	// We don't actually need the memory to be imported so we can just convert any memory
630	// import into an export with impunity. This simplifies our code since `wasmtime` will
631	// now automatically take care of creating the memory for us, and it is also necessary
632	// to enable `wasmtime`'s instance pooling. (Imported memories are ineligible for pooling.)
633	blob.convert_memory_import_into_export()?;
634	blob.setup_memory_according_to_heap_alloc_strategy(semantics.heap_alloc_strategy)?;
635
636	Ok(blob)
637}
638
639/// Takes a [`RuntimeBlob`] and precompiles it returning the serialized result of compilation. It
640/// can then be used for calling [`create_runtime`] avoiding long compilation times.
641pub fn prepare_runtime_artifact(
642	blob: RuntimeBlob,
643	semantics: &Semantics,
644) -> std::result::Result<Vec<u8>, WasmError> {
645	let mut semantics = semantics.clone();
646	replace_strategy_if_broken(&mut semantics.instantiation_strategy);
647
648	let blob = prepare_blob_for_compilation(blob, &semantics)?;
649
650	let engine = Engine::new(&common_config(&semantics)?)
651		.map_err(|e| WasmError::Other(format!("cannot create the engine: {:#}", e)))?;
652
653	engine
654		.precompile_module(&blob.serialize())
655		.map_err(|e| WasmError::Other(format!("cannot precompile module: {:#}", e)))
656}
657
658fn perform_call(
659	data: &[u8],
660	instance_wrapper: &mut InstanceWrapper,
661	entrypoint: EntryPoint,
662	mut allocator: FreeingBumpHeapAllocator,
663	allocation_stats: &mut Option<AllocationStats>,
664) -> Result<Vec<u8>> {
665	let (data_ptr, data_len) = inject_input_data(instance_wrapper, &mut allocator, data)?;
666
667	let host_state = HostState::new(allocator);
668
669	// Set the host state before calling into wasm.
670	instance_wrapper.store_mut().data_mut().host_state = Some(host_state);
671
672	let ret = entrypoint
673		.call(instance_wrapper.store_mut(), data_ptr, data_len)
674		.map(unpack_ptr_and_len);
675
676	// Reset the host state
677	let host_state = instance_wrapper.store_mut().data_mut().host_state.take().expect(
678		"the host state is always set before calling into WASM so it can't be None here; qed",
679	);
680	*allocation_stats = Some(host_state.allocation_stats());
681
682	let (output_ptr, output_len) = ret?;
683	let output = extract_output_data(instance_wrapper, output_ptr, output_len)?;
684
685	Ok(output)
686}
687
688fn inject_input_data(
689	instance: &mut InstanceWrapper,
690	allocator: &mut FreeingBumpHeapAllocator,
691	data: &[u8],
692) -> Result<(Pointer<u8>, WordSize)> {
693	let mut ctx = instance.store_mut();
694	let memory = ctx.data().memory();
695	let data_len = data.len() as WordSize;
696	let data_ptr = allocator.allocate(&mut MemoryWrapper(&memory, &mut ctx), data_len)?;
697	util::write_memory_from(instance.store_mut(), data_ptr, data)?;
698	Ok((data_ptr, data_len))
699}
700
701fn extract_output_data(
702	instance: &InstanceWrapper,
703	output_ptr: u32,
704	output_len: u32,
705) -> Result<Vec<u8>> {
706	let ctx = instance.store();
707
708	// Do a length check before allocating. The returned output should not be bigger than the
709	// available WASM memory. Otherwise, a malicious parachain can trigger a large allocation,
710	// potentially causing memory exhaustion.
711	//
712	// Get the size of the WASM memory in bytes.
713	let memory_size = ctx.as_context().data().memory().data_size(ctx);
714	if checked_range(output_ptr as usize, output_len as usize, memory_size).is_none() {
715		Err(Error::OutputExceedsBounds)?
716	}
717	let mut output = vec![0; output_len as usize];
718
719	util::read_memory_into(ctx, Pointer::new(output_ptr), &mut output)?;
720	Ok(output)
721}