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