1 use cranelift_module::{ModuleError, ModuleResult};
2 use std::io;
3 
4 mod arena;
5 mod system;
6 
7 pub use arena::ArenaMemoryProvider;
8 pub use system::SystemMemoryProvider;
9 
10 /// Type of branch protection to apply to executable memory.
11 #[derive(Clone, Copy, Debug, PartialEq)]
12 pub enum BranchProtection {
13     /// No protection.
14     None,
15     /// Use the Branch Target Identification extension of the Arm architecture.
16     BTI,
17 }
18 
19 /// A provider of memory for the JIT.
20 pub trait JITMemoryProvider {
21     /// Allocate memory that will be executable once finalized.
22     fn allocate_readexec(&mut self, size: usize, align: u64) -> io::Result<*mut u8>;
23     /// Allocate writable memory.
24     fn allocate_readwrite(&mut self, size: usize, align: u64) -> io::Result<*mut u8>;
25     /// Allocate memory that will be read-only once finalized.
26     fn allocate_readonly(&mut self, size: usize, align: u64) -> io::Result<*mut u8>;
27 
28     /// Free the memory region.
29     unsafe fn free_memory(&mut self);
30     /// Finalize the memory region and apply memory protections.
31     fn finalize(&mut self, branch_protection: BranchProtection) -> ModuleResult<()>;
32 }
33 
34 /// Marks the memory region as readable and executable.
35 ///
36 /// This function deals with applies branch protection and clears the icache,
37 /// but *doesn't* flush the pipeline. Callers have to ensure that
38 /// [`wasmtime_jit_icache_coherence::pipeline_flush_mt`] is called before the
39 /// mappings are used.
40 pub(crate) fn set_readable_and_executable(
41     ptr: *mut u8,
42     len: usize,
43     branch_protection: BranchProtection,
44 ) -> ModuleResult<()> {
45     // Clear all the newly allocated code from cache if the processor requires it
46     //
47     // Do this before marking the memory as R+X, technically we should be able to do it after
48     // but there are some CPU's that have had errata about doing this with read only memory.
49     unsafe {
50         wasmtime_jit_icache_coherence::clear_cache(ptr as *const libc::c_void, len)
51             .expect("Failed cache clear")
52     };
53 
54     unsafe {
55         region::protect(ptr, len, region::Protection::READ_EXECUTE).map_err(|e| {
56             ModuleError::Backend(
57                 anyhow::Error::new(e).context("unable to make memory readable+executable"),
58             )
59         })?;
60     }
61 
62     // If BTI is requested, and the architecture supports it, use mprotect to set the PROT_BTI flag.
63     if branch_protection == BranchProtection::BTI {
64         #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
65         if std::arch::is_aarch64_feature_detected!("bti") {
66             let prot = libc::PROT_EXEC | libc::PROT_READ | /* PROT_BTI */ 0x10;
67 
68             unsafe {
69                 if libc::mprotect(ptr as *mut libc::c_void, len, prot) < 0 {
70                     return Err(ModuleError::Backend(
71                         anyhow::Error::new(io::Error::last_os_error())
72                             .context("unable to make memory readable+executable"),
73                     ));
74                 }
75             }
76         }
77     }
78 
79     Ok(())
80 }
81