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