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