1 //! Memory management for executable code.
2 
3 use crate::Engine;
4 use crate::prelude::*;
5 use crate::runtime::vm::MmapVec;
6 use alloc::sync::Arc;
7 use core::ops::Range;
8 use object::SectionFlags;
9 use object::endian::Endianness;
10 use object::read::{Object, ObjectSection, elf::ElfFile64};
11 use wasmtime_environ::{Trap, lookup_trap_code, obj};
12 use wasmtime_unwinder::ExceptionTable;
13 
14 /// Management of executable memory within a `MmapVec`
15 ///
16 /// This type consumes ownership of a region of memory and will manage the
17 /// executable permissions of the contained JIT code as necessary.
18 pub struct CodeMemory {
19     mmap: MmapVec,
20     #[cfg(has_host_compiler_backend)]
21     unwind_registration: Option<crate::runtime::vm::UnwindRegistration>,
22     #[cfg(feature = "debug-builtins")]
23     debug_registration: Option<crate::runtime::vm::GdbJitImageRegistration>,
24     published: bool,
25     enable_branch_protection: bool,
26     needs_executable: bool,
27     #[cfg(feature = "debug-builtins")]
28     has_native_debug_info: bool,
29     custom_code_memory: Option<Arc<dyn CustomCodeMemory>>,
30 
31     // Ranges within `self.mmap` of where the particular sections lie.
32     text: Range<usize>,
33     unwind: Range<usize>,
34     trap_data: Range<usize>,
35     wasm_data: Range<usize>,
36     address_map_data: Range<usize>,
37     stack_map_data: Range<usize>,
38     exception_data: Range<usize>,
39     frame_tables_data: Range<usize>,
40     func_name_data: Range<usize>,
41     info_data: Range<usize>,
42     wasm_dwarf: Range<usize>,
43 }
44 
45 impl Drop for CodeMemory {
46     fn drop(&mut self) {
47         // If there is a custom code memory handler, restore the
48         // original (non-executable) state of the memory.
49         if let Some(mem) = self.custom_code_memory.as_ref() {
50             if self.published && self.needs_executable {
51                 let text = self.text();
52                 mem.unpublish_executable(text.as_ptr(), text.len())
53                     .expect("Executable memory unpublish failed");
54             }
55         }
56 
57         // Drop the registrations before `self.mmap` since they (implicitly) refer to it.
58         #[cfg(has_host_compiler_backend)]
59         let _ = self.unwind_registration.take();
60         #[cfg(feature = "debug-builtins")]
61         let _ = self.debug_registration.take();
62     }
63 }
64 
65 fn _assert() {
66     fn _assert_send_sync<T: Send + Sync>() {}
67     _assert_send_sync::<CodeMemory>();
68 }
69 
70 /// Interface implemented by an embedder to provide custom
71 /// implementations of code-memory protection and execute permissions.
72 pub trait CustomCodeMemory: Send + Sync {
73     /// The minimal alignment granularity for an address region that
74     /// can be made executable.
75     ///
76     /// Wasmtime does not assume the system page size for this because
77     /// custom code-memory protection can be used when all other uses
78     /// of virtual memory are disabled.
79     fn required_alignment(&self) -> usize;
80 
81     /// Publish a region of memory as executable.
82     ///
83     /// This should update permissions from the default RW
84     /// (readable/writable but not executable) to RX
85     /// (readable/executable but not writable), enforcing W^X
86     /// discipline.
87     ///
88     /// If the platform requires any data/instruction coherence
89     /// action, that should be performed as part of this hook as well.
90     ///
91     /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as
92     /// per `required_alignment()`.
93     fn publish_executable(&self, ptr: *const u8, len: usize) -> anyhow::Result<()>;
94 
95     /// Unpublish a region of memory.
96     ///
97     /// This should perform the opposite effect of `make_executable`,
98     /// switching a range of memory back from RX (readable/executable)
99     /// to RW (readable/writable). It is guaranteed that no code is
100     /// running anymore from this region.
101     ///
102     /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as
103     /// per `required_alignment()`.
104     fn unpublish_executable(&self, ptr: *const u8, len: usize) -> anyhow::Result<()>;
105 }
106 
107 impl CodeMemory {
108     /// Creates a new `CodeMemory` by taking ownership of the provided
109     /// `MmapVec`.
110     ///
111     /// The returned `CodeMemory` manages the internal `MmapVec` and the
112     /// `publish` method is used to actually make the memory executable.
113     pub fn new(engine: &Engine, mmap: MmapVec) -> Result<Self> {
114         let obj = ElfFile64::<Endianness>::parse(&mmap[..])
115             .map_err(obj::ObjectCrateErrorWrapper)
116             .with_context(|| "failed to parse internal compilation artifact")?;
117 
118         let mut text = 0..0;
119         let mut unwind = 0..0;
120         let mut enable_branch_protection = None;
121         let mut needs_executable = true;
122         #[cfg(feature = "debug-builtins")]
123         let mut has_native_debug_info = false;
124         let mut trap_data = 0..0;
125         let mut exception_data = 0..0;
126         let mut frame_tables_data = 0..0;
127         let mut wasm_data = 0..0;
128         let mut address_map_data = 0..0;
129         let mut stack_map_data = 0..0;
130         let mut func_name_data = 0..0;
131         let mut info_data = 0..0;
132         let mut wasm_dwarf = 0..0;
133         for section in obj.sections() {
134             let data = section.data().map_err(obj::ObjectCrateErrorWrapper)?;
135             let name = section.name().map_err(obj::ObjectCrateErrorWrapper)?;
136             let range = subslice_range(data, &mmap);
137 
138             // Double-check that sections are all aligned properly.
139             if section.align() != 0 && data.len() != 0 {
140                 if (data.as_ptr() as u64 - mmap.as_ptr() as u64) % section.align() != 0 {
141                     bail!(
142                         "section `{}` isn't aligned to {:#x}",
143                         section.name().unwrap_or("ERROR"),
144                         section.align()
145                     );
146                 }
147             }
148 
149             match name {
150                 obj::ELF_WASM_BTI => match data.len() {
151                     1 => enable_branch_protection = Some(data[0] != 0),
152                     _ => bail!("invalid `{name}` section"),
153                 },
154                 ".text" => {
155                     text = range;
156 
157                     if let SectionFlags::Elf { sh_flags } = section.flags() {
158                         if sh_flags & obj::SH_WASMTIME_NOT_EXECUTED != 0 {
159                             needs_executable = false;
160                         }
161                     }
162 
163                     // Assert that Cranelift hasn't inserted any calls that need to be
164                     // relocated. We avoid using things like Cranelift's floor/ceil/etc.
165                     // operators in the Wasm-to-Cranelift translator specifically to
166                     // avoid having to do any relocations here. This also ensures that
167                     // all builtins use the same trampoline mechanism.
168                     assert!(section.relocations().next().is_none());
169                 }
170                 #[cfg(has_host_compiler_backend)]
171                 crate::runtime::vm::UnwindRegistration::SECTION_NAME => unwind = range,
172                 obj::ELF_WASM_DATA => wasm_data = range,
173                 obj::ELF_WASMTIME_ADDRMAP => address_map_data = range,
174                 obj::ELF_WASMTIME_STACK_MAP => stack_map_data = range,
175                 obj::ELF_WASMTIME_TRAPS => trap_data = range,
176                 obj::ELF_WASMTIME_EXCEPTIONS => exception_data = range,
177                 obj::ELF_WASMTIME_FRAMES => frame_tables_data = range,
178                 obj::ELF_NAME_DATA => func_name_data = range,
179                 obj::ELF_WASMTIME_INFO => info_data = range,
180                 obj::ELF_WASMTIME_DWARF => wasm_dwarf = range,
181                 #[cfg(feature = "debug-builtins")]
182                 ".debug_info" => has_native_debug_info = true,
183 
184                 _ => log::debug!("ignoring section {name}"),
185             }
186         }
187 
188         // require mutability even when this is turned off
189         #[cfg(not(has_host_compiler_backend))]
190         let _ = &mut unwind;
191 
192         // Ensure that the exception table is well-formed. This parser
193         // construction is cheap: it reads the header and validates
194         // ranges but nothing else. We do this only in debug-assertion
195         // builds because we otherwise require for safety that the
196         // compiled artifact is as-produced-by this version of
197         // Wasmtime, and we should always produce a correct exception
198         // table (i.e., we are not expecting untrusted data here).
199         if cfg!(debug_assertions) {
200             let _ = ExceptionTable::parse(&mmap[exception_data.clone()])?;
201         }
202 
203         Ok(Self {
204             mmap,
205             #[cfg(has_host_compiler_backend)]
206             unwind_registration: None,
207             #[cfg(feature = "debug-builtins")]
208             debug_registration: None,
209             published: false,
210             enable_branch_protection: enable_branch_protection
211                 .ok_or_else(|| anyhow!("missing `{}` section", obj::ELF_WASM_BTI))?,
212             needs_executable,
213             #[cfg(feature = "debug-builtins")]
214             has_native_debug_info,
215             custom_code_memory: engine.custom_code_memory().cloned(),
216             text,
217             unwind,
218             trap_data,
219             address_map_data,
220             stack_map_data,
221             exception_data,
222             frame_tables_data,
223             func_name_data,
224             wasm_dwarf,
225             info_data,
226             wasm_data,
227         })
228     }
229 
230     /// Returns a reference to the underlying `MmapVec` this memory owns.
231     #[inline]
232     pub fn mmap(&self) -> &MmapVec {
233         &self.mmap
234     }
235 
236     /// Returns the contents of the text section of the ELF executable this
237     /// represents.
238     #[inline]
239     pub fn text(&self) -> &[u8] {
240         &self.mmap[self.text.clone()]
241     }
242 
243     /// Returns the contents of the `ELF_WASMTIME_DWARF` section.
244     #[inline]
245     pub fn wasm_dwarf(&self) -> &[u8] {
246         &self.mmap[self.wasm_dwarf.clone()]
247     }
248 
249     /// Returns the data in the `ELF_NAME_DATA` section.
250     #[inline]
251     pub fn func_name_data(&self) -> &[u8] {
252         &self.mmap[self.func_name_data.clone()]
253     }
254 
255     /// Returns the concatenated list of all data associated with this wasm
256     /// module.
257     ///
258     /// This is used for initialization of memories and all data ranges stored
259     /// in a `Module` are relative to the slice returned here.
260     #[inline]
261     pub fn wasm_data(&self) -> &[u8] {
262         &self.mmap[self.wasm_data.clone()]
263     }
264 
265     /// Returns the encoded address map section used to pass to
266     /// `wasmtime_environ::lookup_file_pos`.
267     #[inline]
268     pub fn address_map_data(&self) -> &[u8] {
269         &self.mmap[self.address_map_data.clone()]
270     }
271 
272     /// Returns the encoded stack map section used to pass to
273     /// `wasmtime_environ::StackMap::lookup`.
274     pub fn stack_map_data(&self) -> &[u8] {
275         &self.mmap[self.stack_map_data.clone()]
276     }
277 
278     /// Returns the encoded exception-tables section to pass to
279     /// `wasmtime_unwinder::ExceptionTable::parse`.
280     pub fn exception_tables(&self) -> &[u8] {
281         &self.mmap[self.exception_data.clone()]
282     }
283 
284     /// Returns the encoded frame-tables section to pass to
285     /// `wasmtime_environ::FrameTable::parse`.
286     pub fn frame_tables(&self) -> &[u8] {
287         &self.mmap[self.frame_tables_data.clone()]
288     }
289 
290     /// Returns the contents of the `ELF_WASMTIME_INFO` section, or an empty
291     /// slice if it wasn't found.
292     #[inline]
293     pub fn wasmtime_info(&self) -> &[u8] {
294         &self.mmap[self.info_data.clone()]
295     }
296 
297     /// Returns the contents of the `ELF_WASMTIME_TRAPS` section, or an empty
298     /// slice if it wasn't found.
299     #[inline]
300     pub fn trap_data(&self) -> &[u8] {
301         &self.mmap[self.trap_data.clone()]
302     }
303 
304     /// Publishes the internal ELF image to be ready for execution.
305     ///
306     /// This method can only be called once and will panic if called twice. This
307     /// will parse the ELF image from the original `MmapVec` and do everything
308     /// necessary to get it ready for execution, including:
309     ///
310     /// * Change page protections from read/write to read/execute.
311     /// * Register unwinding information with the OS
312     /// * Register this image with the debugger if native DWARF is present
313     ///
314     /// After this function executes all JIT code should be ready to execute.
315     pub fn publish(&mut self) -> Result<()> {
316         assert!(!self.published);
317         self.published = true;
318 
319         if self.text().is_empty() {
320             return Ok(());
321         }
322 
323         // The unsafety here comes from a few things:
324         //
325         // * We're actually updating some page protections to executable memory.
326         //
327         // * We're registering unwinding information which relies on the
328         //   correctness of the information in the first place. This applies to
329         //   both the actual unwinding tables as well as the validity of the
330         //   pointers we pass in itself.
331         unsafe {
332             // Next freeze the contents of this image by making all of the
333             // memory readonly. Nothing after this point should ever be modified
334             // so commit everything. For a compiled-in-memory image this will
335             // mean IPIs to evict writable mappings from other cores. For
336             // loaded-from-disk images this shouldn't result in IPIs so long as
337             // there weren't any relocations because nothing should have
338             // otherwise written to the image at any point either.
339             //
340             // Note that if virtual memory is disabled this is skipped because
341             // we aren't able to make it readonly, but this is just a
342             // defense-in-depth measure and isn't required for correctness.
343             #[cfg(has_virtual_memory)]
344             if self.mmap.supports_virtual_memory() {
345                 self.mmap.make_readonly(0..self.mmap.len())?;
346             }
347 
348             // Switch the executable portion from readonly to read/execute.
349             if self.needs_executable {
350                 if !self.custom_publish()? {
351                     if !self.mmap.supports_virtual_memory() {
352                         bail!("this target requires virtual memory to be enabled");
353                     }
354                     #[cfg(has_virtual_memory)]
355                     self.mmap
356                         .make_executable(self.text.clone(), self.enable_branch_protection)
357                         .context("unable to make memory executable")?;
358                 }
359             }
360 
361             // With all our memory set up use the platform-specific
362             // `UnwindRegistration` implementation to inform the general
363             // runtime that there's unwinding information available for all
364             // our just-published JIT functions.
365             self.register_unwind_info()?;
366 
367             #[cfg(feature = "debug-builtins")]
368             self.register_debug_image()?;
369         }
370 
371         Ok(())
372     }
373 
374     fn custom_publish(&mut self) -> Result<bool> {
375         if let Some(mem) = self.custom_code_memory.as_ref() {
376             let text = self.text();
377             // The text section should be aligned to
378             // `custom_code_memory.required_alignment()` due to a
379             // combination of two invariants:
380             //
381             // - MmapVec aligns its start address, even in owned-Vec mode; and
382             // - The text segment inside the ELF image will be aligned according
383             //   to the platform's requirements.
384             let text_addr = text.as_ptr() as usize;
385             assert_eq!(text_addr & (mem.required_alignment() - 1), 0);
386 
387             // The custom code memory handler will ensure the
388             // memory is executable and also handle icache
389             // coherence.
390             mem.publish_executable(text.as_ptr(), text.len())?;
391             Ok(true)
392         } else {
393             Ok(false)
394         }
395     }
396 
397     unsafe fn register_unwind_info(&mut self) -> Result<()> {
398         if self.unwind.len() == 0 {
399             return Ok(());
400         }
401         #[cfg(has_host_compiler_backend)]
402         {
403             let text = self.text();
404             let unwind_info = &self.mmap[self.unwind.clone()];
405             let registration = unsafe {
406                 crate::runtime::vm::UnwindRegistration::new(
407                     text.as_ptr(),
408                     unwind_info.as_ptr(),
409                     unwind_info.len(),
410                 )
411                 .context("failed to create unwind info registration")?
412             };
413             self.unwind_registration = Some(registration);
414             return Ok(());
415         }
416         #[cfg(not(has_host_compiler_backend))]
417         {
418             bail!("should not have unwind info for non-native backend")
419         }
420     }
421 
422     #[cfg(feature = "debug-builtins")]
423     fn register_debug_image(&mut self) -> Result<()> {
424         if !self.has_native_debug_info {
425             return Ok(());
426         }
427 
428         // TODO-DebugInfo: we're copying the whole image here, which is pretty wasteful.
429         // Use the existing memory by teaching code here about relocations in DWARF sections
430         // and anything else necessary that is done in "create_gdbjit_image" right now.
431         let image = self.mmap().to_vec();
432         let text: &[u8] = self.text();
433         let bytes = crate::native_debug::create_gdbjit_image(image, (text.as_ptr(), text.len()))?;
434         let reg = crate::runtime::vm::GdbJitImageRegistration::register(bytes);
435         self.debug_registration = Some(reg);
436         Ok(())
437     }
438 
439     /// Looks up the given offset within this module's text section and returns
440     /// the trap code associated with that instruction, if there is one.
441     pub fn lookup_trap_code(&self, text_offset: usize) -> Option<Trap> {
442         lookup_trap_code(self.trap_data(), text_offset)
443     }
444 }
445 
446 /// Returns the range of `inner` within `outer`, such that `outer[range]` is the
447 /// same as `inner`.
448 ///
449 /// This method requires that `inner` is a sub-slice of `outer`, and if that
450 /// isn't true then this method will panic.
451 fn subslice_range(inner: &[u8], outer: &[u8]) -> Range<usize> {
452     if inner.len() == 0 {
453         return 0..0;
454     }
455 
456     assert!(outer.as_ptr() <= inner.as_ptr());
457     assert!((&inner[inner.len() - 1] as *const _) <= (&outer[outer.len() - 1] as *const _));
458 
459     let start = inner.as_ptr() as usize - outer.as_ptr() as usize;
460     start..start + inner.len()
461 }
462