1 #[macro_export] 2 macro_rules! alloc_tests { 3 ( $TestRegion:path ) => { 4 use libc::c_void; 5 use rand::rngs::StdRng; 6 use rand::{thread_rng, Rng, SeedableRng}; 7 use std::sync::{Arc, Mutex}; 8 use $TestRegion as TestRegion; 9 use $crate::alloc::{AllocStrategy, Limits, MINSIGSTKSZ}; 10 use $crate::context::{Context, ContextHandle}; 11 use $crate::error::Error; 12 use $crate::instance::InstanceInternal; 13 use $crate::module::{ 14 FunctionPointer, GlobalValue, HeapSpec, MockExportBuilder, MockModuleBuilder, Module, 15 }; 16 use $crate::region::{Region, RegionCreate}; 17 use $crate::sysdeps::host_page_size; 18 use $crate::val::Val; 19 use $crate::vmctx::lucet_vmctx; 20 21 const LIMITS_HEAP_MEM_SIZE: usize = 16 * 64 * 1024; 22 const LIMITS_HEAP_ADDRSPACE_SIZE: usize = 8 * 1024 * 1024; 23 const LIMITS_STACK_SIZE: usize = 64 * 1024; 24 const LIMITS_GLOBALS_SIZE: usize = 4 * 1024; 25 26 const LIMITS: Limits = Limits { 27 heap_memory_size: LIMITS_HEAP_MEM_SIZE, 28 heap_address_space_size: LIMITS_HEAP_ADDRSPACE_SIZE, 29 stack_size: LIMITS_STACK_SIZE, 30 globals_size: LIMITS_GLOBALS_SIZE, 31 ..Limits::default() 32 }; 33 34 const SPEC_HEAP_RESERVED_SIZE: u64 = LIMITS_HEAP_ADDRSPACE_SIZE as u64 / 2; 35 const SPEC_HEAP_GUARD_SIZE: u64 = LIMITS_HEAP_ADDRSPACE_SIZE as u64 / 2; 36 37 // one wasm page, not host page 38 const ONEPAGE_INITIAL_SIZE: u64 = 64 * 1024; 39 const ONEPAGE_MAX_SIZE: u64 = 64 * 1024; 40 41 const ONE_PAGE_HEAP: HeapSpec = HeapSpec { 42 reserved_size: SPEC_HEAP_RESERVED_SIZE, 43 guard_size: SPEC_HEAP_GUARD_SIZE, 44 initial_size: ONEPAGE_INITIAL_SIZE, 45 max_size: Some(ONEPAGE_MAX_SIZE), 46 }; 47 48 const THREEPAGE_INITIAL_SIZE: u64 = 64 * 1024; 49 const THREEPAGE_MAX_SIZE: u64 = 3 * 64 * 1024; 50 51 const THREE_PAGE_MAX_HEAP: HeapSpec = HeapSpec { 52 reserved_size: SPEC_HEAP_RESERVED_SIZE, 53 guard_size: 0, 54 initial_size: THREEPAGE_INITIAL_SIZE, 55 max_size: Some(THREEPAGE_MAX_SIZE), 56 }; 57 58 /// This test shows an `AllocHandle` passed to `Region::allocate_runtime` will have its heap 59 /// and stack of the correct size and read/writability. 60 #[test] 61 fn allocate_runtime_works() { 62 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 63 let mut inst = region 64 .new_instance( 65 MockModuleBuilder::new() 66 .with_heap_spec(ONE_PAGE_HEAP) 67 .build(), 68 ) 69 .expect("new_instance succeeds"); 70 71 let heap_len = inst.alloc().heap_len(); 72 assert_eq!(heap_len, ONEPAGE_INITIAL_SIZE as usize); 73 74 let heap = unsafe { inst.alloc_mut().heap_mut() }; 75 76 assert_eq!(heap[0], 0); 77 heap[0] = 0xFF; 78 assert_eq!(heap[0], 0xFF); 79 80 assert_eq!(heap[heap_len - 1], 0); 81 heap[heap_len - 1] = 0xFF; 82 assert_eq!(heap[heap_len - 1], 0xFF); 83 84 let stack = unsafe { inst.alloc_mut().stack_mut() }; 85 assert_eq!(stack.len(), LIMITS_STACK_SIZE); 86 87 assert_eq!(stack[0], 0); 88 stack[0] = 0xFF; 89 assert_eq!(stack[0], 0xFF); 90 91 assert_eq!(stack[LIMITS_STACK_SIZE - 1], 0); 92 stack[LIMITS_STACK_SIZE - 1] = 0xFF; 93 assert_eq!(stack[LIMITS_STACK_SIZE - 1], 0xFF); 94 } 95 96 /// This test shows the heap works properly after a single expand. 97 #[test] 98 fn expand_heap_once() { 99 expand_heap_once_template(THREE_PAGE_MAX_HEAP) 100 } 101 102 fn expand_heap_once_template(heap_spec: HeapSpec) { 103 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 104 let module = MockModuleBuilder::new() 105 .with_heap_spec(heap_spec.clone()) 106 .build(); 107 let mut inst = region 108 .new_instance(module.clone()) 109 .expect("new_instance succeeds"); 110 111 let heap_len = inst.alloc().heap_len(); 112 assert_eq!(heap_len, heap_spec.initial_size as usize); 113 114 let new_heap_area = inst 115 .alloc_mut() 116 .expand_heap(64 * 1024, module.as_ref()) 117 .expect("expand_heap succeeds"); 118 assert_eq!(heap_len, new_heap_area as usize); 119 120 let new_heap_len = inst.alloc().heap_len(); 121 assert_eq!(new_heap_len, heap_len + (64 * 1024)); 122 123 let heap = unsafe { inst.alloc_mut().heap_mut() }; 124 assert_eq!(heap[new_heap_len - 1], 0); 125 heap[new_heap_len - 1] = 0xFF; 126 assert_eq!(heap[new_heap_len - 1], 0xFF); 127 } 128 129 /// This test shows the heap works properly after two expands. 130 #[test] 131 fn expand_heap_twice() { 132 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 133 let module = MockModuleBuilder::new() 134 .with_heap_spec(THREE_PAGE_MAX_HEAP) 135 .build(); 136 let mut inst = region 137 .new_instance(module.clone()) 138 .expect("new_instance succeeds"); 139 140 let heap_len = inst.alloc().heap_len(); 141 assert_eq!(heap_len, THREEPAGE_INITIAL_SIZE as usize); 142 143 let new_heap_area = inst 144 .alloc_mut() 145 .expand_heap(64 * 1024, module.as_ref()) 146 .expect("expand_heap succeeds"); 147 assert_eq!(heap_len, new_heap_area as usize); 148 149 let new_heap_len = inst.alloc().heap_len(); 150 assert_eq!(new_heap_len, heap_len + (64 * 1024)); 151 152 let second_new_heap_area = inst 153 .alloc_mut() 154 .expand_heap(64 * 1024, module.as_ref()) 155 .expect("expand_heap succeeds"); 156 assert_eq!(new_heap_len, second_new_heap_area as usize); 157 158 let second_new_heap_len = inst.alloc().heap_len(); 159 assert_eq!(second_new_heap_len as u64, THREEPAGE_MAX_SIZE); 160 161 let heap = unsafe { inst.alloc_mut().heap_mut() }; 162 assert_eq!(heap[new_heap_len - 1], 0); 163 heap[new_heap_len - 1] = 0xFF; 164 assert_eq!(heap[new_heap_len - 1], 0xFF); 165 } 166 167 /// This test shows that if you try to expand past the max given by the heap spec, the 168 /// expansion fails, but the existing heap can still be used. This test uses a region with 169 /// multiple slots in order to exercise more edge cases with adjacent managed memory. 170 #[test] 171 fn expand_past_spec_max() { 172 let region = <TestRegion as RegionCreate>::create(10, &LIMITS).expect("region created"); 173 let module = MockModuleBuilder::new() 174 .with_heap_spec(THREE_PAGE_MAX_HEAP) 175 .build(); 176 let mut inst = region 177 .new_instance(module.clone()) 178 .expect("new_instance succeeds"); 179 180 let heap_len = inst.alloc().heap_len(); 181 assert_eq!(heap_len, THREEPAGE_INITIAL_SIZE as usize); 182 183 let new_heap_area = inst 184 .alloc_mut() 185 .expand_heap(THREEPAGE_MAX_SIZE as u32, module.as_ref()); 186 assert!(new_heap_area.is_err(), "heap expansion past spec fails"); 187 188 let new_heap_len = inst.alloc().heap_len(); 189 assert_eq!(new_heap_len, heap_len); 190 191 let heap = unsafe { inst.alloc_mut().heap_mut() }; 192 assert_eq!(heap[new_heap_len - 1], 0); 193 heap[new_heap_len - 1] = 0xFF; 194 assert_eq!(heap[new_heap_len - 1], 0xFF); 195 } 196 197 /// This test exercises custom limits on the heap_memory_size. 198 /// In this scenario, the Region has a limit on the heap memory 199 /// size, but the instance has a smaller limit. Attempts to expand 200 /// the heap fail, but the existing heap can still be used. 201 #[test] 202 fn expand_past_spec_max_with_custom_limit() { 203 let region = <TestRegion as RegionCreate>::create(10, &LIMITS).expect("region created"); 204 let module = MockModuleBuilder::new() 205 .with_heap_spec(THREE_PAGE_MAX_HEAP) 206 .build(); 207 let mut inst = region 208 .new_instance_builder(module.clone()) 209 .with_heap_size_limit((THREEPAGE_INITIAL_SIZE) as usize) 210 .build() 211 .expect("new_instance succeeds"); 212 213 let heap_len = inst.alloc().heap_len(); 214 assert_eq!(heap_len, THREEPAGE_INITIAL_SIZE as usize); 215 216 // Expand the heap within the Region limit but past the 217 // instance limit. 218 let new_heap_area = inst.alloc_mut().expand_heap( 219 (THREEPAGE_MAX_SIZE - THREEPAGE_INITIAL_SIZE) as u32, 220 module.as_ref(), 221 ); 222 assert!( 223 new_heap_area.is_err(), 224 "heap expansion past instance limit fails" 225 ); 226 227 // Affirm that the existing heap can still be used. 228 let new_heap_len = inst.alloc().heap_len(); 229 assert_eq!(new_heap_len, heap_len); 230 231 let heap = unsafe { inst.alloc_mut().heap_mut() }; 232 assert_eq!(heap[new_heap_len - 1], 0); 233 heap[new_heap_len - 1] = 0xFF; 234 assert_eq!(heap[new_heap_len - 1], 0xFF); 235 } 236 237 const EXPANDPASTLIMIT_INITIAL_SIZE: u64 = LIMITS_HEAP_MEM_SIZE as u64 - (64 * 1024); 238 const EXPANDPASTLIMIT_MAX_SIZE: u64 = LIMITS_HEAP_MEM_SIZE as u64 + (64 * 1024); 239 const EXPAND_PAST_LIMIT_SPEC: HeapSpec = HeapSpec { 240 reserved_size: SPEC_HEAP_RESERVED_SIZE, 241 guard_size: SPEC_HEAP_GUARD_SIZE, 242 initial_size: EXPANDPASTLIMIT_INITIAL_SIZE, 243 max_size: Some(EXPANDPASTLIMIT_MAX_SIZE), 244 }; 245 246 /// This test shows that a heap refuses to grow past the region's limits, even if the 247 /// runtime spec says it can grow bigger. This test uses a region with multiple slots in 248 /// order to exercise more edge cases with adjacent managed memory. 249 #[test] 250 fn expand_past_heap_limit() { 251 let region = <TestRegion as RegionCreate>::create(10, &LIMITS).expect("region created"); 252 let module = MockModuleBuilder::new() 253 .with_heap_spec(EXPAND_PAST_LIMIT_SPEC) 254 .build(); 255 let mut inst = region 256 .new_instance(module.clone()) 257 .expect("new_instance succeeds"); 258 259 let heap_len = inst.alloc().heap_len(); 260 assert_eq!(heap_len, EXPANDPASTLIMIT_INITIAL_SIZE as usize); 261 262 let new_heap_area = inst 263 .alloc_mut() 264 .expand_heap(64 * 1024, module.as_ref()) 265 .expect("expand_heap succeeds"); 266 assert_eq!(heap_len, new_heap_area as usize); 267 268 let new_heap_len = inst.alloc().heap_len(); 269 assert_eq!(new_heap_len, LIMITS_HEAP_MEM_SIZE); 270 271 let past_limit_heap_area = inst.alloc_mut().expand_heap(64 * 1024, module.as_ref()); 272 assert!( 273 past_limit_heap_area.is_err(), 274 "heap expansion past limit fails" 275 ); 276 277 let still_heap_len = inst.alloc().heap_len(); 278 assert_eq!(still_heap_len, LIMITS_HEAP_MEM_SIZE); 279 280 let heap = unsafe { inst.alloc_mut().heap_mut() }; 281 assert_eq!(heap[new_heap_len - 1], 0); 282 heap[new_heap_len - 1] = 0xFF; 283 assert_eq!(heap[new_heap_len - 1], 0xFF); 284 } 285 286 /// This test shows that a heap refuses to grow past the instance-specific heap limit, even 287 /// if both the region's limits and the runtime spec says it can grow bigger. This test uses 288 /// a region with multiple slots in order to exercise more edge cases with adjacent managed 289 /// memory. 290 #[test] 291 fn expand_past_instance_heap_limit() { 292 const INSTANCE_LIMIT: usize = LIMITS.heap_memory_size / 2; 293 const SPEC: HeapSpec = HeapSpec { 294 initial_size: INSTANCE_LIMIT as u64 - 64 * 1024, 295 ..EXPAND_PAST_LIMIT_SPEC 296 }; 297 assert_eq!(INSTANCE_LIMIT % host_page_size(), 0); 298 299 let region = <TestRegion as RegionCreate>::create(10, &LIMITS).expect("region created"); 300 let module = MockModuleBuilder::new().with_heap_spec(SPEC).build(); 301 let mut inst = region 302 .new_instance_builder(module.clone()) 303 .with_heap_size_limit(INSTANCE_LIMIT) 304 .build() 305 .expect("instantiation succeeds"); 306 307 let heap_len = inst.alloc().heap_len(); 308 assert_eq!(heap_len, SPEC.initial_size as usize); 309 310 // fill up the rest of the per-instance limit area 311 let new_heap_area = inst 312 .alloc_mut() 313 .expand_heap(64 * 1024, module.as_ref()) 314 .expect("expand_heap succeeds"); 315 assert_eq!(heap_len, new_heap_area as usize); 316 317 let new_heap_len = inst.alloc().heap_len(); 318 assert_eq!(new_heap_len, INSTANCE_LIMIT); 319 320 let past_limit_heap_area = inst.alloc_mut().expand_heap(64 * 1024, module.as_ref()); 321 assert!( 322 past_limit_heap_area.is_err(), 323 "heap expansion past limit fails" 324 ); 325 326 let still_heap_len = inst.alloc().heap_len(); 327 assert_eq!(still_heap_len, INSTANCE_LIMIT); 328 329 let heap = unsafe { inst.alloc_mut().heap_mut() }; 330 assert_eq!(heap[new_heap_len - 1], 0); 331 heap[new_heap_len - 1] = 0xFF; 332 assert_eq!(heap[new_heap_len - 1], 0xFF); 333 } 334 335 const INITIAL_OVERSIZE_HEAP: HeapSpec = HeapSpec { 336 reserved_size: SPEC_HEAP_RESERVED_SIZE, 337 guard_size: SPEC_HEAP_GUARD_SIZE, 338 initial_size: SPEC_HEAP_RESERVED_SIZE + (64 * 1024), 339 max_size: None, 340 }; 341 342 /// This test shows that a heap refuses to grow past the alloc limits, even if the runtime 343 /// spec says it can grow bigger. This test uses a region with multiple slots in order to 344 /// exercise more edge cases with adjacent managed memory. 345 #[test] 346 fn reject_initial_oversize_heap() { 347 let region = <TestRegion as RegionCreate>::create(10, &LIMITS).expect("region created"); 348 let res = region.new_instance( 349 MockModuleBuilder::new() 350 .with_heap_spec(INITIAL_OVERSIZE_HEAP) 351 .build(), 352 ); 353 assert!(res.is_err(), "new_instance fails"); 354 } 355 356 /// This test shows that we reject limits with a larger memory size than address space size 357 #[test] 358 fn reject_undersized_address_space() { 359 const LIMITS: Limits = Limits { 360 heap_memory_size: LIMITS_HEAP_ADDRSPACE_SIZE + 4096, 361 heap_address_space_size: LIMITS_HEAP_ADDRSPACE_SIZE, 362 stack_size: LIMITS_STACK_SIZE, 363 globals_size: LIMITS_GLOBALS_SIZE, 364 ..Limits::default() 365 }; 366 let res = <TestRegion as RegionCreate>::create(10, &LIMITS); 367 assert!(res.is_err(), "region creation fails"); 368 } 369 370 const SMALL_GUARD_HEAP: HeapSpec = HeapSpec { 371 reserved_size: SPEC_HEAP_RESERVED_SIZE, 372 guard_size: SPEC_HEAP_GUARD_SIZE - 1, 373 initial_size: LIMITS_HEAP_MEM_SIZE as u64, 374 max_size: None, 375 }; 376 377 /// This test shows that a heap spec with a guard size smaller than the limits is 378 /// allowed. 379 #[test] 380 fn accept_small_guard_heap() { 381 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 382 let _inst = region 383 .new_instance( 384 MockModuleBuilder::new() 385 .with_heap_spec(SMALL_GUARD_HEAP) 386 .build(), 387 ) 388 .expect("new_instance succeeds"); 389 } 390 391 const LARGE_GUARD_HEAP: HeapSpec = HeapSpec { 392 reserved_size: SPEC_HEAP_RESERVED_SIZE, 393 guard_size: SPEC_HEAP_GUARD_SIZE + 1, 394 initial_size: ONEPAGE_INITIAL_SIZE, 395 max_size: None, 396 }; 397 398 /// This test shows that a `HeapSpec` with a guard size larger than the limits is not 399 /// allowed. 400 #[test] 401 fn reject_large_guard_heap() { 402 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 403 let res = region.new_instance( 404 MockModuleBuilder::new() 405 .with_heap_spec(LARGE_GUARD_HEAP) 406 .build(), 407 ); 408 assert!(res.is_err(), "new_instance fails"); 409 } 410 411 /// This test shows that a `Slot` can be reused after an `AllocHandle` is dropped, and that 412 /// its memory is reset. 413 #[test] 414 fn reuse_slot_works() { 415 fn peek_n_poke(region: &Arc<TestRegion>) { 416 let mut inst = region 417 .new_instance( 418 MockModuleBuilder::new() 419 .with_heap_spec(ONE_PAGE_HEAP) 420 .build(), 421 ) 422 .expect("new_instance succeeds"); 423 424 let heap_len = inst.alloc().heap_len(); 425 assert_eq!(heap_len, ONEPAGE_INITIAL_SIZE as usize); 426 427 let heap = unsafe { inst.alloc_mut().heap_mut() }; 428 429 assert_eq!(heap[0], 0); 430 heap[0] = 0xFF; 431 assert_eq!(heap[0], 0xFF); 432 433 assert_eq!(heap[heap_len - 1], 0); 434 heap[heap_len - 1] = 0xFF; 435 assert_eq!(heap[heap_len - 1], 0xFF); 436 437 let stack = unsafe { inst.alloc_mut().stack_mut() }; 438 assert_eq!(stack.len(), LIMITS_STACK_SIZE); 439 440 assert_eq!(stack[0], 0); 441 stack[0] = 0xFF; 442 assert_eq!(stack[0], 0xFF); 443 444 assert_eq!(stack[LIMITS_STACK_SIZE - 1], 0); 445 stack[LIMITS_STACK_SIZE - 1] = 0xFF; 446 assert_eq!(stack[LIMITS_STACK_SIZE - 1], 0xFF); 447 448 let globals = unsafe { inst.alloc_mut().globals_mut() }; 449 assert_eq!( 450 globals.len(), 451 LIMITS_GLOBALS_SIZE / std::mem::size_of::<GlobalValue>() 452 ); 453 454 unsafe { 455 assert_eq!(globals[0].i_64, 0); 456 globals[0].i_64 = 0xFF; 457 assert_eq!(globals[0].i_64, 0xFF); 458 } 459 460 unsafe { 461 assert_eq!(globals[globals.len() - 1].i_64, 0); 462 globals[globals.len() - 1].i_64 = 0xFF; 463 assert_eq!(globals[globals.len() - 1].i_64, 0xFF); 464 } 465 466 let sigstack = unsafe { inst.alloc_mut().sigstack_mut() }; 467 assert_eq!(sigstack.len(), LIMITS.signal_stack_size); 468 469 assert_eq!(sigstack[0], 0); 470 sigstack[0] = 0xFF; 471 assert_eq!(sigstack[0], 0xFF); 472 473 assert_eq!(sigstack[sigstack.len() - 1], 0); 474 sigstack[sigstack.len() - 1] = 0xFF; 475 assert_eq!(sigstack[sigstack.len() - 1], 0xFF); 476 } 477 478 // with a region size of 1, the slot must be reused 479 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 480 481 peek_n_poke(®ion); 482 peek_n_poke(®ion); 483 } 484 485 /// This test shows that the reset method clears the heap and resets its protections. 486 #[test] 487 fn alloc_reset() { 488 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 489 let module = MockModuleBuilder::new() 490 .with_heap_spec(THREE_PAGE_MAX_HEAP) 491 .build(); 492 let mut inst = region 493 .new_instance(module.clone()) 494 .expect("new_instance succeeds"); 495 496 let heap_len = inst.alloc().heap_len(); 497 assert_eq!(heap_len, THREEPAGE_INITIAL_SIZE as usize); 498 499 let heap = unsafe { inst.alloc_mut().heap_mut() }; 500 501 assert_eq!(heap[0], 0); 502 heap[0] = 0xFF; 503 assert_eq!(heap[0], 0xFF); 504 505 assert_eq!(heap[heap_len - 1], 0); 506 heap[heap_len - 1] = 0xFF; 507 assert_eq!(heap[heap_len - 1], 0xFF); 508 509 // Making a new mock module here because the borrow checker doesn't like referencing 510 // `inst.module` while `inst.alloc()` is borrowed mutably. The `Instance` tests don't have 511 // this weirdness 512 inst.alloc_mut() 513 .reset_heap(module.as_ref()) 514 .expect("reset succeeds"); 515 516 let reset_heap_len = inst.alloc().heap_len(); 517 assert_eq!(reset_heap_len, THREEPAGE_INITIAL_SIZE as usize); 518 519 let heap = unsafe { inst.alloc_mut().heap_mut() }; 520 521 assert_eq!(heap[0], 0); 522 heap[0] = 0xFF; 523 assert_eq!(heap[0], 0xFF); 524 525 assert_eq!(heap[reset_heap_len - 1], 0); 526 heap[reset_heap_len - 1] = 0xFF; 527 assert_eq!(heap[reset_heap_len - 1], 0xFF); 528 } 529 530 /// This test shows that the reset method clears the heap and restores it to the spec 531 /// initial size after growing the heap. 532 #[test] 533 fn alloc_grow_reset() { 534 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 535 let module = MockModuleBuilder::new() 536 .with_heap_spec(THREE_PAGE_MAX_HEAP) 537 .build(); 538 let mut inst = region 539 .new_instance(module.clone()) 540 .expect("new_instance succeeds"); 541 542 let heap_len = inst.alloc().heap_len(); 543 assert_eq!(heap_len, THREEPAGE_INITIAL_SIZE as usize); 544 545 let heap = unsafe { inst.alloc_mut().heap_mut() }; 546 547 assert_eq!(heap[0], 0); 548 heap[0] = 0xFF; 549 assert_eq!(heap[0], 0xFF); 550 551 assert_eq!(heap[heap_len - 1], 0); 552 heap[heap_len - 1] = 0xFF; 553 assert_eq!(heap[heap_len - 1], 0xFF); 554 555 let new_heap_area = inst 556 .alloc_mut() 557 .expand_heap( 558 (THREEPAGE_MAX_SIZE - THREEPAGE_INITIAL_SIZE) as u32, 559 module.as_ref(), 560 ) 561 .expect("expand_heap succeeds"); 562 assert_eq!(heap_len, new_heap_area as usize); 563 564 let new_heap_len = inst.alloc().heap_len(); 565 assert_eq!(new_heap_len, THREEPAGE_MAX_SIZE as usize); 566 567 // Making a new mock module here because the borrow checker doesn't like referencing 568 // `inst.module` while `inst.alloc()` is borrowed mutably. The `Instance` tests don't have 569 // this weirdness 570 inst.alloc_mut() 571 .reset_heap(module.as_ref()) 572 .expect("reset succeeds"); 573 574 let reset_heap_len = inst.alloc().heap_len(); 575 assert_eq!(reset_heap_len, THREEPAGE_INITIAL_SIZE as usize); 576 577 let heap = unsafe { inst.alloc_mut().heap_mut() }; 578 579 assert_eq!(heap[0], 0); 580 heap[0] = 0xFF; 581 assert_eq!(heap[0], 0xFF); 582 583 assert_eq!(heap[reset_heap_len - 1], 0); 584 heap[reset_heap_len - 1] = 0xFF; 585 assert_eq!(heap[reset_heap_len - 1], 0xFF); 586 } 587 588 const GUARDLESS_HEAP: HeapSpec = HeapSpec { 589 reserved_size: SPEC_HEAP_RESERVED_SIZE, 590 guard_size: 0, 591 initial_size: ONEPAGE_INITIAL_SIZE, 592 max_size: None, 593 }; 594 595 /// This test shows the alloc works even with a zero guard size. 596 #[test] 597 fn guardless_heap_create() { 598 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 599 let mut inst = region 600 .new_instance( 601 MockModuleBuilder::new() 602 .with_heap_spec(GUARDLESS_HEAP) 603 .build(), 604 ) 605 .expect("new_instance succeeds"); 606 607 let heap_len = inst.alloc().heap_len(); 608 assert_eq!(heap_len, ONEPAGE_INITIAL_SIZE as usize); 609 610 let heap = unsafe { inst.alloc_mut().heap_mut() }; 611 612 assert_eq!(heap[0], 0); 613 heap[0] = 0xFF; 614 assert_eq!(heap[0], 0xFF); 615 616 assert_eq!(heap[heap_len - 1], 0); 617 heap[heap_len - 1] = 0xFF; 618 assert_eq!(heap[heap_len - 1], 0xFF); 619 620 let stack = unsafe { inst.alloc_mut().stack_mut() }; 621 assert_eq!(stack.len(), LIMITS_STACK_SIZE); 622 623 assert_eq!(stack[0], 0); 624 stack[0] = 0xFF; 625 assert_eq!(stack[0], 0xFF); 626 627 assert_eq!(stack[LIMITS_STACK_SIZE - 1], 0); 628 stack[LIMITS_STACK_SIZE - 1] = 0xFF; 629 assert_eq!(stack[LIMITS_STACK_SIZE - 1], 0xFF); 630 } 631 632 /// This test shows a guardless heap works properly after a single expand. 633 #[test] 634 fn guardless_expand_heap_once() { 635 expand_heap_once_template(GUARDLESS_HEAP) 636 } 637 638 const INITIAL_EMPTY_HEAP: HeapSpec = HeapSpec { 639 reserved_size: SPEC_HEAP_RESERVED_SIZE, 640 guard_size: SPEC_HEAP_GUARD_SIZE, 641 initial_size: 0, 642 max_size: None, 643 }; 644 645 /// This test shows an initially-empty heap works properly after a single expand. 646 #[test] 647 fn initial_empty_expand_heap_once() { 648 expand_heap_once_template(INITIAL_EMPTY_HEAP) 649 } 650 651 const INITIAL_EMPTY_GUARDLESS_HEAP: HeapSpec = HeapSpec { 652 reserved_size: SPEC_HEAP_RESERVED_SIZE, 653 guard_size: 0, 654 initial_size: 0, 655 max_size: None, 656 }; 657 658 /// This test shows an initially-empty, guardless heap works properly after a single 659 /// expand. 660 #[test] 661 fn initial_empty_guardless_expand_heap_once() { 662 expand_heap_once_template(INITIAL_EMPTY_GUARDLESS_HEAP) 663 } 664 665 const CONTEXT_TEST_LIMITS: Limits = Limits { 666 heap_memory_size: 4096, 667 heap_address_space_size: 2 * 4096, 668 stack_size: 4096, 669 globals_size: 4096, 670 ..Limits::default() 671 }; 672 const CONTEXT_TEST_INITIAL_SIZE: u64 = 4096; 673 const CONTEXT_TEST_HEAP: HeapSpec = HeapSpec { 674 reserved_size: 4096, 675 guard_size: 4096, 676 initial_size: CONTEXT_TEST_INITIAL_SIZE, 677 max_size: Some(4096), 678 }; 679 680 /// This test shows that alloced memory will create a heap and a stack that child context 681 /// code can use. 682 #[test] 683 fn context_alloc_child() { 684 extern "C" fn heap_touching_child(heap: *mut u8) { 685 let heap = unsafe { 686 std::slice::from_raw_parts_mut(heap, CONTEXT_TEST_INITIAL_SIZE as usize) 687 }; 688 heap[0] = 123; 689 heap[4095] = 45; 690 } 691 692 let region = <TestRegion as RegionCreate>::create(1, &CONTEXT_TEST_LIMITS) 693 .expect("region created"); 694 let mut inst = region 695 .new_instance( 696 MockModuleBuilder::new() 697 .with_heap_spec(CONTEXT_TEST_HEAP) 698 .build(), 699 ) 700 .expect("new_instance succeeds"); 701 702 let mut parent = ContextHandle::new(); 703 unsafe { 704 let heap_ptr = inst.alloc_mut().heap_mut().as_ptr() as *mut c_void; 705 let mut child = ContextHandle::create_and_init( 706 inst.alloc_mut().stack_u64_mut(), 707 heap_touching_child as usize, 708 &[Val::CPtr(heap_ptr)], 709 ) 710 .expect("context init succeeds"); 711 Context::swap(&mut parent, &mut child); 712 assert_eq!(inst.alloc().heap()[0], 123); 713 assert_eq!(inst.alloc().heap()[4095], 45); 714 } 715 } 716 717 /// This test shows that an alloced memory will create a heap and stack, the child code can 718 /// write a pattern to that stack, and we can read back that same pattern after it is done 719 /// running. 720 #[test] 721 fn context_stack_pattern() { 722 const STACK_PATTERN_LENGTH: usize = 1024; 723 extern "C" fn stack_pattern_child(heap: *mut u64) { 724 let heap = unsafe { 725 std::slice::from_raw_parts_mut(heap, CONTEXT_TEST_INITIAL_SIZE as usize / 8) 726 }; 727 let mut onthestack = [0u8; STACK_PATTERN_LENGTH]; 728 // While not used, this array is load-bearing! A function that executes after the 729 // guest completes, `instance_kill_state_exit_guest_region`, may end up using 730 // sufficient stack space to trample over values in this function's call frame. 731 // 732 // Padding it out with a duplicate pattern makes enough space for `onthestack` to 733 // not be clobbered. 734 let mut ignored = [0u8; STACK_PATTERN_LENGTH]; 735 for i in 0..STACK_PATTERN_LENGTH { 736 ignored[i] = (i % 256) as u8; 737 onthestack[i] = (i % 256) as u8; 738 } 739 heap[0] = onthestack.as_ptr() as u64; 740 } 741 742 let region = <TestRegion as RegionCreate>::create(1, &CONTEXT_TEST_LIMITS) 743 .expect("region created"); 744 let mut inst = region 745 .new_instance( 746 MockModuleBuilder::new() 747 .with_heap_spec(CONTEXT_TEST_HEAP) 748 .build(), 749 ) 750 .expect("new_instance succeeds"); 751 752 let mut parent = ContextHandle::new(); 753 unsafe { 754 let heap_ptr = inst.alloc_mut().heap_mut().as_ptr() as *mut c_void; 755 let mut child = ContextHandle::create_and_init( 756 inst.alloc_mut().stack_u64_mut(), 757 stack_pattern_child as usize, 758 &[Val::CPtr(heap_ptr)], 759 ) 760 .expect("context init succeeds"); 761 Context::swap(&mut parent, &mut child); 762 763 let stack_pattern = inst.alloc().heap_u64()[0] as usize; 764 assert!(stack_pattern > inst.alloc().slot().stack as usize); 765 assert!( 766 stack_pattern + STACK_PATTERN_LENGTH < inst.alloc().slot().stack_top() as usize 767 ); 768 let stack_pattern = 769 std::slice::from_raw_parts(stack_pattern as *const u8, STACK_PATTERN_LENGTH); 770 for i in 0..STACK_PATTERN_LENGTH { 771 assert_eq!(stack_pattern[i], (i % 256) as u8); 772 } 773 } 774 } 775 776 #[test] 777 fn drop_region_first() { 778 let region = <TestRegion as RegionCreate>::create(1, &Limits::default()) 779 .expect("region can be created"); 780 let inst = region 781 .new_instance(MockModuleBuilder::new().build()) 782 .expect("new_instance succeeds"); 783 drop(region); 784 drop(inst); 785 } 786 787 #[test] 788 fn badly_specced_instance_does_not_take_up_capacity() { 789 let module = MockModuleBuilder::new() 790 .with_heap_spec(LARGE_GUARD_HEAP) 791 .build(); 792 let region = <TestRegion as RegionCreate>::create(2, &LIMITS).expect("region created"); 793 assert_eq!(region.capacity(), 2); 794 assert_eq!(region.free_slots(), 2); 795 assert_eq!(region.used_slots(), 0); 796 let bad_inst_res = region.new_instance(module.clone()); 797 assert!(bad_inst_res.is_err()); 798 assert_eq!(region.capacity(), 2); 799 assert_eq!(region.free_slots(), 2); 800 assert_eq!(region.used_slots(), 0); 801 } 802 803 #[test] 804 fn slot_counts_work() { 805 let module = MockModuleBuilder::new() 806 .with_heap_spec(ONE_PAGE_HEAP) 807 .build(); 808 let region = <TestRegion as RegionCreate>::create(2, &LIMITS).expect("region created"); 809 assert_eq!(region.capacity(), 2); 810 assert_eq!(region.free_slots(), 2); 811 assert_eq!(region.used_slots(), 0); 812 let inst1 = region 813 .new_instance(module.clone()) 814 .expect("new_instance succeeds"); 815 assert_eq!(region.capacity(), 2); 816 assert_eq!(region.free_slots(), 1); 817 assert_eq!(region.used_slots(), 1); 818 let inst2 = region.new_instance(module).expect("new_instance succeeds"); 819 assert_eq!(region.capacity(), 2); 820 assert_eq!(region.free_slots(), 0); 821 assert_eq!(region.used_slots(), 2); 822 drop(inst1); 823 assert_eq!(region.capacity(), 2); 824 assert_eq!(region.free_slots(), 1); 825 assert_eq!(region.used_slots(), 1); 826 drop(inst2); 827 assert_eq!(region.capacity(), 2); 828 assert_eq!(region.free_slots(), 2); 829 assert_eq!(region.used_slots(), 0); 830 } 831 832 /// This test exercises the AllocStrategy::Random. In this scenario, 833 /// the Region has a single slot which is "randomly" allocated and then dropped. 834 #[test] 835 fn slot_counts_work_with_random_alloc() { 836 let module = MockModuleBuilder::new() 837 .with_heap_spec(ONE_PAGE_HEAP) 838 .build(); 839 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 840 assert_eq!(region.capacity(), 1); 841 assert_eq!(region.free_slots(), 1); 842 assert_eq!(region.used_slots(), 0); 843 844 let inst = region 845 .new_instance_builder(module.clone()) 846 .with_alloc_strategy(AllocStrategy::Random) 847 .build() 848 .expect("new_instance succeeds"); 849 assert_eq!(region.capacity(), 1); 850 assert_eq!(region.free_slots(), 0); 851 assert_eq!(region.used_slots(), 1); 852 853 drop(inst); 854 assert_eq!(region.capacity(), 1); 855 assert_eq!(region.free_slots(), 1); 856 assert_eq!(region.used_slots(), 0); 857 } 858 859 /// This test exercises the AllocStrategy::CustomRandom. In this scenario, 860 /// the Region has 10 slots which are randomly allocated up to capacity 861 /// and then dropped. The test is executed 100 times to exercise the 862 /// random nature of the allocation strategy. 863 #[test] 864 fn slot_counts_work_with_custom_random_alloc() { 865 let mut master_rng = thread_rng(); 866 let seed: u64 = master_rng.gen(); 867 eprintln!( 868 "Seeding slot_counts_work_with_custom_random_alloc() with {}", 869 seed 870 ); 871 872 let rng: StdRng = SeedableRng::seed_from_u64(seed); 873 let shared_rng = Arc::new(Mutex::new(rng)); 874 875 for _ in 0..100 { 876 let mut inst_vec = Vec::new(); 877 let module = MockModuleBuilder::new() 878 .with_heap_spec(ONE_PAGE_HEAP) 879 .build(); 880 let total_slots = 10; 881 let region = <TestRegion as RegionCreate>::create(total_slots, &LIMITS) 882 .expect("region created"); 883 assert_eq!(region.capacity(), total_slots); 884 assert_eq!(region.free_slots(), 10); 885 assert_eq!(region.used_slots(), 0); 886 887 // Randomly allocate all of the slots in the region. 888 for i in 1..=total_slots { 889 let inst = region 890 .new_instance_builder(module.clone()) 891 .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) 892 .build() 893 .expect("new_instance succeeds"); 894 895 assert_eq!(region.capacity(), total_slots); 896 assert_eq!(region.free_slots(), total_slots - i); 897 assert_eq!(region.used_slots(), i); 898 inst_vec.push(inst); 899 } 900 901 // It's not possible to allocate just one more. Try 902 // it and affirm that the error is handled gracefully. 903 let wont_inst = region 904 .new_instance_builder(module.clone()) 905 .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) 906 .build(); 907 assert!(wont_inst.is_err()); 908 909 // Drop all of the slots in the region. 910 for i in 1..=total_slots { 911 drop(inst_vec.pop()); 912 assert_eq!(region.capacity(), total_slots); 913 assert_eq!(region.free_slots(), total_slots - (total_slots - i)); 914 assert_eq!(region.used_slots(), total_slots - i); 915 } 916 917 // Allocate just one more to make sure the drops took place 918 // and the Region has capacity again. 919 region 920 .new_instance_builder(module.clone()) 921 .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) 922 .build() 923 .expect("new_instance succeeds"); 924 } 925 } 926 927 /// This test exercises a mixed AllocStrategy. In this scenario, 928 /// the Region has 10 slots which are randomly and linearly allocated 929 /// up to capacity and then dropped. The test is executed 100 times to 930 /// exercise the random nature of the allocation strategy. 931 #[test] 932 fn slot_counts_work_with_mixed_alloc() { 933 let mut master_rng = thread_rng(); 934 let seed: u64 = master_rng.gen(); 935 eprintln!("Seeding slot_counts_work_with_mixed_alloc() with {}", seed); 936 937 let rng: StdRng = SeedableRng::seed_from_u64(seed); 938 let shared_rng = Arc::new(Mutex::new(rng)); 939 940 for _ in 0..100 { 941 let mut inst_vec = Vec::new(); 942 let module = MockModuleBuilder::new() 943 .with_heap_spec(ONE_PAGE_HEAP) 944 .build(); 945 let total_slots = 10; 946 let region = <TestRegion as RegionCreate>::create(total_slots, &LIMITS) 947 .expect("region created"); 948 assert_eq!(region.capacity(), total_slots); 949 assert_eq!(region.free_slots(), 10); 950 assert_eq!(region.used_slots(), 0); 951 952 // Allocate all of the slots in the region. 953 for i in 1..=total_slots { 954 let inst; 955 if i % 2 == 0 { 956 inst = region 957 .new_instance_builder(module.clone()) 958 .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) 959 .build() 960 .expect("new_instance succeeds"); 961 } else { 962 inst = region 963 .new_instance_builder(module.clone()) 964 .with_alloc_strategy(AllocStrategy::Linear) 965 .build() 966 .expect("new_instance succeeds"); 967 } 968 969 assert_eq!(region.capacity(), total_slots); 970 assert_eq!(region.free_slots(), total_slots - i); 971 assert_eq!(region.used_slots(), i); 972 inst_vec.push(inst); 973 } 974 975 // It's not possible to allocate just one more. Try 976 // it and affirm that the error is handled gracefully. 977 let wont_inst = region 978 .new_instance_builder(module.clone()) 979 .with_alloc_strategy(AllocStrategy::CustomRandom(shared_rng.clone())) 980 .build(); 981 assert!(wont_inst.is_err()); 982 983 // Drop all of the slots in the region. 984 for i in 1..=total_slots { 985 drop(inst_vec.pop()); 986 assert_eq!(region.capacity(), total_slots); 987 assert_eq!(region.free_slots(), total_slots - (total_slots - i)); 988 assert_eq!(region.used_slots(), total_slots - i); 989 } 990 991 // Allocate just one more to make sure the drops took place 992 // and the Region has capacity again. 993 region 994 .new_instance_builder(module.clone()) 995 .with_alloc_strategy(AllocStrategy::Linear) 996 .build() 997 .expect("new_instance succeeds"); 998 } 999 } 1000 1001 fn do_nothing_module() -> Arc<dyn Module> { 1002 extern "C" fn do_nothing(_vmctx: *const lucet_vmctx) -> () {} 1003 1004 MockModuleBuilder::new() 1005 .with_export_func(MockExportBuilder::new( 1006 "do_nothing", 1007 FunctionPointer::from_usize(do_nothing as usize), 1008 )) 1009 .build() 1010 } 1011 1012 #[test] 1013 fn reject_sigstack_smaller_than_min() { 1014 if MINSIGSTKSZ == 0 { 1015 // can't trigger the error on this platform 1016 return; 1017 } 1018 let limits = Limits { 1019 // keep it page-aligned but make it too small 1020 signal_stack_size: (MINSIGSTKSZ.checked_sub(1).unwrap() / host_page_size()) 1021 * host_page_size(), 1022 ..Limits::default() 1023 }; 1024 let region = <TestRegion as RegionCreate>::create(1, &limits).expect("region created"); 1025 let mut inst = region 1026 .new_instance(do_nothing_module()) 1027 .expect("new_instance succeeds"); 1028 1029 // run the bad one a bunch of times, in case there's some bad state left over following 1030 // a failure 1031 for _ in 0..5 { 1032 match inst.run("do_nothing", &[]) { 1033 Err(Error::InvalidArgument( 1034 "signal stack size must be at least MINSIGSTKSZ (defined in <signal.h>)", 1035 )) => (), 1036 Err(e) => panic!("unexpected error: {}", e), 1037 Ok(_) => panic!("unexpected success"), 1038 } 1039 assert!(inst.is_ready()); 1040 } 1041 1042 // now make sure that we can run an instance with reasonable limits on this same thread, 1043 // to make sure the `CURRENT_INSTANCE` thread-local isn't left in a bad state 1044 let region = <TestRegion as RegionCreate>::create(1, &Limits::default()) 1045 .expect("region created"); 1046 let mut inst = region 1047 .new_instance(do_nothing_module()) 1048 .expect("new_instance succeeds"); 1049 inst.run("do_nothing", &[]).expect("run succeeds"); 1050 } 1051 1052 /// This test ensures that a signal stack smaller than 12KiB is rejected when Lucet is 1053 /// compiled in debug mode. 1054 #[test] 1055 #[cfg(debug_assertions)] 1056 fn reject_debug_sigstack_smaller_than_12kib() { 1057 if 8192 < MINSIGSTKSZ { 1058 // can't trigger the error on this platform, as the MINSIGSTKSZ check runs first 1059 return; 1060 } 1061 let limits = Limits { 1062 signal_stack_size: 8192, 1063 ..Limits::default() 1064 }; 1065 let region = <TestRegion as RegionCreate>::create(1, &limits).expect("region created"); 1066 let mut inst = region 1067 .new_instance(do_nothing_module()) 1068 .expect("new_instance succeeds"); 1069 match inst.run("do_nothing", &[]) { 1070 Err(Error::InvalidArgument( 1071 "signal stack size must be at least 12KiB for debug builds", 1072 )) => (), 1073 Err(e) => panic!("unexpected error: {}", e), 1074 Ok(_) => panic!("unexpected success"), 1075 } 1076 assert!(inst.is_ready()); 1077 1078 // now make sure that we can run an instance with reasonable limits on this same thread, 1079 // to make sure the `CURRENT_INSTANCE` thread-local isn't left in a bad state 1080 let region = <TestRegion as RegionCreate>::create(1, &Limits::default()) 1081 .expect("region created"); 1082 let mut inst = region 1083 .new_instance(do_nothing_module()) 1084 .expect("new_instance succeeds"); 1085 inst.run("do_nothing", &[]).expect("run succeeds"); 1086 } 1087 1088 #[test] 1089 fn reject_unaligned_sigstack() { 1090 let limits = Limits { 1091 signal_stack_size: std::cmp::max(libc::SIGSTKSZ, 12 * 1024) 1092 .checked_add(1) 1093 .unwrap(), 1094 ..Limits::default() 1095 }; 1096 let res = <TestRegion as RegionCreate>::create(1, &limits); 1097 match res { 1098 Err(Error::InvalidArgument( 1099 "signal stack size must be a multiple of host page size", 1100 )) => (), 1101 Err(e) => panic!("unexpected error: {}", e), 1102 Ok(_) => panic!("unexpected success"), 1103 } 1104 } 1105 1106 /// This test exercises custom limits on the heap_memory_size. 1107 /// In this scenario, the Region has a limit on the heap 1108 /// memory size, but the instance has a larger limit. An 1109 /// instance's custom limit must not exceed the Region's. 1110 #[test] 1111 fn reject_heap_memory_size_exceeds_region_limits() { 1112 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 1113 let module = MockModuleBuilder::new() 1114 .with_heap_spec(THREE_PAGE_MAX_HEAP) 1115 .build(); 1116 let res = region 1117 .new_instance_builder(module.clone()) 1118 .with_heap_size_limit(&LIMITS.heap_memory_size * 2) 1119 .build(); 1120 1121 match res { 1122 Err(Error::InvalidArgument( 1123 "heap memory size requested for instance is larger than slot allows", 1124 )) => (), 1125 Err(e) => panic!("unexpected error: {}", e), 1126 Ok(_) => panic!("unexpected success"), 1127 } 1128 } 1129 1130 /// This test exercises custom limits on the heap_memory_size. 1131 /// In this scenario, successfully create a custom-sized 1132 /// instance, drop it, then create a default-sized instance to 1133 /// affirm that a custom size doesn't somehow overwrite the 1134 /// default size. 1135 #[test] 1136 fn custom_size_does_not_break_default() { 1137 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 1138 1139 // Build an instance that is has custom limits that are big 1140 // enough to accommodate the HeapSpec. 1141 let custom_inst = region 1142 .new_instance_builder( 1143 MockModuleBuilder::new() 1144 .with_heap_spec(THREE_PAGE_MAX_HEAP) 1145 .build(), 1146 ) 1147 .with_heap_size_limit((THREE_PAGE_MAX_HEAP.initial_size * 2) as usize) 1148 .build() 1149 .expect("new instance succeeds"); 1150 1151 // Affirm that its heap is the expected size, the size 1152 // specified in the HeapSpec. 1153 let heap_len = custom_inst.alloc().heap_len(); 1154 assert_eq!(heap_len, THREE_PAGE_MAX_HEAP.initial_size as usize); 1155 drop(custom_inst); 1156 1157 // Build a default heap-limited instance, to make sure the 1158 // custom limits didn't break the defaults. 1159 let default_inst = region 1160 .new_instance( 1161 MockModuleBuilder::new() 1162 .with_heap_spec(SMALL_GUARD_HEAP) 1163 .build(), 1164 ) 1165 .expect("new_instance succeeds"); 1166 1167 // Affirm that its heap is the expected size. 1168 let heap_len = default_inst.alloc().heap_len(); 1169 assert_eq!(heap_len, SMALL_GUARD_HEAP.initial_size as usize); 1170 } 1171 1172 /// This test exercises custom limits on the heap_memory_size. 1173 /// In this scenario, the HeapSpec has an expectation for the 1174 /// initial heap memory size, but the instance's limit is too small. 1175 #[test] 1176 fn reject_heap_memory_size_exeeds_instance_limits() { 1177 let region = <TestRegion as RegionCreate>::create(1, &LIMITS).expect("region created"); 1178 let res = region 1179 .new_instance_builder( 1180 MockModuleBuilder::new() 1181 .with_heap_spec(THREE_PAGE_MAX_HEAP) 1182 .build(), 1183 ) 1184 .with_heap_size_limit((THREE_PAGE_MAX_HEAP.initial_size / 2) as usize) 1185 .build(); 1186 1187 assert!(res.is_err(), "new_instance fails"); 1188 } 1189 }; 1190 } 1191 1192 #[cfg(test)] 1193 mod mmap { 1194 alloc_tests!(crate::region::mmap::MmapRegion); 1195 } 1196 1197 #[cfg(all(test, target_os = "linux", feature = "uffd"))] 1198 mod uffd { 1199 alloc_tests!(crate::region::uffd::UffdRegion); 1200 } 1201