lucet_runtime_internals/
module.rs

1mod dl;
2mod mock;
3mod sparse_page_data;
4
5pub use crate::module::dl::DlModule;
6pub use crate::module::mock::{MockExportBuilder, MockModuleBuilder};
7pub use lucet_module::{
8    FunctionHandle, FunctionIndex, FunctionPointer, FunctionSpec, Global, GlobalSpec, GlobalValue,
9    HeapSpec, Signature, TableElement, TrapCode, TrapManifest, ValueType,
10};
11
12use crate::alloc::Limits;
13use crate::error::Error;
14use libc::c_void;
15
16/// Details about a program address.
17///
18/// It is possible to determine whether an address lies within the module code if the module is
19/// loaded from a shared object. Statically linked modules are not resolvable. Best effort is made
20/// to resolve the symbol the address is found inside, and the file that symbol is found in. See
21/// `dladdr(3)` for more details.
22#[derive(Clone, Debug)]
23pub struct AddrDetails {
24    pub in_module_code: bool,
25    pub file_name: Option<String>,
26    pub sym_name: Option<String>,
27}
28
29/// The read-only parts of a Lucet program, including its code and initial heap configuration.
30///
31/// Types that implement this trait are suitable for use with
32/// [`Region::new_instance()`](trait.Region.html#method.new_instance).
33pub trait Module: ModuleInternal {
34    /// Calculate the initial size in bytes of the module's Wasm globals.
35    fn initial_globals_size(&self) -> usize {
36        self.globals().len() * std::mem::size_of::<u64>()
37    }
38}
39
40pub trait ModuleInternal: Send + Sync {
41    /// Determine whether this module has been instrumented with additional
42    /// instructions that monitor the number of wasm operations executed
43    /// during runtime.
44    fn is_instruction_count_instrumented(&self) -> bool;
45
46    fn heap_spec(&self) -> Option<&HeapSpec>;
47
48    /// Get the WebAssembly globals of the module.
49    ///
50    /// The indices into the returned slice correspond to the WebAssembly indices of the globals
51    /// (<https://webassembly.github.io/spec/core/syntax/modules.html#syntax-globalidx>)
52    fn globals(&self) -> &[GlobalSpec<'_>];
53
54    fn get_sparse_page_data(&self, page: usize) -> Option<&[u8]>;
55
56    /// Get the number of pages in the sparse page data.
57    fn sparse_page_data_len(&self) -> usize;
58
59    /// Get the table elements from the module.
60    fn table_elements(&self) -> Result<&[TableElement], Error>;
61
62    fn get_export_func(&self, sym: &str) -> Result<FunctionHandle, Error>;
63
64    fn get_func_from_idx(&self, table_id: u32, func_id: u32) -> Result<FunctionHandle, Error>;
65
66    fn get_start_func(&self) -> Result<Option<FunctionHandle>, Error>;
67
68    fn function_manifest(&self) -> &[FunctionSpec];
69
70    fn addr_details(&self, addr: *const c_void) -> Result<Option<AddrDetails>, Error>;
71
72    fn get_signature(&self, fn_id: FunctionIndex) -> &Signature;
73
74    fn function_handle_from_ptr(&self, ptr: FunctionPointer) -> FunctionHandle {
75        let id = self
76            .function_manifest()
77            .iter()
78            .enumerate()
79            .find(|(_, fn_spec)| fn_spec.ptr() == ptr)
80            .map(|(fn_id, _)| FunctionIndex::from_u32(fn_id as u32))
81            .expect("valid function pointer");
82
83        FunctionHandle { ptr, id }
84    }
85
86    /// Look up an instruction pointer in the trap manifest.
87    ///
88    /// This function must be signal-safe.
89    fn lookup_trapcode(&self, rip: *const c_void) -> Option<TrapCode> {
90        for fn_spec in self.function_manifest() {
91            if let Some(offset) = fn_spec.relative_addr(rip as u64) {
92                // the address falls in this trap manifest's function.
93                // `rip` can only lie in one function, so either
94                // there's a trap site in this manifest, and that's
95                // the one we want, or there's none
96                return fn_spec.traps().and_then(|traps| traps.lookup_addr(offset));
97            }
98        }
99        None
100    }
101
102    /// Check that the specifications of the WebAssembly module are valid given certain `Limit`s.
103    ///
104    /// Returns a `Result<(), Error>` rather than a boolean in order to provide a richer accounting
105    /// of what may be invalid.
106    fn validate_runtime_spec(&self, limits: &Limits) -> Result<(), Error> {
107        // Modules without heap specs will not access the heap
108        if let Some(heap) = self.heap_spec() {
109            // Assure that the total reserved + guard regions fit in the address space.
110            // First check makes sure they fit our 32-bit model, and ensures the second
111            // check doesn't overflow.
112            if heap.reserved_size > std::u32::MAX as u64 + 1
113                || heap.guard_size > std::u32::MAX as u64 + 1
114            {
115                return Err(lucet_incorrect_module!(
116                    "heap spec sizes would overflow: {:?}",
117                    heap
118                ));
119            }
120
121            if heap.reserved_size as usize + heap.guard_size as usize
122                > limits.heap_address_space_size
123            {
124                bail_limits_exceeded!("heap spec reserved and guard size: {:?}", heap);
125            }
126
127            if heap.initial_size as usize > limits.heap_memory_size {
128                bail_limits_exceeded!("heap spec initial size: {:?}", heap);
129            }
130
131            if heap.initial_size > heap.reserved_size {
132                return Err(lucet_incorrect_module!(
133                    "initial heap size greater than reserved size: {:?}",
134                    heap
135                ));
136            }
137        }
138
139        if self.globals().len() * std::mem::size_of::<u64>() > limits.globals_size {
140            bail_limits_exceeded!("globals exceed limits");
141        }
142
143        Ok(())
144    }
145}