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