referrerpolicy=no-referrer-when-downgrade

sc_executor_wasmtime/
instance_wrapper.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Defines data and logic needed for interaction with an WebAssembly instance of a substrate
20//! runtime module.
21
22use std::sync::Arc;
23
24use crate::runtime::{InstanceCounter, ReleaseInstanceHandle, Store, StoreData};
25use sc_executor_common::error::{Backtrace, Error, MessageWithBacktrace, Result, WasmError};
26use sp_wasm_interface::{Pointer, WordSize};
27use wasmtime::{AsContext, AsContextMut, Engine, Instance, InstancePre, Memory};
28
29/// Wasm blob entry point.
30pub struct EntryPoint(wasmtime::TypedFunc<(u32, u32), u64>);
31
32impl EntryPoint {
33	/// Call this entry point.
34	pub(crate) fn call(
35		&self,
36		store: &mut Store,
37		data_ptr: Pointer<u8>,
38		data_len: WordSize,
39	) -> Result<u64> {
40		let data_ptr = u32::from(data_ptr);
41		let data_len = u32::from(data_len);
42
43		self.0.call(&mut *store, (data_ptr, data_len)).map_err(|trap| {
44			let host_state = store
45				.data_mut()
46				.host_state
47				.as_mut()
48				.expect("host state cannot be empty while a function is being called; qed");
49
50			let backtrace = trap.downcast_ref::<wasmtime::WasmBacktrace>().map(|backtrace| {
51				// The logic to print out a backtrace is somewhat complicated,
52				// so let's get wasmtime to print it out for us.
53				Backtrace { backtrace_string: backtrace.to_string() }
54			});
55
56			if let Some(message) = host_state.take_panic_message() {
57				Error::AbortedDueToPanic(MessageWithBacktrace { message, backtrace })
58			} else {
59				let message = trap.root_cause().to_string();
60				Error::AbortedDueToTrap(MessageWithBacktrace { message, backtrace })
61			}
62		})
63	}
64
65	pub fn direct(
66		func: wasmtime::Func,
67		ctx: impl AsContext,
68	) -> std::result::Result<Self, &'static str> {
69		let entrypoint = func
70			.typed::<(u32, u32), u64>(ctx)
71			.map_err(|_| "Invalid signature for direct entry point")?;
72		Ok(Self(entrypoint))
73	}
74}
75
76/// Wrapper around [`Memory`] that implements [`sc_allocator::Memory`].
77pub(crate) struct MemoryWrapper<'a, C>(pub &'a wasmtime::Memory, pub &'a mut C);
78
79impl<C: AsContextMut> sc_allocator::Memory for MemoryWrapper<'_, C> {
80	fn with_access_mut<R>(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R {
81		run(self.0.data_mut(&mut self.1))
82	}
83
84	fn with_access<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {
85		run(self.0.data(&self.1))
86	}
87
88	fn grow(&mut self, additional: u32) -> std::result::Result<(), ()> {
89		self.0
90			.grow(&mut self.1, additional as u64)
91			.map_err(|e| {
92				log::error!(
93					target: "wasm-executor",
94					"Failed to grow memory by {} pages: {}",
95					additional,
96					e,
97				)
98			})
99			.map(drop)
100	}
101
102	fn pages(&self) -> u32 {
103		self.0.size(&self.1) as u32
104	}
105
106	fn max_pages(&self) -> Option<u32> {
107		self.0.ty(&self.1).maximum().map(|p| p as _)
108	}
109}
110
111/// Wrap the given WebAssembly Instance of a wasm module with Substrate-runtime.
112///
113/// This struct is a handy wrapper around a wasmtime `Instance` that provides substrate specific
114/// routines.
115pub struct InstanceWrapper {
116	instance: Instance,
117	store: Store,
118	// NOTE: We want to decrement the instance counter *after* the store has been dropped
119	// to avoid a potential race condition, so this field must always be kept
120	// as the last field in the struct!
121	_release_instance_handle: ReleaseInstanceHandle,
122}
123
124impl InstanceWrapper {
125	pub(crate) fn new(
126		engine: &Engine,
127		instance_pre: &InstancePre<StoreData>,
128		instance_counter: Arc<InstanceCounter>,
129	) -> Result<Self> {
130		let _release_instance_handle = instance_counter.acquire_instance();
131		let mut store = Store::new(engine, Default::default());
132		let instance = instance_pre.instantiate(&mut store).map_err(|error| {
133			WasmError::Other(format!(
134				"failed to instantiate a new WASM module instance: {:#}",
135				error,
136			))
137		})?;
138
139		let memory = get_linear_memory(&instance, &mut store)?;
140
141		store.data_mut().memory = Some(memory);
142
143		Ok(InstanceWrapper { instance, store, _release_instance_handle })
144	}
145
146	/// Resolves a substrate entrypoint by the given name.
147	///
148	/// An entrypoint must have a signature `(i32, i32) -> i64`, otherwise this function will return
149	/// an error.
150	pub fn resolve_entrypoint(&mut self, method: &str) -> Result<EntryPoint> {
151		// Resolve the requested method and verify that it has a proper signature.
152		let export = self
153			.instance
154			.get_export(&mut self.store, method)
155			.ok_or_else(|| Error::from(format!("Exported method {} is not found", method)))?;
156		let func = export
157			.into_func()
158			.ok_or_else(|| Error::from(format!("Export {} is not a function", method)))?;
159		EntryPoint::direct(func, &self.store).map_err(|_| {
160			Error::from(format!("Exported function '{}' has invalid signature.", method))
161		})
162	}
163
164	/// Reads `__heap_base: i32` global variable and returns it.
165	///
166	/// If it doesn't exist, not a global or of not i32 type returns an error.
167	pub fn extract_heap_base(&mut self) -> Result<u32> {
168		let heap_base_export = self
169			.instance
170			.get_export(&mut self.store, "__heap_base")
171			.ok_or_else(|| Error::from("__heap_base is not found"))?;
172
173		let heap_base_global = heap_base_export
174			.into_global()
175			.ok_or_else(|| Error::from("__heap_base is not a global"))?;
176
177		let heap_base = heap_base_global
178			.get(&mut self.store)
179			.i32()
180			.ok_or_else(|| Error::from("__heap_base is not a i32"))?;
181
182		Ok(heap_base as u32)
183	}
184}
185
186/// Extract linear memory instance from the given instance.
187fn get_linear_memory(instance: &Instance, ctx: impl AsContextMut) -> Result<Memory> {
188	let memory_export = instance
189		.get_export(ctx, "memory")
190		.ok_or_else(|| Error::from("memory is not exported under `memory` name"))?;
191
192	let memory = memory_export
193		.into_memory()
194		.ok_or_else(|| Error::from("the `memory` export should have memory type"))?;
195
196	Ok(memory)
197}
198
199/// Functions related to memory.
200impl InstanceWrapper {
201	pub(crate) fn store(&self) -> &Store {
202		&self.store
203	}
204
205	pub(crate) fn store_mut(&mut self) -> &mut Store {
206		&mut self.store
207	}
208}