1 mod dl;
2 mod mock;
3 mod sparse_page_data;
4 
5 pub use crate::module::dl::DlModule;
6 pub use crate::module::mock::{MockExportBuilder, MockModuleBuilder};
7 pub use lucet_module::{
8     FunctionHandle, FunctionIndex, FunctionPointer, FunctionSpec, Global, GlobalSpec, GlobalValue,
9     HeapSpec, Signature, TableElement, TrapCode, TrapManifest, ValueType,
10 };
11 
12 use crate::alloc::Limits;
13 use crate::error::Error;
14 use 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)]
23 pub 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).
33 pub trait Module: ModuleInternal {
34     /// Calculate the initial size in bytes of the module's Wasm globals.
initial_globals_size(&self) -> usize35     fn initial_globals_size(&self) -> usize {
36         self.globals().len() * std::mem::size_of::<u64>()
37     }
38 }
39 
40 pub trait ModuleInternal: Send + Sync {
heap_spec(&self) -> Option<&HeapSpec>41     fn heap_spec(&self) -> Option<&HeapSpec>;
42 
43     /// Get the WebAssembly globals of the module.
44     ///
45     /// The indices into the returned slice correspond to the WebAssembly indices of the globals
46     /// (<https://webassembly.github.io/spec/core/syntax/modules.html#syntax-globalidx>)
globals(&self) -> &[GlobalSpec<'_>]47     fn globals(&self) -> &[GlobalSpec<'_>];
48 
get_sparse_page_data(&self, page: usize) -> Option<&[u8]>49     fn get_sparse_page_data(&self, page: usize) -> Option<&[u8]>;
50 
51     /// Get the number of pages in the sparse page data.
sparse_page_data_len(&self) -> usize52     fn sparse_page_data_len(&self) -> usize;
53 
54     /// Get the table elements from the module.
table_elements(&self) -> Result<&[TableElement], Error>55     fn table_elements(&self) -> Result<&[TableElement], Error>;
56 
get_export_func(&self, sym: &str) -> Result<FunctionHandle, Error>57     fn get_export_func(&self, sym: &str) -> Result<FunctionHandle, Error>;
58 
get_func_from_idx(&self, table_id: u32, func_id: u32) -> Result<FunctionHandle, Error>59     fn get_func_from_idx(&self, table_id: u32, func_id: u32) -> Result<FunctionHandle, Error>;
60 
get_start_func(&self) -> Result<Option<FunctionHandle>, Error>61     fn get_start_func(&self) -> Result<Option<FunctionHandle>, Error>;
62 
function_manifest(&self) -> &[FunctionSpec]63     fn function_manifest(&self) -> &[FunctionSpec];
64 
addr_details(&self, addr: *const c_void) -> Result<Option<AddrDetails>, Error>65     fn addr_details(&self, addr: *const c_void) -> Result<Option<AddrDetails>, Error>;
66 
get_signature(&self, fn_id: FunctionIndex) -> &Signature67     fn get_signature(&self, fn_id: FunctionIndex) -> &Signature;
68 
get_signatures(&self) -> &[Signature]69     fn get_signatures(&self) -> &[Signature];
70 
function_handle_from_ptr(&self, ptr: FunctionPointer) -> FunctionHandle71     fn function_handle_from_ptr(&self, ptr: FunctionPointer) -> FunctionHandle {
72         let id = self
73             .function_manifest()
74             .iter()
75             .enumerate()
76             .find(|(_, fn_spec)| fn_spec.ptr() == ptr)
77             .map(|(fn_id, _)| FunctionIndex::from_u32(fn_id as u32))
78             .expect("valid function pointer");
79 
80         FunctionHandle { ptr, id }
81     }
82 
83     /// Look up an instruction pointer in the trap manifest.
84     ///
85     /// This function must be signal-safe.
lookup_trapcode(&self, rip: *const c_void) -> Option<TrapCode>86     fn lookup_trapcode(&self, rip: *const c_void) -> Option<TrapCode> {
87         for fn_spec in self.function_manifest() {
88             if let Some(offset) = fn_spec.relative_addr(rip as u64) {
89                 // the address falls in this trap manifest's function.
90                 // `rip` can only lie in one function, so either
91                 // there's a trap site in this manifest, and that's
92                 // the one we want, or there's none
93                 return fn_spec.traps().and_then(|traps| traps.lookup_addr(offset));
94             }
95         }
96         None
97     }
98 
99     /// Check that the specifications of the WebAssembly module are valid given certain `Limit`s.
100     ///
101     /// Returns a `Result<(), Error>` rather than a boolean in order to provide a richer accounting
102     /// of what may be invalid.
validate_runtime_spec(&self, limits: &Limits) -> Result<(), Error>103     fn validate_runtime_spec(&self, limits: &Limits) -> Result<(), Error> {
104         // Modules without heap specs will not access the heap
105         if let Some(heap) = self.heap_spec() {
106             // Assure that the total reserved + guard regions fit in the address space.
107             // First check makes sure they fit our 32-bit model, and ensures the second
108             // check doesn't overflow.
109             if heap.reserved_size > std::u32::MAX as u64 + 1
110                 || heap.guard_size > std::u32::MAX as u64 + 1
111             {
112                 return Err(lucet_incorrect_module!(
113                     "heap spec sizes would overflow: {:?}",
114                     heap
115                 ));
116             }
117 
118             if heap.reserved_size as usize + heap.guard_size as usize
119                 > limits.heap_address_space_size
120             {
121                 bail_limits_exceeded!("heap spec reserved and guard size: {:?}", heap);
122             }
123 
124             if heap.initial_size as usize > limits.heap_memory_size {
125                 bail_limits_exceeded!("heap spec initial size: {:?}", heap);
126             }
127 
128             if heap.initial_size > heap.reserved_size {
129                 return Err(lucet_incorrect_module!(
130                     "initial heap size greater than reserved size: {:?}",
131                     heap
132                 ));
133             }
134         }
135 
136         if self.globals().len() * std::mem::size_of::<u64>() > limits.globals_size {
137             bail_limits_exceeded!("globals exceed limits");
138         }
139 
140         Ok(())
141     }
142 }
143