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     registered: bool,
26     enable_branch_protection: bool,
27     needs_executable: bool,
28     #[cfg(feature = "debug-builtins")]
29     has_native_debug_info: bool,
30     custom_code_memory: Option<Arc<dyn CustomCodeMemory>>,
31 
32     // Ranges within `self.mmap` of where the particular sections lie.
33     text: Range<usize>,
34     unwind: Range<usize>,
35     trap_data: Range<usize>,
36     wasm_data: Range<usize>,
37     address_map_data: Range<usize>,
38     stack_map_data: Range<usize>,
39     exception_data: Range<usize>,
40     frame_tables_data: Range<usize>,
41     func_name_data: Range<usize>,
42     info_data: Range<usize>,
43     wasm_dwarf: Range<usize>,
44 }
45 
46 impl Drop for CodeMemory {
47     fn drop(&mut self) {
48         // If there is a custom code memory handler, restore the
49         // original (non-executable) state of the memory.
50         //
51         // We do this rather than invoking `unpublish()` because we
52         // want to skip the mprotect() if we natively own the mmap and
53         // are going to munmap soon anyway.
54         if let Some(mem) = self.custom_code_memory.as_ref() {
55             if self.published && self.needs_executable {
56                 let text = self.text();
57                 mem.unpublish_executable(text.as_ptr(), text.len())
58                     .expect("Executable memory unpublish failed");
59             }
60         }
61 
62         // Drop the registrations before `self.mmap` since they (implicitly) refer to it.
63         #[cfg(has_host_compiler_backend)]
64         let _ = self.unwind_registration.take();
65         #[cfg(feature = "debug-builtins")]
66         let _ = self.debug_registration.take();
67     }
68 }
69 
70 fn _assert() {
71     fn _assert_send_sync<T: Send + Sync>() {}
72     _assert_send_sync::<CodeMemory>();
73 }
74 
75 /// Interface implemented by an embedder to provide custom
76 /// implementations of code-memory protection and execute permissions.
77 pub trait CustomCodeMemory: Send + Sync {
78     /// The minimal alignment granularity for an address region that
79     /// can be made executable.
80     ///
81     /// Wasmtime does not assume the system page size for this because
82     /// custom code-memory protection can be used when all other uses
83     /// of virtual memory are disabled.
84     fn required_alignment(&self) -> usize;
85 
86     /// Publish a region of memory as executable.
87     ///
88     /// This should update permissions from the default RW
89     /// (readable/writable but not executable) to RX
90     /// (readable/executable but not writable), enforcing W^X
91     /// discipline.
92     ///
93     /// If the platform requires any data/instruction coherence
94     /// action, that should be performed as part of this hook as well.
95     ///
96     /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as
97     /// per `required_alignment()`.
98     fn publish_executable(&self, ptr: *const u8, len: usize) -> crate::Result<()>;
99 
100     /// Unpublish a region of memory.
101     ///
102     /// This should perform the opposite effect of `make_executable`,
103     /// switching a range of memory back from RX (readable/executable)
104     /// to RW (readable/writable). It is guaranteed that no code is
105     /// running anymore from this region.
106     ///
107     /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as
108     /// per `required_alignment()`.
109     fn unpublish_executable(&self, ptr: *const u8, len: usize) -> crate::Result<()>;
110 }
111 
112 impl CodeMemory {
113     /// Creates a new `CodeMemory` by taking ownership of the provided
114     /// `MmapVec`.
115     ///
116     /// The returned `CodeMemory` manages the internal `MmapVec` and the
117     /// `publish` method is used to actually make the memory executable.
118     pub fn new(engine: &Engine, mmap: MmapVec) -> Result<Self> {
119         let obj = ElfFile64::<Endianness>::parse(&mmap[..])
120             .map_err(obj::ObjectCrateErrorWrapper)
121             .with_context(|| "failed to parse internal compilation artifact")?;
122 
123         let mut text = 0..0;
124         let mut unwind = 0..0;
125         let mut enable_branch_protection = None;
126         let mut needs_executable = true;
127         #[cfg(feature = "debug-builtins")]
128         let mut has_native_debug_info = false;
129         let mut trap_data = 0..0;
130         let mut exception_data = 0..0;
131         let mut frame_tables_data = 0..0;
132         let mut wasm_data = 0..0;
133         let mut address_map_data = 0..0;
134         let mut stack_map_data = 0..0;
135         let mut func_name_data = 0..0;
136         let mut info_data = 0..0;
137         let mut wasm_dwarf = 0..0;
138         for section in obj.sections() {
139             let data = section.data().map_err(obj::ObjectCrateErrorWrapper)?;
140             let name = section.name().map_err(obj::ObjectCrateErrorWrapper)?;
141             let range = subslice_range(data, &mmap);
142 
143             // Double-check that sections are all aligned properly.
144             if section.align() != 0 && data.len() != 0 {
145                 if (data.as_ptr() as u64 - mmap.as_ptr() as u64) % section.align() != 0 {
146                     bail!(
147                         "section `{}` isn't aligned to {:#x}",
148                         section.name().unwrap_or("ERROR"),
149                         section.align()
150                     );
151                 }
152             }
153 
154             match name {
155                 obj::ELF_WASM_BTI => match data.len() {
156                     1 => enable_branch_protection = Some(data[0] != 0),
157                     _ => bail!("invalid `{name}` section"),
158                 },
159                 ".text" => {
160                     text = range;
161 
162                     if let SectionFlags::Elf { sh_flags } = section.flags() {
163                         if sh_flags & obj::SH_WASMTIME_NOT_EXECUTED != 0 {
164                             needs_executable = false;
165                         }
166                     }
167 
168                     // Assert that Cranelift hasn't inserted any calls that need to be
169                     // relocated. We avoid using things like Cranelift's floor/ceil/etc.
170                     // operators in the Wasm-to-Cranelift translator specifically to
171                     // avoid having to do any relocations here. This also ensures that
172                     // all builtins use the same trampoline mechanism.
173                     assert!(section.relocations().next().is_none());
174                 }
175                 #[cfg(has_host_compiler_backend)]
176                 crate::runtime::vm::UnwindRegistration::SECTION_NAME => unwind = range,
177                 obj::ELF_WASM_DATA => wasm_data = range,
178                 obj::ELF_WASMTIME_ADDRMAP => address_map_data = range,
179                 obj::ELF_WASMTIME_STACK_MAP => stack_map_data = range,
180                 obj::ELF_WASMTIME_TRAPS => trap_data = range,
181                 obj::ELF_WASMTIME_EXCEPTIONS => exception_data = range,
182                 obj::ELF_WASMTIME_FRAMES => frame_tables_data = range,
183                 obj::ELF_NAME_DATA => func_name_data = range,
184                 obj::ELF_WASMTIME_INFO => info_data = range,
185                 obj::ELF_WASMTIME_DWARF => wasm_dwarf = range,
186                 #[cfg(feature = "debug-builtins")]
187                 ".debug_info" => has_native_debug_info = true,
188 
189                 _ => log::debug!("ignoring section {name}"),
190             }
191         }
192 
193         // require mutability even when this is turned off
194         #[cfg(not(has_host_compiler_backend))]
195         let _ = &mut unwind;
196 
197         // Ensure that the exception table is well-formed. This parser
198         // construction is cheap: it reads the header and validates
199         // ranges but nothing else. We do this only in debug-assertion
200         // builds because we otherwise require for safety that the
201         // compiled artifact is as-produced-by this version of
202         // Wasmtime, and we should always produce a correct exception
203         // table (i.e., we are not expecting untrusted data here).
204         if cfg!(debug_assertions) {
205             let _ = ExceptionTable::parse(&mmap[exception_data.clone()])?;
206         }
207 
208         Ok(Self {
209             mmap,
210             #[cfg(has_host_compiler_backend)]
211             unwind_registration: None,
212             #[cfg(feature = "debug-builtins")]
213             debug_registration: None,
214             published: false,
215             registered: false,
216             enable_branch_protection: enable_branch_protection
217                 .ok_or_else(|| format_err!("missing `{}` section", obj::ELF_WASM_BTI))?,
218             needs_executable,
219             #[cfg(feature = "debug-builtins")]
220             has_native_debug_info,
221             custom_code_memory: engine.custom_code_memory().cloned(),
222             text,
223             unwind,
224             trap_data,
225             address_map_data,
226             stack_map_data,
227             exception_data,
228             frame_tables_data,
229             func_name_data,
230             wasm_dwarf,
231             info_data,
232             wasm_data,
233         })
234     }
235 
236     /// Returns a reference to the underlying `MmapVec` this memory owns.
237     #[inline]
238     pub fn mmap(&self) -> &MmapVec {
239         &self.mmap
240     }
241 
242     /// Returns the contents of the text section of the ELF executable this
243     /// represents.
244     #[inline]
245     pub fn text(&self) -> &[u8] {
246         &self.mmap[self.text.clone()]
247     }
248 
249     /// Returns the contents of the `ELF_WASMTIME_DWARF` section.
250     #[inline]
251     pub fn wasm_dwarf(&self) -> &[u8] {
252         &self.mmap[self.wasm_dwarf.clone()]
253     }
254 
255     /// Returns the data in the `ELF_NAME_DATA` section.
256     #[inline]
257     pub fn func_name_data(&self) -> &[u8] {
258         &self.mmap[self.func_name_data.clone()]
259     }
260 
261     /// Returns the concatenated list of all data associated with this wasm
262     /// module.
263     ///
264     /// This is used for initialization of memories and all data ranges stored
265     /// in a `Module` are relative to the slice returned here.
266     #[inline]
267     pub fn wasm_data(&self) -> &[u8] {
268         &self.mmap[self.wasm_data.clone()]
269     }
270 
271     /// Returns the encoded address map section used to pass to
272     /// `wasmtime_environ::lookup_file_pos`.
273     #[inline]
274     pub fn address_map_data(&self) -> &[u8] {
275         &self.mmap[self.address_map_data.clone()]
276     }
277 
278     /// Returns the encoded stack map section used to pass to
279     /// `wasmtime_environ::StackMap::lookup`.
280     pub fn stack_map_data(&self) -> &[u8] {
281         &self.mmap[self.stack_map_data.clone()]
282     }
283 
284     /// Returns the encoded exception-tables section to pass to
285     /// `wasmtime_unwinder::ExceptionTable::parse`.
286     pub fn exception_tables(&self) -> &[u8] {
287         &self.mmap[self.exception_data.clone()]
288     }
289 
290     /// Returns the encoded frame-tables section to pass to
291     /// `wasmtime_environ::FrameTable::parse`.
292     pub fn frame_tables(&self) -> &[u8] {
293         &self.mmap[self.frame_tables_data.clone()]
294     }
295 
296     /// Returns the contents of the `ELF_WASMTIME_INFO` section, or an empty
297     /// slice if it wasn't found.
298     #[inline]
299     pub fn wasmtime_info(&self) -> &[u8] {
300         &self.mmap[self.info_data.clone()]
301     }
302 
303     /// Returns the contents of the `ELF_WASMTIME_TRAPS` section, or an empty
304     /// slice if it wasn't found.
305     #[inline]
306     pub fn trap_data(&self) -> &[u8] {
307         &self.mmap[self.trap_data.clone()]
308     }
309 
310     /// Publishes the internal ELF image to be ready for execution.
311     ///
312     /// This method can only be when the image is not published (its
313     /// default state) and will panic if called when already
314     /// published. This will parse the ELF image from the original
315     /// `MmapVec` and do everything necessary to get it ready for
316     /// execution, including:
317     ///
318     /// * Change page protections from read/write to read/execute.
319     /// * Register unwinding information with the OS
320     /// * Register this image with the debugger if native DWARF is present
321     ///
322     /// After this function executes all JIT code should be ready to execute.
323     ///
324     /// The action may be reversed by calling [`Self::unpublish`], as long
325     /// as that method's safety requirements are upheld.
326     pub fn publish(&mut self) -> Result<()> {
327         assert!(!self.published);
328         self.published = true;
329 
330         if self.text().is_empty() {
331             return Ok(());
332         }
333 
334         // The unsafety here comes from a few things:
335         //
336         // * We're actually updating some page protections to executable memory.
337         //
338         // * We're registering unwinding information which relies on the
339         //   correctness of the information in the first place. This applies to
340         //   both the actual unwinding tables as well as the validity of the
341         //   pointers we pass in itself.
342         unsafe {
343             // Next freeze the contents of this image by making all of the
344             // memory readonly. Nothing after this point should ever be modified
345             // so commit everything. For a compiled-in-memory image this will
346             // mean IPIs to evict writable mappings from other cores. For
347             // loaded-from-disk images this shouldn't result in IPIs so long as
348             // there weren't any relocations because nothing should have
349             // otherwise written to the image at any point either.
350             //
351             // Note that if virtual memory is disabled this is skipped because
352             // we aren't able to make it readonly, but this is just a
353             // defense-in-depth measure and isn't required for correctness.
354             #[cfg(has_virtual_memory)]
355             if self.mmap.supports_virtual_memory() {
356                 self.mmap.make_readonly(0..self.mmap.len())?;
357             }
358 
359             // Switch the executable portion from readonly to read/execute.
360             if self.needs_executable {
361                 if !self.custom_publish()? {
362                     if !self.mmap.supports_virtual_memory() {
363                         bail!("this target requires virtual memory to be enabled");
364                     }
365                     #[cfg(has_virtual_memory)]
366                     self.mmap
367                         .make_executable(self.text.clone(), self.enable_branch_protection)
368                         .context("unable to make memory executable")?;
369                 }
370             }
371 
372             if !self.registered {
373                 // With all our memory set up use the platform-specific
374                 // `UnwindRegistration` implementation to inform the general
375                 // runtime that there's unwinding information available for all
376                 // our just-published JIT functions.
377                 self.register_unwind_info()?;
378 
379                 #[cfg(feature = "debug-builtins")]
380                 self.register_debug_image()?;
381                 self.registered = true;
382             }
383         }
384 
385         Ok(())
386     }
387 
388     fn custom_publish(&mut self) -> Result<bool> {
389         if let Some(mem) = self.custom_code_memory.as_ref() {
390             let text = self.text();
391             // The text section should be aligned to
392             // `custom_code_memory.required_alignment()` due to a
393             // combination of two invariants:
394             //
395             // - MmapVec aligns its start address, even in owned-Vec mode; and
396             // - The text segment inside the ELF image will be aligned according
397             //   to the platform's requirements.
398             let text_addr = text.as_ptr() as usize;
399             assert_eq!(text_addr & (mem.required_alignment() - 1), 0);
400 
401             // The custom code memory handler will ensure the
402             // memory is executable and also handle icache
403             // coherence.
404             mem.publish_executable(text.as_ptr(), text.len())?;
405             Ok(true)
406         } else {
407             Ok(false)
408         }
409     }
410 
411     /// "Unpublish" code memory (transition it from executable to read/writable).
412     ///
413     /// This may be used to edit the code image, as long as the
414     /// overall size of the memory remains the same. Note the hazards
415     /// inherent in editing code that may have been executed: any
416     /// stack frames with PC still active in this code must be
417     /// suspended (e.g., called into a hostcall that is then invoking
418     /// this method, or async-yielded) and any active PC values must
419     /// point to valid instructions. Thus this is mostly useful for
420     /// patching in-place at particular sites, such as by the use of
421     /// Cranelift's `patchable_call` instruction.
422     ///
423     /// If this fails, then the memory remains executable.
424     pub fn unpublish(&mut self) -> Result<()> {
425         assert!(self.published);
426         self.published = false;
427 
428         if self.text().is_empty() {
429             return Ok(());
430         }
431 
432         if self.custom_unpublish()? {
433             return Ok(());
434         }
435 
436         if !self.mmap.supports_virtual_memory() {
437             bail!("this target requires virtual memory to be enabled");
438         }
439 
440         // SAFETY: we are guaranteed by our own safety conditions that
441         // we have exclusive access to this code and can change its
442         // permissions (removing the execute bit) without causing
443         // problems.
444         #[cfg(has_virtual_memory)]
445         unsafe {
446             self.mmap.make_readwrite(0..self.mmap.len())?;
447         }
448 
449         // Note that we do *not* unregister: we expect unpublish
450         // to be used for temporary edits, so we want the
451         // registration to "stick" after the initial publish and
452         // not toggle in subsequent unpublish/publish cycles.
453 
454         Ok(())
455     }
456 
457     fn custom_unpublish(&mut self) -> Result<bool> {
458         if let Some(mem) = self.custom_code_memory.as_ref() {
459             let text = self.text();
460             mem.unpublish_executable(text.as_ptr(), text.len())?;
461             Ok(true)
462         } else {
463             Ok(false)
464         }
465     }
466 
467     /// Return a mutable borrow to the code, suitable for editing.
468     ///
469     /// Must not be published.
470     ///
471     /// # Panics
472     ///
473     /// This method panics if the code has been published (and not
474     /// subsequently unpublished).
475     pub fn text_mut(&mut self) -> &mut [u8] {
476         assert!(!self.published);
477         // SAFETY: we assert !published, which means we either have
478         // not yet applied readonly + execute permissinos, or we have
479         // undone that and flipped back to read-write via unpublish.
480         unsafe { &mut self.mmap.as_mut_slice()[self.text.clone()] }
481     }
482 
483     unsafe fn register_unwind_info(&mut self) -> Result<()> {
484         if self.unwind.len() == 0 {
485             return Ok(());
486         }
487         #[cfg(has_host_compiler_backend)]
488         {
489             let text = self.text();
490             let unwind_info = &self.mmap[self.unwind.clone()];
491             let registration = unsafe {
492                 crate::runtime::vm::UnwindRegistration::new(
493                     text.as_ptr(),
494                     unwind_info.as_ptr(),
495                     unwind_info.len(),
496                 )
497                 .context("failed to create unwind info registration")?
498             };
499             self.unwind_registration = Some(registration);
500             return Ok(());
501         }
502         #[cfg(not(has_host_compiler_backend))]
503         {
504             bail!("should not have unwind info for non-native backend")
505         }
506     }
507 
508     #[cfg(feature = "debug-builtins")]
509     fn register_debug_image(&mut self) -> Result<()> {
510         if !self.has_native_debug_info {
511             return Ok(());
512         }
513 
514         // TODO-DebugInfo: we're copying the whole image here, which is pretty wasteful.
515         // Use the existing memory by teaching code here about relocations in DWARF sections
516         // and anything else necessary that is done in "create_gdbjit_image" right now.
517         let image = self.mmap().to_vec();
518         let text: &[u8] = self.text();
519         let bytes = crate::native_debug::create_gdbjit_image(image, (text.as_ptr(), text.len()))?;
520         let reg = crate::runtime::vm::GdbJitImageRegistration::register(bytes);
521         self.debug_registration = Some(reg);
522         Ok(())
523     }
524 
525     /// Looks up the given offset within this module's text section and returns
526     /// the trap code associated with that instruction, if there is one.
527     pub fn lookup_trap_code(&self, text_offset: usize) -> Option<Trap> {
528         lookup_trap_code(self.trap_data(), text_offset)
529     }
530 
531     /// Get the raw address range of this CodeMemory.
532     pub(crate) fn raw_addr_range(&self) -> Range<usize> {
533         let start = self.text().as_ptr().addr();
534         let end = start + self.text().len();
535         start..end
536     }
537 
538     /// Create a "deep clone": a separate CodeMemory for the same code
539     /// that can be patched or mutated independently. Also returns a
540     /// "metadata and location" handle that can be registered with the
541     /// global module registry and used for trap metadata lookups.
542     #[cfg(feature = "debug")]
543     pub(crate) fn deep_clone(self: &Arc<Self>, engine: &Engine) -> Result<CodeMemory> {
544         let mmap = self.mmap.deep_clone()?;
545         Self::new(engine, mmap)
546     }
547 }
548 
549 /// Returns the range of `inner` within `outer`, such that `outer[range]` is the
550 /// same as `inner`.
551 ///
552 /// This method requires that `inner` is a sub-slice of `outer`, and if that
553 /// isn't true then this method will panic.
554 fn subslice_range(inner: &[u8], outer: &[u8]) -> Range<usize> {
555     if inner.len() == 0 {
556         return 0..0;
557     }
558 
559     assert!(outer.as_ptr() <= inner.as_ptr());
560     assert!((&inner[inner.len() - 1] as *const _) <= (&outer[outer.len() - 1] as *const _));
561 
562     let start = inner.as_ptr() as usize - outer.as_ptr() as usize;
563     start..start + inner.len()
564 }
565