1 use crate::prelude::*;
2 use crate::runtime::vm::sys::vm::MemoryImageSource;
3 use crate::runtime::vm::{HostAlignedByteCount, SendSyncPtr};
4 use std::fs::{File, OpenOptions};
5 use std::io;
6 use std::ops::Range;
7 use std::os::windows::prelude::*;
8 use std::path::Path;
9 use std::ptr::{self, NonNull};
10 use windows_sys::Win32::Foundation::*;
11 use windows_sys::Win32::Storage::FileSystem::*;
12 use windows_sys::Win32::System::Memory::*;
13
14 /// Open a file so that it can be mmap'd for executing.
15 ///
16 /// Open the file with read/execute access and only share for
17 /// read. This will enable us to perform the proper mmap below
18 /// while also disallowing other processes modifying the file
19 /// and having those modifications show up in our address space.
open_file_for_mmap(path: &Path) -> Result<File>20 pub fn open_file_for_mmap(path: &Path) -> Result<File> {
21 OpenOptions::new()
22 .read(true)
23 .access_mode(FILE_GENERIC_READ | FILE_GENERIC_EXECUTE)
24 .share_mode(FILE_SHARE_READ)
25 .open(path)
26 .context("failed to open file")
27 }
28
29 #[derive(Debug)]
30 pub struct Mmap {
31 memory: SendSyncPtr<[u8]>,
32 is_file: bool,
33 }
34
35 impl Mmap {
new_empty() -> Mmap36 pub fn new_empty() -> Mmap {
37 Mmap {
38 memory: crate::vm::sys::empty_mmap(),
39 is_file: false,
40 }
41 }
42
new(size: HostAlignedByteCount) -> Result<Self>43 pub fn new(size: HostAlignedByteCount) -> Result<Self> {
44 let ptr = unsafe {
45 VirtualAlloc(
46 ptr::null_mut(),
47 size.byte_count(),
48 MEM_RESERVE | MEM_COMMIT,
49 PAGE_READWRITE,
50 )
51 };
52 if ptr.is_null() {
53 bail!(io::Error::last_os_error())
54 }
55
56 let memory = std::ptr::slice_from_raw_parts_mut(ptr.cast(), size.byte_count());
57 let memory = SendSyncPtr::new(NonNull::new(memory).unwrap());
58 Ok(Self {
59 memory,
60 is_file: false,
61 })
62 }
63
reserve(size: HostAlignedByteCount) -> Result<Self>64 pub fn reserve(size: HostAlignedByteCount) -> Result<Self> {
65 let ptr = unsafe {
66 VirtualAlloc(
67 ptr::null_mut(),
68 size.byte_count(),
69 MEM_RESERVE,
70 PAGE_NOACCESS,
71 )
72 };
73 if ptr.is_null() {
74 bail!(io::Error::last_os_error())
75 }
76 let memory = std::ptr::slice_from_raw_parts_mut(ptr.cast(), size.byte_count());
77 let memory = SendSyncPtr::new(NonNull::new(memory).unwrap());
78 Ok(Self {
79 memory,
80 is_file: false,
81 })
82 }
83
from_file(file: &File) -> Result<Self>84 pub fn from_file(file: &File) -> Result<Self> {
85 unsafe {
86 let len = file
87 .metadata()
88 .context("failed to get file metadata")?
89 .len();
90 let len = usize::try_from(len).map_err(|_| format_err!("file too large to map"))?;
91
92 // Create a file mapping that allows PAGE_EXECUTE_WRITECOPY.
93 // This enables up-to these permissions but we won't leave all
94 // of these permissions active at all times. Execution is
95 // necessary for the generated code from Cranelift and the
96 // WRITECOPY part is needed for possibly resolving relocations,
97 // but otherwise writes don't happen.
98 let mapping = CreateFileMappingW(
99 file.as_raw_handle(),
100 ptr::null_mut(),
101 PAGE_EXECUTE_WRITECOPY,
102 0,
103 0,
104 ptr::null(),
105 );
106 if mapping == INVALID_HANDLE_VALUE {
107 return Err(io::Error::last_os_error()).context("failed to create file mapping");
108 }
109
110 // Create a view for the entire file using all our requisite
111 // permissions so that we can change the virtual permissions
112 // later on.
113 let ptr = MapViewOfFile(
114 mapping,
115 FILE_MAP_READ | FILE_MAP_EXECUTE | FILE_MAP_COPY,
116 0,
117 0,
118 len,
119 )
120 .Value;
121 let err = io::Error::last_os_error();
122 CloseHandle(mapping);
123 if ptr.is_null() {
124 return Err(err).context(format!("failed to create map view of {len:#x} bytes"));
125 }
126
127 let memory = std::ptr::slice_from_raw_parts_mut(ptr.cast(), len);
128 let memory = SendSyncPtr::new(NonNull::new(memory).unwrap());
129 let ret = Self {
130 memory,
131 is_file: true,
132 };
133
134 // Protect the entire file as PAGE_WRITECOPY to start (i.e.
135 // remove the execute bit)
136 let mut old = 0;
137 if VirtualProtect(
138 ret.as_send_sync_ptr().as_ptr().cast(),
139 ret.len(),
140 PAGE_WRITECOPY,
141 &mut old,
142 ) == 0
143 {
144 return Err(io::Error::last_os_error())
145 .context("failed change pages to `PAGE_READONLY`");
146 }
147
148 Ok(ret)
149 }
150 }
151
make_accessible( &self, start: HostAlignedByteCount, len: HostAlignedByteCount, ) -> Result<()>152 pub unsafe fn make_accessible(
153 &self,
154 start: HostAlignedByteCount,
155 len: HostAlignedByteCount,
156 ) -> Result<()> {
157 if unsafe {
158 VirtualAlloc(
159 self.as_send_sync_ptr()
160 .as_ptr()
161 .add(start.byte_count())
162 .cast(),
163 len.byte_count(),
164 MEM_COMMIT,
165 PAGE_READWRITE,
166 )
167 }
168 .is_null()
169 {
170 bail!(io::Error::last_os_error())
171 }
172
173 Ok(())
174 }
175
176 #[inline]
as_send_sync_ptr(&self) -> SendSyncPtr<u8>177 pub fn as_send_sync_ptr(&self) -> SendSyncPtr<u8> {
178 self.memory.cast()
179 }
180
181 #[inline]
len(&self) -> usize182 pub fn len(&self) -> usize {
183 self.memory.as_ptr().len()
184 }
185
make_executable( &self, range: Range<usize>, enable_branch_protection: bool, ) -> Result<()>186 pub unsafe fn make_executable(
187 &self,
188 range: Range<usize>,
189 enable_branch_protection: bool,
190 ) -> Result<()> {
191 let base = self
192 .as_send_sync_ptr()
193 .as_ptr()
194 .wrapping_add(range.start)
195 .cast();
196 let len = range.end - range.start;
197
198 if !cfg!(feature = "std") {
199 bail!(
200 "with the `std` feature disabled at compile time \
201 there must be a custom implementation of publishing \
202 code memory, otherwise it's unknown how to do icache \
203 management"
204 );
205 }
206
207 // Clear the newly allocated code from cache if the processor requires
208 // it
209 //
210 // Do this before marking the memory as R+X, technically we should be
211 // able to do it after but there are some CPU's that have had errata
212 // about doing this with read only memory.
213 #[cfg(feature = "std")]
214 unsafe {
215 wasmtime_jit_icache_coherence::clear_cache(base, len).context("failed cache clear")?;
216 }
217
218 let flags = if enable_branch_protection {
219 // TODO: We use this check to avoid an unused variable warning,
220 // but some of the CFG-related flags might be applicable
221 PAGE_EXECUTE_READ
222 } else {
223 PAGE_EXECUTE_READ
224 };
225 let mut old = 0;
226 unsafe {
227 let result = VirtualProtect(base, len, flags, &mut old);
228 if result == 0 {
229 bail!(io::Error::last_os_error());
230 }
231 }
232
233 // Flush any in-flight instructions from the pipeline
234 #[cfg(feature = "std")]
235 wasmtime_jit_icache_coherence::pipeline_flush_mt().context("Failed pipeline flush")?;
236 Ok(())
237 }
238
make_readonly(&self, range: Range<usize>) -> Result<()>239 pub unsafe fn make_readonly(&self, range: Range<usize>) -> Result<()> {
240 let mut old = 0;
241 unsafe {
242 let base = self.as_send_sync_ptr().as_ptr().add(range.start).cast();
243 let result = VirtualProtect(base, range.end - range.start, PAGE_READONLY, &mut old);
244 if result == 0 {
245 bail!(io::Error::last_os_error());
246 }
247 }
248 Ok(())
249 }
250
make_readwrite(&self, range: Range<usize>) -> Result<()>251 pub unsafe fn make_readwrite(&self, range: Range<usize>) -> Result<()> {
252 let mut old = 0;
253 unsafe {
254 let base = self.as_send_sync_ptr().as_ptr().add(range.start).cast();
255 let result = VirtualProtect(base, range.end - range.start, PAGE_READWRITE, &mut old);
256 if result == 0 {
257 bail!(io::Error::last_os_error());
258 }
259 }
260 Ok(())
261 }
262
map_image_at( &self, image_source: &MemoryImageSource, _source_offset: u64, _memory_offset: HostAlignedByteCount, _memory_len: HostAlignedByteCount, ) -> Result<()>263 pub unsafe fn map_image_at(
264 &self,
265 image_source: &MemoryImageSource,
266 _source_offset: u64,
267 _memory_offset: HostAlignedByteCount,
268 _memory_len: HostAlignedByteCount,
269 ) -> Result<()> {
270 match *image_source {}
271 }
272 }
273
274 impl Drop for Mmap {
drop(&mut self)275 fn drop(&mut self) {
276 if self.len() == 0 {
277 return;
278 }
279
280 if self.is_file {
281 let r = unsafe {
282 UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS {
283 Value: self.memory.as_ptr().cast(),
284 })
285 };
286 assert_ne!(r, 0);
287 } else {
288 let r = unsafe { VirtualFree(self.memory.as_ptr().cast(), 0, MEM_RELEASE) };
289 assert_ne!(r, 0);
290 }
291 }
292 }
293