wasmer_runtime_core_fl/memory/
ptr.rs1use crate::{
10 memory::Memory,
11 types::{ValueType, WasmExternType},
12};
13use std::{cell::Cell, fmt, marker::PhantomData, mem};
14
15pub struct Array;
18pub struct Item;
21
22#[repr(transparent)]
39pub struct WasmPtr<T: Copy, Ty = Item> {
40 offset: u32,
41 _phantom: PhantomData<(T, Ty)>,
42}
43
44impl<T: Copy, Ty> WasmPtr<T, Ty> {
46 #[inline]
48 pub fn new(offset: u32) -> Self {
49 Self {
50 offset,
51 _phantom: PhantomData,
52 }
53 }
54
55 #[inline]
57 pub fn offset(self) -> u32 {
58 self.offset
59 }
60}
61
62#[inline(always)]
63fn align_pointer(ptr: usize, align: usize) -> usize {
64 debug_assert!(align.count_ones() == 1);
66 ptr & !(align - 1)
67}
68
69impl<T: Copy + ValueType> WasmPtr<T, Item> {
73 #[inline]
80 pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell<T>> {
81 if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
82 || mem::size_of::<T>() == 0
83 {
84 return None;
85 }
86 unsafe {
87 let cell_ptr = align_pointer(
88 memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
89 mem::align_of::<T>(),
90 ) as *const Cell<T>;
91 Some(&*cell_ptr)
92 }
93 }
94
95 #[inline]
103 pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell<T>> {
104 if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
105 || mem::size_of::<T>() == 0
106 {
107 return None;
108 }
109 let cell_ptr = align_pointer(
110 memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
111 mem::align_of::<T>(),
112 ) as *mut Cell<T>;
113 Some(&mut *cell_ptr)
114 }
115}
116
117impl<T: Copy + ValueType> WasmPtr<T, Array> {
121 #[inline]
128 pub fn deref(self, memory: &Memory, index: u32, length: u32) -> Option<&[Cell<T>]> {
129 let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
132 let slice_full_len = index as usize + length as usize;
133 let memory_size = memory.size().bytes().0;
134
135 if (self.offset as usize) + (item_size * slice_full_len) > memory_size
136 || self.offset as usize >= memory_size
137 || mem::size_of::<T>() == 0
138 {
139 return None;
140 }
141
142 unsafe {
143 let cell_ptr = align_pointer(
144 memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
145 mem::align_of::<T>(),
146 ) as *const Cell<T>;
147 let cell_ptrs = &std::slice::from_raw_parts(cell_ptr, slice_full_len)
148 [index as usize..slice_full_len];
149 Some(cell_ptrs)
150 }
151 }
152
153 #[inline]
161 pub unsafe fn deref_mut(
162 self,
163 memory: &Memory,
164 index: u32,
165 length: u32,
166 ) -> Option<&mut [Cell<T>]> {
167 let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
170 let slice_full_len = index as usize + length as usize;
171 let memory_size = memory.size().bytes().0;
172
173 if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
174 || self.offset as usize >= memory_size
175 || mem::size_of::<T>() == 0
176 {
177 return None;
178 }
179
180 let cell_ptr = align_pointer(
181 memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
182 mem::align_of::<T>(),
183 ) as *mut Cell<T>;
184 let cell_ptrs = &mut std::slice::from_raw_parts_mut(cell_ptr, slice_full_len)
185 [index as usize..slice_full_len];
186 Some(cell_ptrs)
187 }
188
189 pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> {
195 let memory_size = memory.size().bytes().0;
196
197 if self.offset as usize + str_len as usize > memory.size().bytes().0
198 || self.offset as usize >= memory_size
199 {
200 return None;
201 }
202 let ptr = unsafe { memory.view::<u8>().as_ptr().add(self.offset as usize) as *const u8 };
203 let slice: &[u8] = unsafe { std::slice::from_raw_parts(ptr, str_len as usize) };
204 std::str::from_utf8(slice).ok()
205 }
206
207 pub fn get_utf8_string_with_nul(self, memory: &Memory) -> Option<&str> {
216 memory.view::<u8>()[(self.offset as usize)..]
217 .iter()
218 .map(|cell| cell.get())
219 .position(|byte| byte == 0)
220 .and_then(|length| self.get_utf8_string(memory, length as u32))
221 }
222}
223
224unsafe impl<T: Copy, Ty> WasmExternType for WasmPtr<T, Ty> {
225 type Native = i32;
226
227 fn to_native(self) -> Self::Native {
228 self.offset as i32
229 }
230 fn from_native(n: Self::Native) -> Self {
231 Self {
232 offset: n as u32,
233 _phantom: PhantomData,
234 }
235 }
236}
237
238unsafe impl<T: Copy, Ty> ValueType for WasmPtr<T, Ty> {}
239
240impl<T: Copy, Ty> Clone for WasmPtr<T, Ty> {
241 fn clone(&self) -> Self {
242 Self {
243 offset: self.offset,
244 _phantom: PhantomData,
245 }
246 }
247}
248
249impl<T: Copy, Ty> Copy for WasmPtr<T, Ty> {}
250
251impl<T: Copy, Ty> PartialEq for WasmPtr<T, Ty> {
252 fn eq(&self, other: &Self) -> bool {
253 self.offset == other.offset
254 }
255}
256
257impl<T: Copy, Ty> Eq for WasmPtr<T, Ty> {}
258
259impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
260 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
261 write!(f, "WasmPtr({:#x})", self.offset)
262 }
263}
264
265#[cfg(test)]
266mod test {
267 use super::*;
268 use crate::memory;
269 use crate::units::Pages;
270
271 #[test]
274 fn wasm_ptr_memory_bounds_checks_hold() {
275 let memory_descriptor =
277 memory::MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap();
278 let memory = memory::Memory::new(memory_descriptor).unwrap();
279
280 let start_wasm_ptr: WasmPtr<u8> = WasmPtr::new(0);
282 let start_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(0);
283
284 assert!(start_wasm_ptr.deref(&memory).is_some());
285 assert!(unsafe { start_wasm_ptr.deref_mut(&memory).is_some() });
286 assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_some());
287 assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_some());
288 assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_some() });
289 assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some());
290 assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
291
292 let last_valid_address_for_u8 = (memory.size().bytes().0 - 1) as u32;
294 let end_wasm_ptr: WasmPtr<u8> = WasmPtr::new(last_valid_address_for_u8);
295 assert!(end_wasm_ptr.deref(&memory).is_some());
296 assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
297
298 let end_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(last_valid_address_for_u8);
299
300 assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
301 assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
302 let invalid_idx_len_combos: [(u32, u32); 3] =
303 [(last_valid_address_for_u8 + 1, 0), (0, 2), (1, 1)];
304 for &(idx, len) in invalid_idx_len_combos.iter() {
305 assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
306 assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
307 }
308 assert!(end_wasm_ptr_array.get_utf8_string(&memory, 2).is_none());
309
310 let last_valid_address_for_u32 = (memory.size().bytes().0 - 4) as u32;
313 let end_wasm_ptr: WasmPtr<u32> = WasmPtr::new(last_valid_address_for_u32);
314 assert!(end_wasm_ptr.deref(&memory).is_some());
315 assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
316 assert!(end_wasm_ptr.deref(&memory).is_some());
317 assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
318
319 let end_wasm_ptr_oob_array: [WasmPtr<u32>; 4] = [
320 WasmPtr::new(last_valid_address_for_u32 + 1),
321 WasmPtr::new(last_valid_address_for_u32 + 2),
322 WasmPtr::new(last_valid_address_for_u32 + 3),
323 WasmPtr::new(last_valid_address_for_u32 + 4),
324 ];
325 for oob_end_ptr in end_wasm_ptr_oob_array.iter() {
326 assert!(oob_end_ptr.deref(&memory).is_none());
327 assert!(unsafe { oob_end_ptr.deref_mut(&memory).is_none() });
328 }
329 let end_wasm_ptr_array: WasmPtr<u32, Array> = WasmPtr::new(last_valid_address_for_u32);
330 assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
331 assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
332
333 let invalid_idx_len_combos: [(u32, u32); 3] =
334 [(last_valid_address_for_u32 + 1, 0), (0, 2), (1, 1)];
335 for &(idx, len) in invalid_idx_len_combos.iter() {
336 assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
337 assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
338 }
339
340 let end_wasm_ptr_array_oob_array: [WasmPtr<u32, Array>; 4] = [
341 WasmPtr::new(last_valid_address_for_u32 + 1),
342 WasmPtr::new(last_valid_address_for_u32 + 2),
343 WasmPtr::new(last_valid_address_for_u32 + 3),
344 WasmPtr::new(last_valid_address_for_u32 + 4),
345 ];
346
347 for oob_end_array_ptr in end_wasm_ptr_array_oob_array.iter() {
348 assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none());
349 assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() });
350 assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none());
351 assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() });
352 }
353 }
354}