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