Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/jit/src/memory/system.rs
3067 views
1
use cranelift_module::{ModuleError, ModuleResult};
2
3
#[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
4
use memmap2::MmapMut;
5
6
#[cfg(not(any(feature = "selinux-fix", windows)))]
7
use std::alloc;
8
use std::io;
9
use std::mem;
10
use std::ptr;
11
12
use super::{BranchProtection, JITMemoryKind, JITMemoryProvider};
13
14
/// A simple struct consisting of a pointer and length.
15
struct PtrLen {
16
#[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
17
map: Option<MmapMut>,
18
19
ptr: *mut u8,
20
len: usize,
21
}
22
23
impl PtrLen {
24
/// Create a new empty `PtrLen`.
25
fn new() -> Self {
26
Self {
27
#[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
28
map: None,
29
30
ptr: ptr::null_mut(),
31
len: 0,
32
}
33
}
34
35
/// Create a new `PtrLen` pointing to at least `size` bytes of memory,
36
/// suitably sized and aligned for memory protection.
37
#[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
38
fn with_size(size: usize) -> io::Result<Self> {
39
let alloc_size = region::page::ceil(size as *const ()) as usize;
40
MmapMut::map_anon(alloc_size).map(|mut mmap| {
41
// The order here is important; we assign the pointer first to get
42
// around compile time borrow errors.
43
Self {
44
ptr: mmap.as_mut_ptr(),
45
map: Some(mmap),
46
len: alloc_size,
47
}
48
})
49
}
50
51
#[cfg(all(not(target_os = "windows"), not(feature = "selinux-fix")))]
52
fn with_size(size: usize) -> io::Result<Self> {
53
assert_ne!(size, 0);
54
let page_size = region::page::size();
55
let alloc_size = region::page::ceil(size as *const ()) as usize;
56
let layout = alloc::Layout::from_size_align(alloc_size, page_size).unwrap();
57
// Safety: We assert that the size is non-zero above.
58
let ptr = unsafe { alloc::alloc(layout) };
59
60
if !ptr.is_null() {
61
Ok(Self {
62
ptr,
63
len: alloc_size,
64
})
65
} else {
66
Err(io::Error::from(io::ErrorKind::OutOfMemory))
67
}
68
}
69
70
#[cfg(target_os = "windows")]
71
fn with_size(size: usize) -> io::Result<Self> {
72
use windows_sys::Win32::System::Memory::{
73
MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE, VirtualAlloc,
74
};
75
76
// VirtualAlloc always rounds up to the next multiple of the page size
77
let ptr = unsafe {
78
VirtualAlloc(
79
ptr::null_mut(),
80
size,
81
MEM_COMMIT | MEM_RESERVE,
82
PAGE_READWRITE,
83
)
84
};
85
if !ptr.is_null() {
86
Ok(Self {
87
ptr: ptr as *mut u8,
88
len: region::page::ceil(size as *const ()) as usize,
89
})
90
} else {
91
Err(io::Error::last_os_error())
92
}
93
}
94
}
95
96
// `MMapMut` from `cfg(feature = "selinux-fix")` already deallocates properly.
97
#[cfg(all(not(target_os = "windows"), not(feature = "selinux-fix")))]
98
impl Drop for PtrLen {
99
fn drop(&mut self) {
100
if !self.ptr.is_null() {
101
let page_size = region::page::size();
102
let layout = alloc::Layout::from_size_align(self.len, page_size).unwrap();
103
unsafe {
104
region::protect(self.ptr, self.len, region::Protection::READ_WRITE)
105
.expect("unable to unprotect memory");
106
alloc::dealloc(self.ptr, layout)
107
}
108
}
109
}
110
}
111
112
// TODO: add a `Drop` impl for `cfg(target_os = "windows")`
113
114
/// JIT memory manager. This manages pages of suitably aligned and
115
/// accessible memory. Memory will be leaked by default to have
116
/// function pointers remain valid for the remainder of the
117
/// program's life.
118
pub(crate) struct Memory {
119
allocations: Vec<PtrLen>,
120
already_protected: usize,
121
current: PtrLen,
122
position: usize,
123
}
124
125
unsafe impl Send for Memory {}
126
127
impl Memory {
128
pub(crate) fn new() -> Self {
129
Self {
130
allocations: Vec::new(),
131
already_protected: 0,
132
current: PtrLen::new(),
133
position: 0,
134
}
135
}
136
137
fn finish_current(&mut self) {
138
self.allocations
139
.push(mem::replace(&mut self.current, PtrLen::new()));
140
self.position = 0;
141
}
142
143
pub(crate) fn allocate(&mut self, size: usize, align: u64) -> io::Result<*mut u8> {
144
let align = usize::try_from(align).expect("alignment too big");
145
if self.position % align != 0 {
146
self.position += align - self.position % align;
147
debug_assert!(self.position % align == 0);
148
}
149
150
if size <= self.current.len - self.position {
151
// TODO: Ensure overflow is not possible.
152
let ptr = unsafe { self.current.ptr.add(self.position) };
153
self.position += size;
154
return Ok(ptr);
155
}
156
157
self.finish_current();
158
159
// TODO: Allocate more at a time.
160
self.current = PtrLen::with_size(size)?;
161
self.position = size;
162
163
Ok(self.current.ptr)
164
}
165
166
/// Set all memory allocated in this `Memory` up to now as readable and executable.
167
pub(crate) fn set_readable_and_executable(
168
&mut self,
169
branch_protection: BranchProtection,
170
) -> ModuleResult<()> {
171
self.finish_current();
172
173
for &PtrLen { ptr, len, .. } in self.non_protected_allocations_iter() {
174
super::set_readable_and_executable(ptr, len, branch_protection)?;
175
}
176
177
// Flush any in-flight instructions from the pipeline
178
wasmtime_jit_icache_coherence::pipeline_flush_mt().expect("Failed pipeline flush");
179
180
self.already_protected = self.allocations.len();
181
Ok(())
182
}
183
184
/// Set all memory allocated in this `Memory` up to now as readonly.
185
pub(crate) fn set_readonly(&mut self) -> ModuleResult<()> {
186
self.finish_current();
187
188
for &PtrLen { ptr, len, .. } in self.non_protected_allocations_iter() {
189
unsafe {
190
region::protect(ptr, len, region::Protection::READ).map_err(|e| {
191
ModuleError::Backend(
192
anyhow::Error::new(e).context("unable to make memory readonly"),
193
)
194
})?;
195
}
196
}
197
198
self.already_protected = self.allocations.len();
199
Ok(())
200
}
201
202
/// Iterates non protected memory allocations that are of not zero bytes in size.
203
fn non_protected_allocations_iter(&self) -> impl Iterator<Item = &PtrLen> {
204
let iter = self.allocations[self.already_protected..].iter();
205
206
#[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
207
return iter.filter(|&PtrLen { map, len, .. }| *len != 0 && map.is_some());
208
209
#[cfg(any(target_os = "windows", not(feature = "selinux-fix")))]
210
return iter.filter(|&PtrLen { len, .. }| *len != 0);
211
}
212
213
/// Frees all allocated memory regions that would be leaked otherwise.
214
/// Likely to invalidate existing function pointers, causing unsafety.
215
pub(crate) unsafe fn free_memory(&mut self) {
216
self.allocations.clear();
217
self.already_protected = 0;
218
}
219
}
220
221
impl Drop for Memory {
222
fn drop(&mut self) {
223
// leak memory to guarantee validity of function pointers
224
mem::replace(&mut self.allocations, Vec::new())
225
.into_iter()
226
.for_each(mem::forget);
227
}
228
}
229
230
/// A memory provider that allocates memory on-demand using the system
231
/// allocator.
232
///
233
/// Note: Memory will be leaked by default unless
234
/// [`JITMemoryProvider::free_memory`] is called to ensure function pointers
235
/// remain valid for the remainder of the program's life.
236
pub struct SystemMemoryProvider {
237
code: Memory,
238
readonly: Memory,
239
writable: Memory,
240
}
241
242
impl SystemMemoryProvider {
243
/// Create a new memory handle with the given branch protection.
244
pub fn new() -> Self {
245
Self {
246
code: Memory::new(),
247
readonly: Memory::new(),
248
writable: Memory::new(),
249
}
250
}
251
}
252
253
impl JITMemoryProvider for SystemMemoryProvider {
254
unsafe fn free_memory(&mut self) {
255
self.code.free_memory();
256
self.readonly.free_memory();
257
self.writable.free_memory();
258
}
259
260
fn finalize(&mut self, branch_protection: BranchProtection) -> ModuleResult<()> {
261
self.readonly.set_readonly()?;
262
self.code.set_readable_and_executable(branch_protection)
263
}
264
265
fn allocate(&mut self, size: usize, align: u64, kind: JITMemoryKind) -> io::Result<*mut u8> {
266
match kind {
267
JITMemoryKind::Executable => self.code.allocate(size, align),
268
JITMemoryKind::Writable => self.writable.allocate(size, align),
269
JITMemoryKind::ReadOnly => self.readonly.allocate(size, align),
270
}
271
}
272
}
273
274