wasmtime_internal_wmemcheck/
lib.rs1use std::cmp::*;
9use std::collections::HashMap;
10
11pub struct Wmemcheck {
13 metadata: Vec<MemState>,
14 mallocs: HashMap<usize, usize>,
15 pub stack_pointer: usize,
16 max_stack_size: usize,
17 pub flag: bool,
18}
19
20#[derive(Debug, PartialEq)]
22pub enum AccessError {
23 DoubleMalloc { addr: usize, len: usize },
25 InvalidRead { addr: usize, len: usize },
27 InvalidWrite { addr: usize, len: usize },
29 InvalidFree { addr: usize },
31 OutOfBounds { addr: usize, len: usize },
33}
34
35#[derive(Debug, Clone, PartialEq)]
37pub enum MemState {
38 Unallocated,
40 ValidToWrite,
42 ValidToReadWrite,
44}
45
46impl Wmemcheck {
47 pub fn new(mem_size: usize) -> Wmemcheck {
49 let metadata = vec![MemState::Unallocated; mem_size];
50 let mallocs = HashMap::new();
51 Wmemcheck {
52 metadata,
53 mallocs,
54 stack_pointer: 0,
55 max_stack_size: 0,
56 flag: true,
57 }
58 }
59
60 pub fn malloc(&mut self, addr: usize, len: usize) -> Result<(), AccessError> {
62 if !self.is_in_bounds_heap(addr, len) {
63 return Err(AccessError::OutOfBounds { addr, len });
64 }
65 for i in addr..addr + len {
66 match self.metadata[i] {
67 MemState::ValidToWrite => {
68 return Err(AccessError::DoubleMalloc { addr, len });
69 }
70 MemState::ValidToReadWrite => {
71 return Err(AccessError::DoubleMalloc { addr, len });
72 }
73 _ => {}
74 }
75 }
76 for i in addr..addr + len {
77 self.metadata[i] = MemState::ValidToWrite;
78 }
79 self.mallocs.insert(addr, len);
80 Ok(())
81 }
82
83 pub fn read(&mut self, addr: usize, len: usize) -> Result<(), AccessError> {
85 if !self.flag {
86 return Ok(());
87 }
88 if !(self.is_in_bounds_stack(addr, len) || self.is_in_bounds_heap(addr, len)) {
89 return Err(AccessError::OutOfBounds { addr, len });
90 }
91 for i in addr..addr + len {
92 match self.metadata[i] {
93 MemState::Unallocated => {
94 return Err(AccessError::InvalidRead { addr, len });
95 }
96 MemState::ValidToWrite => {
97 return Err(AccessError::InvalidRead { addr, len });
98 }
99 _ => {}
100 }
101 }
102 Ok(())
103 }
104
105 pub fn write(&mut self, addr: usize, len: usize) -> Result<(), AccessError> {
107 if !self.flag {
108 return Ok(());
109 }
110 if !(self.is_in_bounds_stack(addr, len) || self.is_in_bounds_heap(addr, len)) {
111 return Err(AccessError::OutOfBounds { addr, len });
112 }
113 for i in addr..addr + len {
114 if let MemState::Unallocated = self.metadata[i] {
115 return Err(AccessError::InvalidWrite { addr, len });
116 }
117 }
118 for i in addr..addr + len {
119 self.metadata[i] = MemState::ValidToReadWrite;
120 }
121 Ok(())
122 }
123
124 pub fn free(&mut self, addr: usize) -> Result<(), AccessError> {
126 if !self.mallocs.contains_key(&addr) {
127 return Err(AccessError::InvalidFree { addr });
128 }
129 let len = self.mallocs[&addr];
130 for i in addr..addr + len {
131 if let MemState::Unallocated = self.metadata[i] {
132 return Err(AccessError::InvalidFree { addr });
133 }
134 }
135 self.mallocs.remove(&addr);
136 for i in addr..addr + len {
137 self.metadata[i] = MemState::Unallocated;
138 }
139 Ok(())
140 }
141
142 fn is_in_bounds_heap(&self, addr: usize, len: usize) -> bool {
143 self.max_stack_size <= addr && addr + len <= self.metadata.len()
144 }
145
146 fn is_in_bounds_stack(&self, addr: usize, len: usize) -> bool {
147 self.stack_pointer <= addr && addr + len < self.max_stack_size
148 }
149
150 pub fn update_stack_pointer(&mut self, new_sp: usize) -> Result<(), AccessError> {
152 if new_sp > self.max_stack_size {
153 return Err(AccessError::OutOfBounds {
154 addr: self.stack_pointer,
155 len: new_sp - self.stack_pointer,
156 });
157 } else if new_sp < self.stack_pointer {
158 for i in new_sp..self.stack_pointer + 1 {
159 self.metadata[i] = MemState::ValidToReadWrite;
160 }
161 } else {
162 for i in self.stack_pointer..new_sp {
163 self.metadata[i] = MemState::Unallocated;
164 }
165 }
166 self.stack_pointer = new_sp;
167 Ok(())
168 }
169
170 pub fn memcheck_on(&mut self) {
172 self.flag = true;
173 }
174
175 pub fn memcheck_off(&mut self) {
177 self.flag = false;
178 }
179
180 pub fn set_stack_size(&mut self, stack_size: usize) {
182 self.max_stack_size = stack_size + 1;
183 self.stack_pointer = stack_size;
186 let _ = self.update_stack_pointer(0);
187 }
188
189 pub fn update_mem_size(&mut self, num_bytes: usize) {
191 let to_append = vec![MemState::Unallocated; num_bytes];
192 self.metadata.extend(to_append);
193 }
194}
195
196#[test]
197fn basic_wmemcheck() {
198 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
199
200 wmemcheck_state.set_stack_size(1024);
201 assert!(wmemcheck_state.malloc(0x1000, 32).is_ok());
202 assert!(wmemcheck_state.write(0x1000, 4).is_ok());
203 assert!(wmemcheck_state.read(0x1000, 4).is_ok());
204 assert_eq!(wmemcheck_state.mallocs, HashMap::from([(0x1000, 32)]));
205 assert!(wmemcheck_state.free(0x1000).is_ok());
206 assert!(wmemcheck_state.mallocs.is_empty());
207}
208
209#[test]
210fn read_before_initializing() {
211 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
212
213 assert!(wmemcheck_state.malloc(0x1000, 32).is_ok());
214 assert_eq!(
215 wmemcheck_state.read(0x1000, 4),
216 Err(AccessError::InvalidRead {
217 addr: 0x1000,
218 len: 4
219 })
220 );
221 assert!(wmemcheck_state.write(0x1000, 4).is_ok());
222 assert!(wmemcheck_state.free(0x1000).is_ok());
223}
224
225#[test]
226fn use_after_free() {
227 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
228
229 assert!(wmemcheck_state.malloc(0x1000, 32).is_ok());
230 assert!(wmemcheck_state.write(0x1000, 4).is_ok());
231 assert!(wmemcheck_state.write(0x1000, 4).is_ok());
232 assert!(wmemcheck_state.free(0x1000).is_ok());
233 assert_eq!(
234 wmemcheck_state.write(0x1000, 4),
235 Err(AccessError::InvalidWrite {
236 addr: 0x1000,
237 len: 4
238 })
239 );
240}
241
242#[test]
243fn double_free() {
244 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
245
246 assert!(wmemcheck_state.malloc(0x1000, 32).is_ok());
247 assert!(wmemcheck_state.write(0x1000, 4).is_ok());
248 assert!(wmemcheck_state.free(0x1000).is_ok());
249 assert_eq!(
250 wmemcheck_state.free(0x1000),
251 Err(AccessError::InvalidFree { addr: 0x1000 })
252 );
253}
254
255#[test]
256fn out_of_bounds_malloc() {
257 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
258
259 assert_eq!(
260 wmemcheck_state.malloc(640 * 1024, 1),
261 Err(AccessError::OutOfBounds {
262 addr: 640 * 1024,
263 len: 1
264 })
265 );
266 assert_eq!(
267 wmemcheck_state.malloc(640 * 1024 - 10, 15),
268 Err(AccessError::OutOfBounds {
269 addr: 640 * 1024 - 10,
270 len: 15
271 })
272 );
273 assert!(wmemcheck_state.mallocs.is_empty());
274}
275
276#[test]
277fn out_of_bounds_read() {
278 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
279
280 assert!(wmemcheck_state.malloc(640 * 1024 - 24, 24).is_ok());
281 assert_eq!(
282 wmemcheck_state.read(640 * 1024 - 24, 25),
283 Err(AccessError::OutOfBounds {
284 addr: 640 * 1024 - 24,
285 len: 25
286 })
287 );
288}
289
290#[test]
291fn double_malloc() {
292 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
293
294 assert!(wmemcheck_state.malloc(0x1000, 32).is_ok());
295 assert_eq!(
296 wmemcheck_state.malloc(0x1000, 32),
297 Err(AccessError::DoubleMalloc {
298 addr: 0x1000,
299 len: 32
300 })
301 );
302 assert_eq!(
303 wmemcheck_state.malloc(0x1002, 32),
304 Err(AccessError::DoubleMalloc {
305 addr: 0x1002,
306 len: 32
307 })
308 );
309 assert!(wmemcheck_state.free(0x1000).is_ok());
310}
311
312#[test]
313fn error_type() {
314 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
315
316 assert!(wmemcheck_state.malloc(0x1000, 32).is_ok());
317 assert_eq!(
318 wmemcheck_state.malloc(0x1000, 32),
319 Err(AccessError::DoubleMalloc {
320 addr: 0x1000,
321 len: 32
322 })
323 );
324 assert_eq!(
325 wmemcheck_state.malloc(640 * 1024, 32),
326 Err(AccessError::OutOfBounds {
327 addr: 640 * 1024,
328 len: 32
329 })
330 );
331 assert!(wmemcheck_state.free(0x1000).is_ok());
332}
333
334#[test]
335fn update_sp_no_error() {
336 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
337
338 wmemcheck_state.set_stack_size(1024);
339 assert!(wmemcheck_state.update_stack_pointer(768).is_ok());
340 assert_eq!(wmemcheck_state.stack_pointer, 768);
341 assert!(wmemcheck_state.malloc(1024 * 2, 32).is_ok());
342 assert!(wmemcheck_state.free(1024 * 2).is_ok());
343 assert!(wmemcheck_state.update_stack_pointer(896).is_ok());
344 assert_eq!(wmemcheck_state.stack_pointer, 896);
345 assert!(wmemcheck_state.update_stack_pointer(1024).is_ok());
346}
347
348#[test]
349fn bad_stack_malloc() {
350 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
351
352 wmemcheck_state.set_stack_size(1024);
353
354 assert!(wmemcheck_state.update_stack_pointer(0).is_ok());
355 assert_eq!(wmemcheck_state.stack_pointer, 0);
356 assert_eq!(
357 wmemcheck_state.malloc(512, 32),
358 Err(AccessError::OutOfBounds { addr: 512, len: 32 })
359 );
360 assert_eq!(
361 wmemcheck_state.malloc(1022, 32),
362 Err(AccessError::OutOfBounds {
363 addr: 1022,
364 len: 32
365 })
366 );
367}
368
369#[test]
370fn stack_full_empty() {
371 let mut wmemcheck_state = Wmemcheck::new(640 * 1024);
372
373 wmemcheck_state.set_stack_size(1024);
374
375 assert!(wmemcheck_state.update_stack_pointer(0).is_ok());
376 assert_eq!(wmemcheck_state.stack_pointer, 0);
377 assert!(wmemcheck_state.update_stack_pointer(1024).is_ok());
378 assert_eq!(wmemcheck_state.stack_pointer, 1024)
379}
380
381#[test]
382fn from_test_program() {
383 let mut wmemcheck_state = Wmemcheck::new(1024 * 1024 * 128);
384 wmemcheck_state.set_stack_size(70864);
385 assert!(wmemcheck_state.write(70832, 1).is_ok());
386 assert!(wmemcheck_state.read(1138, 1).is_ok());
387}