gear_sandbox_host/
sandbox.rs

1// This file is part of Gear.
2
3// Copyright (C) Gear Technologies Inc.
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//! This module implements sandboxing support in the runtime.
20//!
21//! Sandboxing is backed by wasmi and wasmer, depending on the configuration.
22
23mod 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/// Index of a function inside the supervisor.
58///
59/// This is a typically an index in the default table of the supervisor, however
60/// the exact meaning of this index is depends on the implementation of dispatch function.
61#[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/// Index of a function within guest index space.
71///
72/// This index is supposed to be used as index for `Externals`.
73#[derive(Copy, Clone, Debug, PartialEq)]
74struct GuestFuncIndex(usize);
75
76/// This struct holds a mapping from guest index space to supervisor.
77struct GuestToSupervisorFunctionMapping {
78    /// Position of elements in this vector are interpreted
79    /// as indices of guest functions and are mapped to
80    /// corresponding supervisor function indices.
81    funcs: Vec<SupervisorFuncIndex>,
82}
83
84impl GuestToSupervisorFunctionMapping {
85    /// Create an empty function mapping
86    fn new() -> GuestToSupervisorFunctionMapping {
87        GuestToSupervisorFunctionMapping { funcs: Vec::new() }
88    }
89
90    /// Add a new supervisor function to the mapping.
91    /// Returns a newly assigned guest function index.
92    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    /// Find supervisor function index by its corresponding guest function index
99    fn func_by_guest_index(&self, guest_func_idx: GuestFuncIndex) -> Option<SupervisorFuncIndex> {
100        self.funcs.get(guest_func_idx.0).cloned()
101    }
102}
103
104/// Holds sandbox function and memory imports and performs name resolution
105struct Imports {
106    /// Maps qualified function name to its guest function index
107    func_map: HashMap<(String, String), GuestFuncIndex>,
108
109    /// Maps qualified field name to its memory reference
110    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
127/// The supervisor context used to execute sandboxed functions.
128pub trait SupervisorContext {
129    /// Invoke a function in the supervisor environment.
130    ///
131    /// This first invokes the dispatch thunk function, passing in the function index of the
132    /// desired function to call and serialized arguments. The thunk calls the desired function
133    /// with the deserialized arguments, then serializes the result into memory and returns
134    /// reference. The pointer to and length of the result in linear memory is encoded into an
135    /// `i64`, with the upper 32 bits representing the pointer and the lower 32 bits representing
136    /// the length.
137    ///
138    /// # Errors
139    ///
140    /// Returns `Err` if the dispatch_thunk function has an incorrect signature or traps during
141    /// execution.
142    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    /// Read memory from `address` into a vector.
150    fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> SandboxResult<()>;
151
152    /// Read memory into the given `dest` buffer from `address`.
153    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    /// Write the given data at `address` into the memory.
160    fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> SandboxResult<()>;
161
162    /// Allocate a memory instance of `size` bytes.
163    fn allocate_memory(&mut self, size: WordSize) -> SandboxResult<Pointer<u8>>;
164
165    /// Deallocate a given memory instance.
166    fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> SandboxResult<()>;
167}
168
169/// Module instance in terms of selected backend
170enum BackendInstanceBundle {
171    /// Wasmi module instance
172    Wasmi {
173        /// Wasmer module instance
174        instance: wasmi::Instance,
175        /// Wasmer store
176        store: Rc<WasmiStoreRefCell>,
177    },
178
179    /// Wasmer module instance and store
180    Wasmer {
181        /// Wasmer module instance
182        instance: wasmer::Instance,
183        /// Wasmer store
184        store: Rc<WasmerStoreRefCell>,
185    },
186}
187
188/// Sandboxed instance of a wasm module.
189///
190/// It's primary purpose is to [`invoke`] exported functions on it.
191///
192/// All imports of this instance are specified at the creation time and
193/// imports are implemented by the supervisor.
194///
195/// Hence, in order to invoke an exported function on a sandboxed module instance,
196/// it's required to provide supervisor externals: it will be used to execute
197/// code in the supervisor context.
198///
199/// This is generic over a supervisor function reference type.
200///
201/// [`invoke`]: #method.invoke
202pub struct SandboxInstance {
203    backend_instance: BackendInstanceBundle,
204}
205
206impl SandboxInstance {
207    /// Invoke an exported function by a name.
208    ///
209    /// `supervisor_externals` is required to execute the implementations
210    /// of the syscalls that published to a sandboxed module instance.
211    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    /// Get the value from a global with the given `name`.
229    ///
230    /// Returns `Some(_)` if the global could be found.
231    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    /// Set the value of a global with the given `name`.
244    ///
245    /// Returns `Ok(Some(()))` if the global could be modified.
246    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    /// Get the value from a global with the given `name`. Only for usage in signal handler.
263    ///
264    /// Returns `Some(_)` if the global has been found.
265    ///
266    /// # Safety
267    ///
268    /// Expected to be called only from signal handler.
269    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                // We cannot use `store.borrow_mut()` in signal handler context because it's already borrowed during `invoke` call.
277                wasmer_get_global(instance, &mut *store.as_ptr(), name)
278            },
279        }
280    }
281
282    /// Set the value of a global with the given `name`. Only for usage in signal handler.
283    ///
284    /// Returns `Ok(Some(()))` if the global could be modified.
285    ///
286    /// # Safety
287    ///
288    /// Expected to be called only from signal handler.
289    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                // We cannot use `store.borrow_mut()` in signal handler context because it's already borrowed during `invoke` call.
297                wasmi_set_global(instance, &mut *store.as_ptr(), name, value)
298            },
299
300            BackendInstanceBundle::Wasmer { instance, store } => unsafe {
301                // We cannot use `store.borrow_mut()` in signal handler context because it's already borrowed during `invoke` call.
302                wasmer_set_global(instance, &mut *store.as_ptr(), name, value)
303            },
304        }
305    }
306}
307
308/// Error occurred during instantiation of a sandboxed module.
309pub enum InstantiationError {
310    /// Something wrong with the environment definition. It either can't
311    /// be decoded, have a reference to a non-existent or torn down memory instance.
312    EnvironmentDefinitionCorrupted,
313    /// Provided module isn't recognized as a valid webassembly binary.
314    ModuleDecoding,
315    /// Module is a well-formed webassembly binary but could not be instantiated. This could
316    /// happen because, e.g. the module imports entries not provided by the environment.
317    Instantiation,
318    /// Module is well-formed, instantiated and linked, but while executing the start function
319    /// a trap was generated.
320    StartTrapped,
321    /// The code was compiled with a CPU feature not available on the host.
322    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
366/// An environment in which the guest module is instantiated.
367pub struct GuestEnvironment {
368    /// Function and memory imports of the guest module
369    imports: Imports,
370
371    /// Supervisor functinons mapped to guest index space
372    guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping,
373}
374
375impl GuestEnvironment {
376    /// Decodes an environment definition from the given raw bytes.
377    ///
378    /// Returns `Err` if the definition cannot be decoded.
379    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/// An unregistered sandboxed instance.
393///
394/// To finish off the instantiation the user must call `register`.
395#[must_use]
396pub struct UnregisteredInstance {
397    sandbox_instance: SandboxInstance,
398}
399
400impl UnregisteredInstance {
401    /// Finalizes instantiation of this module.
402    pub fn register<DT>(self, store: &mut SandboxComponents<DT>, dispatch_thunk: DT) -> u32 {
403        // At last, register the instance.
404        store.register_sandbox_instance(self.sandbox_instance, dispatch_thunk)
405    }
406}
407
408/// Sandbox backend to use
409#[atomic_enum::atomic_enum]
410pub enum SandboxBackend {
411    /// Wasm interpreter
412    Wasmi,
413
414    /// Wasmer environment
415    Wasmer,
416}
417
418/// Memory reference in terms of a selected backend
419#[derive(Clone, Debug)]
420pub enum Memory {
421    /// Wasmi memory reference
422    Wasmi(WasmiMemoryWrapper),
423
424    /// Wasmer memory reference
425    Wasmer(WasmerMemoryWrapper),
426}
427
428impl Memory {
429    /// View as wasmi memory
430    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    /// View as wasmer memory
439    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
499/// Information specific to a particular execution backend
500enum BackendContext {
501    /// Wasmi specific context
502    Wasmi(WasmiBackend),
503
504    /// Wasmer specific context
505    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
518/// This struct keeps track of all sandboxed components.
519///
520/// This is generic over a supervisor function reference type.
521pub struct SandboxComponents<DT> {
522    /// Stores the instance and the dispatch thunk associated to per instance.
523    ///
524    /// Instances are `Some` until torn down.
525    instances: Vec<Option<(Pin<Rc<SandboxInstance>>, DT)>>,
526    /// Memories are `Some` until torn down.
527    memories: Vec<Option<Memory>>,
528    backend_context: BackendContext,
529}
530
531impl<DT: Clone> SandboxComponents<DT> {
532    /// Create a new empty sandbox store.
533    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    /// Clear instance list and memory list.
542    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    /// Create a new memory instance and return it's index.
559    ///
560    /// # Errors
561    ///
562    /// Returns `Err` if the memory couldn't be created.
563    /// Typically happens if `initial` is more than `maximum`.
564    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    /// Returns `SandboxInstance` by `instance_idx`.
588    ///
589    /// # Errors
590    ///
591    /// Returns `Err` If `instance_idx` isn't a valid index of an instance or
592    /// instance is already torndown.
593    #[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    /// Returns dispatch thunk by `instance_idx`.
604    ///
605    /// # Errors
606    ///
607    /// Returns `Err` If `instance_idx` isn't a valid index of an instance or
608    /// instance is already torndown.
609    #[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    /// Returns reference to a memory instance by `memory_idx`.
621    ///
622    /// # Errors
623    ///
624    /// Returns `Err` If `memory_idx` isn't a valid index of an memory or
625    /// if memory has been torn down.
626    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    /// Tear down the memory at the specified index.
635    ///
636    /// # Errors
637    ///
638    /// Returns `Err` if `memory_idx` isn't a valid index of an memory or
639    /// if it has been torn down.
640    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    /// Tear down the instance at the specified index.
652    ///
653    /// # Errors
654    ///
655    /// Returns `Err` if `instance_idx` isn't a valid index of an instance or
656    /// if it has been torn down.
657    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    /// Instantiate a guest module and return it's index in the store.
669    ///
670    /// The guest module's code is specified in `wasm`. Environment that will be available to
671    /// guest module is specified in `guest_env`. A dispatch thunk is used as function that
672    /// handle calls from guests.
673    ///
674    /// Note: Due to borrowing constraints dispatch thunk is now propagated using DTH
675    ///
676    /// Returns uninitialized sandboxed module instance or an instantiation error.
677    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
698// Private routines
699impl<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}