1mod wasmer_backend;
24mod wasmi_backend;
25
26use std::{collections::HashMap, pin::Pin, rc::Rc};
27
28use env::Instantiate;
29use gear_sandbox_env as sandbox_env;
30use parity_scale_codec::Decode;
31use sp_wasm_interface_common::{Pointer, Value, WordSize};
32
33use crate::{
34 error::{self, Result},
35 util,
36};
37
38use self::{
39 wasmer_backend::{
40 Backend as WasmerBackend, MemoryWrapper as WasmerMemoryWrapper,
41 StoreRefCell as WasmerStoreRefCell, get_global as wasmer_get_global,
42 instantiate as wasmer_instantiate, invoke as wasmer_invoke,
43 new_memory as wasmer_new_memory, set_global as wasmer_set_global,
44 },
45 wasmi_backend::{
46 Backend as WasmiBackend, MemoryWrapper as WasmiMemoryWrapper,
47 StoreRefCell as WasmiStoreRefCell, get_global as wasmi_get_global,
48 instantiate as wasmi_instantiate, invoke as wasmi_invoke, new_memory as wasmi_new_memory,
49 set_global as wasmi_set_global,
50 },
51};
52
53pub use gear_sandbox_env as env;
54
55type SandboxResult<T> = core::result::Result<T, String>;
56
57#[derive(Copy, Clone, Debug, PartialEq)]
62pub struct SupervisorFuncIndex(usize);
63
64impl From<SupervisorFuncIndex> for usize {
65 fn from(index: SupervisorFuncIndex) -> Self {
66 index.0
67 }
68}
69
70#[derive(Copy, Clone, Debug, PartialEq)]
74struct GuestFuncIndex(usize);
75
76struct GuestToSupervisorFunctionMapping {
78 funcs: Vec<SupervisorFuncIndex>,
82}
83
84impl GuestToSupervisorFunctionMapping {
85 fn new() -> GuestToSupervisorFunctionMapping {
87 GuestToSupervisorFunctionMapping { funcs: Vec::new() }
88 }
89
90 fn define(&mut self, supervisor_func: SupervisorFuncIndex) -> GuestFuncIndex {
93 let idx = self.funcs.len();
94 self.funcs.push(supervisor_func);
95 GuestFuncIndex(idx)
96 }
97
98 fn func_by_guest_index(&self, guest_func_idx: GuestFuncIndex) -> Option<SupervisorFuncIndex> {
100 self.funcs.get(guest_func_idx.0).cloned()
101 }
102}
103
104struct Imports {
106 func_map: HashMap<(String, String), GuestFuncIndex>,
108
109 memories_map: HashMap<(String, String), Memory>,
111}
112
113impl Imports {
114 fn func_by_name(&self, module_name: &str, func_name: &str) -> Option<GuestFuncIndex> {
115 self.func_map
116 .get(&(module_name.to_owned(), func_name.to_string()))
117 .cloned()
118 }
119
120 fn memory_by_name(&self, module_name: &str, memory_name: &str) -> Option<Memory> {
121 self.memories_map
122 .get(&(module_name.to_string(), memory_name.to_string()))
123 .cloned()
124 }
125}
126
127pub trait SupervisorContext {
129 fn invoke(
143 &mut self,
144 invoke_args_ptr: Pointer<u8>,
145 invoke_args_len: WordSize,
146 func_idx: SupervisorFuncIndex,
147 ) -> Result<i64>;
148
149 fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> SandboxResult<()>;
151
152 fn read_memory(&self, address: Pointer<u8>, size: WordSize) -> Result<Vec<u8>> {
154 let mut vec = vec![0; size as usize];
155 self.read_memory_into(address, &mut vec)?;
156 Ok(vec)
157 }
158
159 fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> SandboxResult<()>;
161
162 fn allocate_memory(&mut self, size: WordSize) -> SandboxResult<Pointer<u8>>;
164
165 fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> SandboxResult<()>;
167}
168
169enum BackendInstanceBundle {
171 Wasmi {
173 instance: wasmi::Instance,
175 store: Rc<WasmiStoreRefCell>,
177 },
178
179 Wasmer {
181 instance: wasmer::Instance,
183 store: Rc<WasmerStoreRefCell>,
185 },
186}
187
188pub struct SandboxInstance {
203 backend_instance: BackendInstanceBundle,
204}
205
206impl SandboxInstance {
207 pub fn invoke(
212 &self,
213 export_name: &str,
214 args: &[Value],
215 supervisor_context: &mut dyn SupervisorContext,
216 ) -> std::result::Result<Option<Value>, error::Error> {
217 match &self.backend_instance {
218 BackendInstanceBundle::Wasmi { instance, store } => {
219 wasmi_invoke(instance, store, export_name, args, supervisor_context)
220 }
221
222 BackendInstanceBundle::Wasmer { instance, store } => {
223 wasmer_invoke(instance, store, export_name, args, supervisor_context)
224 }
225 }
226 }
227
228 pub fn get_global_val(&self, name: &str) -> Option<Value> {
232 match &self.backend_instance {
233 BackendInstanceBundle::Wasmi { instance, store } => {
234 wasmi_get_global(instance, &store.borrow(), name)
235 }
236
237 BackendInstanceBundle::Wasmer { instance, store } => {
238 wasmer_get_global(instance, &mut store.borrow_mut(), name)
239 }
240 }
241 }
242
243 pub fn set_global_val(
247 &self,
248 name: &str,
249 value: Value,
250 ) -> std::result::Result<Option<()>, error::Error> {
251 match &self.backend_instance {
252 BackendInstanceBundle::Wasmi { instance, store } => {
253 wasmi_set_global(instance, &mut store.borrow_mut(), name, value)
254 }
255
256 BackendInstanceBundle::Wasmer { instance, store } => {
257 wasmer_set_global(instance, &mut store.borrow_mut(), name, value)
258 }
259 }
260 }
261
262 pub unsafe fn signal_handler_get_global_val(&self, name: &str) -> Option<Value> {
270 match &self.backend_instance {
271 BackendInstanceBundle::Wasmi { instance, store } => unsafe {
272 wasmi_get_global(instance, &*store.as_ptr(), name)
273 },
274
275 BackendInstanceBundle::Wasmer { instance, store } => unsafe {
276 wasmer_get_global(instance, &mut *store.as_ptr(), name)
278 },
279 }
280 }
281
282 pub unsafe fn signal_handler_set_global_val(
290 &self,
291 name: &str,
292 value: Value,
293 ) -> Result<Option<()>> {
294 match &self.backend_instance {
295 BackendInstanceBundle::Wasmi { instance, store } => unsafe {
296 wasmi_set_global(instance, &mut *store.as_ptr(), name, value)
298 },
299
300 BackendInstanceBundle::Wasmer { instance, store } => unsafe {
301 wasmer_set_global(instance, &mut *store.as_ptr(), name, value)
303 },
304 }
305 }
306}
307
308pub enum InstantiationError {
310 EnvironmentDefinitionCorrupted,
313 ModuleDecoding,
315 Instantiation,
318 StartTrapped,
321 CpuFeature,
323}
324
325fn decode_environment_definition(
326 mut raw_env_def: &[u8],
327 memories: &[Option<Memory>],
328) -> std::result::Result<(Imports, GuestToSupervisorFunctionMapping), InstantiationError> {
329 let env_def = sandbox_env::EnvironmentDefinition::decode(&mut raw_env_def)
330 .map_err(|_| InstantiationError::EnvironmentDefinitionCorrupted)?;
331
332 let mut func_map = HashMap::new();
333 let mut memories_map = HashMap::new();
334 let mut guest_to_supervisor_mapping = GuestToSupervisorFunctionMapping::new();
335
336 for entry in &env_def.entries {
337 let module = entry.module_name.clone();
338 let field = entry.field_name.clone();
339
340 match entry.entity {
341 sandbox_env::ExternEntity::Function(func_idx) => {
342 let externals_idx =
343 guest_to_supervisor_mapping.define(SupervisorFuncIndex(func_idx as usize));
344 func_map.insert((module, field), externals_idx);
345 }
346 sandbox_env::ExternEntity::Memory(memory_idx) => {
347 let memory_ref = memories
348 .get(memory_idx as usize)
349 .cloned()
350 .ok_or(InstantiationError::EnvironmentDefinitionCorrupted)?
351 .ok_or(InstantiationError::EnvironmentDefinitionCorrupted)?;
352 memories_map.insert((module, field), memory_ref);
353 }
354 }
355 }
356
357 Ok((
358 Imports {
359 func_map,
360 memories_map,
361 },
362 guest_to_supervisor_mapping,
363 ))
364}
365
366pub struct GuestEnvironment {
368 imports: Imports,
370
371 guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping,
373}
374
375impl GuestEnvironment {
376 pub fn decode<DT>(
380 store: &SandboxComponents<DT>,
381 raw_env_def: &[u8],
382 ) -> std::result::Result<Self, InstantiationError> {
383 let (imports, guest_to_supervisor_mapping) =
384 decode_environment_definition(raw_env_def, &store.memories)?;
385 Ok(Self {
386 imports,
387 guest_to_supervisor_mapping,
388 })
389 }
390}
391
392#[must_use]
396pub struct UnregisteredInstance {
397 sandbox_instance: SandboxInstance,
398}
399
400impl UnregisteredInstance {
401 pub fn register<DT>(self, store: &mut SandboxComponents<DT>, dispatch_thunk: DT) -> u32 {
403 store.register_sandbox_instance(self.sandbox_instance, dispatch_thunk)
405 }
406}
407
408#[atomic_enum::atomic_enum]
410pub enum SandboxBackend {
411 Wasmi,
413
414 Wasmer,
416}
417
418#[derive(Clone, Debug)]
420pub enum Memory {
421 Wasmi(WasmiMemoryWrapper),
423
424 Wasmer(WasmerMemoryWrapper),
426}
427
428impl Memory {
429 pub fn as_wasmi(&self) -> Option<WasmiMemoryWrapper> {
431 match self {
432 Memory::Wasmi(memory) => Some(memory.clone()),
433
434 Memory::Wasmer(_) => None,
435 }
436 }
437
438 pub fn as_wasmer(&self) -> Option<WasmerMemoryWrapper> {
440 match self {
441 Memory::Wasmer(memory) => Some(memory.clone()),
442 Memory::Wasmi(_) => None,
443 }
444 }
445}
446
447impl util::MemoryTransfer for Memory {
448 fn read(&self, source_addr: Pointer<u8>, size: usize) -> Result<Vec<u8>> {
449 match self {
450 Memory::Wasmi(sandboxed_memory) => sandboxed_memory.read(source_addr, size),
451
452 Memory::Wasmer(sandboxed_memory) => sandboxed_memory.read(source_addr, size),
453 }
454 }
455
456 fn read_into(&self, source_addr: Pointer<u8>, destination: &mut [u8]) -> Result<()> {
457 match self {
458 Memory::Wasmi(sandboxed_memory) => sandboxed_memory.read_into(source_addr, destination),
459
460 Memory::Wasmer(sandboxed_memory) => {
461 sandboxed_memory.read_into(source_addr, destination)
462 }
463 }
464 }
465
466 fn write_from(&self, dest_addr: Pointer<u8>, source: &[u8]) -> Result<()> {
467 match self {
468 Memory::Wasmi(sandboxed_memory) => sandboxed_memory.write_from(dest_addr, source),
469
470 Memory::Wasmer(sandboxed_memory) => sandboxed_memory.write_from(dest_addr, source),
471 }
472 }
473
474 fn memory_grow(&mut self, pages: u32) -> Result<u32> {
475 match self {
476 Memory::Wasmi(sandboxed_memory) => sandboxed_memory.memory_grow(pages),
477
478 Memory::Wasmer(sandboxed_memory) => sandboxed_memory.memory_grow(pages),
479 }
480 }
481
482 fn memory_size(&mut self) -> u32 {
483 match self {
484 Memory::Wasmi(sandboxed_memory) => sandboxed_memory.memory_size(),
485
486 Memory::Wasmer(sandboxed_memory) => sandboxed_memory.memory_size(),
487 }
488 }
489
490 fn get_buff(&mut self) -> *mut u8 {
491 match self {
492 Memory::Wasmi(sandboxed_memory) => sandboxed_memory.get_buff(),
493
494 Memory::Wasmer(sandboxed_memory) => sandboxed_memory.get_buff(),
495 }
496 }
497}
498
499enum BackendContext {
501 Wasmi(WasmiBackend),
503
504 Wasmer(WasmerBackend),
506}
507
508impl BackendContext {
509 pub fn new(backend: SandboxBackend) -> BackendContext {
510 match backend {
511 SandboxBackend::Wasmi => BackendContext::Wasmi(WasmiBackend::new()),
512
513 SandboxBackend::Wasmer => BackendContext::Wasmer(WasmerBackend::new()),
514 }
515 }
516}
517
518pub struct SandboxComponents<DT> {
522 instances: Vec<Option<(Pin<Rc<SandboxInstance>>, DT)>>,
526 memories: Vec<Option<Memory>>,
528 backend_context: BackendContext,
529}
530
531impl<DT: Clone> SandboxComponents<DT> {
532 pub fn new(backend: SandboxBackend) -> Self {
534 SandboxComponents {
535 instances: Vec::new(),
536 memories: Vec::new(),
537 backend_context: BackendContext::new(backend),
538 }
539 }
540
541 pub fn clear(&mut self) {
543 log::trace!("clear; instances cnt = {}", self.instances.len());
544 self.instances.clear();
545 log::trace!("clear; memories cnt = {}", self.memories.len());
546 self.memories.clear();
547
548 match self.backend_context {
549 BackendContext::Wasmi(_) => {
550 self.backend_context = BackendContext::Wasmi(WasmiBackend::new());
551 }
552 BackendContext::Wasmer(_) => {
553 self.backend_context = BackendContext::Wasmer(WasmerBackend::new());
554 }
555 }
556 }
557
558 pub fn new_memory(&mut self, initial: u32, maximum: u32) -> Result<u32> {
565 let memories = &mut self.memories;
566 let backend_context = &mut self.backend_context;
567
568 let maximum = match maximum {
569 sandbox_env::MEM_UNLIMITED => None,
570 specified_limit => Some(specified_limit),
571 };
572
573 let memory = match backend_context {
574 BackendContext::Wasmi(backend) => wasmi_new_memory(backend, initial, maximum)?,
575
576 BackendContext::Wasmer(backend) => {
577 wasmer_new_memory(backend.store().clone(), initial, maximum)?
578 }
579 };
580
581 let mem_idx = memories.len();
582 memories.push(Some(memory));
583
584 Ok(mem_idx as u32)
585 }
586
587 #[allow(clippy::useless_asref)]
594 pub fn instance(&self, instance_idx: u32) -> Result<Pin<Rc<SandboxInstance>>> {
595 self.instances
596 .get(instance_idx as usize)
597 .ok_or("Trying to access a non-existent instance")?
598 .as_ref()
599 .map(|v| v.0.clone())
600 .ok_or_else(|| "Trying to access a torndown instance".into())
601 }
602
603 #[allow(clippy::useless_asref)]
610 pub fn dispatch_thunk(&self, instance_idx: u32) -> Result<DT> {
611 self.instances
612 .get(instance_idx as usize)
613 .as_ref()
614 .ok_or("Trying to access a non-existent instance")?
615 .as_ref()
616 .map(|v| v.1.clone())
617 .ok_or_else(|| "Trying to access a torndown instance".into())
618 }
619
620 pub fn memory(&self, memory_idx: u32) -> Result<Memory> {
627 self.memories
628 .get(memory_idx as usize)
629 .cloned()
630 .ok_or("Trying to access a non-existent sandboxed memory")?
631 .ok_or_else(|| "Trying to access a torndown sandboxed memory".into())
632 }
633
634 pub fn memory_teardown(&mut self, memory_idx: u32) -> Result<()> {
641 match self.memories.get_mut(memory_idx as usize) {
642 None => Err("Trying to teardown a non-existent sandboxed memory".into()),
643 Some(None) => Err("Double teardown of a sandboxed memory".into()),
644 Some(memory) => {
645 *memory = None;
646 Ok(())
647 }
648 }
649 }
650
651 pub fn instance_teardown(&mut self, instance_idx: u32) -> Result<()> {
658 match self.instances.get_mut(instance_idx as usize) {
659 None => Err("Trying to teardown a non-existent instance".into()),
660 Some(None) => Err("Double teardown of an instance".into()),
661 Some(instance) => {
662 *instance = None;
663 Ok(())
664 }
665 }
666 }
667
668 pub fn instantiate(
678 &mut self,
679 version: Instantiate,
680 wasm: &[u8],
681 guest_env: GuestEnvironment,
682 supervisor_context: &mut dyn SupervisorContext,
683 ) -> std::result::Result<UnregisteredInstance, InstantiationError> {
684 let sandbox_instance = match self.backend_context {
685 BackendContext::Wasmi(ref context) => {
686 wasmi_instantiate(version, context, wasm, guest_env, supervisor_context)?
687 }
688
689 BackendContext::Wasmer(ref context) => {
690 wasmer_instantiate(version, context, wasm, guest_env, supervisor_context)?
691 }
692 };
693
694 Ok(UnregisteredInstance { sandbox_instance })
695 }
696}
697
698impl<DT> SandboxComponents<DT> {
700 fn register_sandbox_instance(
701 &mut self,
702 sandbox_instance: SandboxInstance,
703 dispatch_thunk: DT,
704 ) -> u32 {
705 let instance_idx = self.instances.len();
706 self.instances
707 .push(Some((Rc::pin(sandbox_instance), dispatch_thunk)));
708 instance_idx as u32
709 }
710}