1 use rayon::prelude::*; 2 use std::sync::atomic::{AtomicU32, Ordering::SeqCst}; 3 use std::time::Duration; 4 use wasmtime::*; 5 use wasmtime_test_macros::wasmtime_test; 6 7 fn module(engine: &Engine) -> Result<Module> { 8 let mut wat = format!("(module\n"); 9 wat.push_str("(import \"\" \"\" (memory 0))\n"); 10 for i in 0..=33 { 11 let offset = if i == 0 { 12 0 13 } else if i == 33 { 14 !0 15 } else { 16 1u32 << (i - 1) 17 }; 18 19 for (width, instr) in [ 20 (1, &["i32.load8_s"][..]), 21 (2, &["i32.load16_s"]), 22 (4, &["i32.load" /*, "f32.load"*/]), 23 (8, &["i64.load" /*, "f64.load"*/]), 24 #[cfg(not(any(target_arch = "s390x", target_arch = "riscv64")))] 25 (16, &["v128.load"]), 26 ] 27 .iter() 28 { 29 for (j, instr) in instr.iter().enumerate() { 30 wat.push_str(&format!( 31 "(func (export \"{width} {offset} v{j}\") (param i32)\n" 32 )); 33 wat.push_str("local.get 0\n"); 34 wat.push_str(instr); 35 wat.push_str(&format!(" offset={offset}\n")); 36 wat.push_str("drop\n)"); 37 } 38 } 39 } 40 wat.push_str(")"); 41 Module::new(engine, &wat) 42 } 43 44 struct TestFunc { 45 width: u32, 46 offset: u32, 47 func: TypedFunc<u32, ()>, 48 } 49 50 fn find_funcs(store: &mut Store<()>, instance: &Instance) -> Vec<TestFunc> { 51 let list = instance 52 .exports(&mut *store) 53 .map(|export| { 54 let name = export.name(); 55 let mut parts = name.split_whitespace(); 56 ( 57 parts.next().unwrap().parse().unwrap(), 58 parts.next().unwrap().parse().unwrap(), 59 export.into_func().unwrap(), 60 ) 61 }) 62 .collect::<Vec<_>>(); 63 list.into_iter() 64 .map(|(width, offset, func)| TestFunc { 65 width, 66 offset, 67 func: func.typed(&store).unwrap(), 68 }) 69 .collect() 70 } 71 72 fn test_traps(store: &mut Store<()>, funcs: &[TestFunc], addr: u32, mem: &Memory) { 73 let mem_size = mem.data_size(&store) as u64; 74 for func in funcs { 75 let result = func.func.call(&mut *store, addr); 76 let base = u64::from(func.offset) + u64::from(addr); 77 let range = base..base + u64::from(func.width); 78 if range.start >= mem_size || range.end >= mem_size { 79 assert!( 80 result.is_err(), 81 "access at {}+{}+{} succeeded but should have failed when memory has {} bytes", 82 addr, 83 func.offset, 84 func.width, 85 mem_size 86 ); 87 } else { 88 assert!(result.is_ok()); 89 } 90 } 91 } 92 93 #[wasmtime_test(wasm_features(simd))] 94 #[cfg_attr(miri, ignore)] 95 fn offsets_static_dynamic_oh_my(config: &mut Config) -> Result<()> { 96 const GB: u64 = 1 << 30; 97 const MB: u64 = 1 << 20; 98 99 let mut engines = Vec::new(); 100 let sizes = if cfg!(target_pointer_width = "32") { 101 [0, 10 * MB, 20 * MB] 102 } else { 103 [0, 1 * GB, 4 * GB] 104 }; 105 for &memory_reservation in sizes.iter() { 106 for &guard_size in sizes.iter() { 107 for &guard_before_linear_memory in [true, false].iter() { 108 config.memory_reservation(memory_reservation); 109 config.memory_guard_size(guard_size); 110 config.guard_before_linear_memory(guard_before_linear_memory); 111 config.cranelift_debug_verifier(true); 112 engines.push(Engine::new(&config)?); 113 } 114 } 115 } 116 117 engines.par_iter().try_for_each(|engine| { 118 let module = module(&engine)?; 119 120 for (min, max) in [(1, Some(2)), (1, None)].iter() { 121 let mut store = Store::new(&engine, ()); 122 let mem = Memory::new(&mut store, MemoryType::new(*min, *max)).unwrap(); 123 let instance = Instance::new(&mut store, &module, &[mem.into()]).unwrap(); 124 let funcs = find_funcs(&mut store, &instance); 125 126 test_traps(&mut store, &funcs, 0, &mem); 127 test_traps(&mut store, &funcs, 65536, &mem); 128 test_traps(&mut store, &funcs, u32::MAX, &mem); 129 130 mem.grow(&mut store, 1).unwrap(); 131 132 test_traps(&mut store, &funcs, 0, &mem); 133 test_traps(&mut store, &funcs, 65536, &mem); 134 test_traps(&mut store, &funcs, u32::MAX, &mem); 135 } 136 Ok::<_, wasmtime::Error>(()) 137 })?; 138 139 Ok(()) 140 } 141 142 #[test] 143 #[cfg_attr(miri, ignore)] 144 #[cfg_attr(asan, ignore)] 145 fn guards_present() -> Result<()> { 146 const GUARD_SIZE: u64 = 65536; 147 148 let mut config = Config::new(); 149 config.memory_reservation(1 << 20); 150 config.memory_guard_size(GUARD_SIZE); 151 config.guard_before_linear_memory(true); 152 let engine = Engine::new(&config)?; 153 let mut store = Store::new(&engine, ()); 154 let static_mem = Memory::new(&mut store, MemoryType::new(1, Some(2)))?; 155 let dynamic_mem = Memory::new(&mut store, MemoryType::new(1, None))?; 156 157 let assert_guards = |store: &Store<()>| unsafe { 158 // guards before 159 println!("check pre-static-mem"); 160 assert_faults(static_mem.data_ptr(&store).offset(-(GUARD_SIZE as isize))); 161 println!("check pre-dynamic-mem"); 162 assert_faults(dynamic_mem.data_ptr(&store).offset(-(GUARD_SIZE as isize))); 163 164 // guards after 165 println!("check post-static-mem"); 166 assert_faults( 167 static_mem 168 .data_ptr(&store) 169 .add(static_mem.data_size(&store)), 170 ); 171 println!("check post-dynamic-mem"); 172 assert_faults( 173 dynamic_mem 174 .data_ptr(&store) 175 .add(dynamic_mem.data_size(&store)), 176 ); 177 }; 178 assert_guards(&store); 179 // static memory should start with the second page unmapped 180 unsafe { 181 assert_faults(static_mem.data_ptr(&store).add(65536)); 182 } 183 println!("growing"); 184 static_mem.grow(&mut store, 1).unwrap(); 185 dynamic_mem.grow(&mut store, 1).unwrap(); 186 assert_guards(&store); 187 188 Ok(()) 189 } 190 191 #[wasmtime_test] 192 #[cfg_attr(miri, ignore)] 193 #[cfg_attr(asan, ignore)] 194 fn guards_present_pooling(config: &mut Config) -> Result<()> { 195 const GUARD_SIZE: u64 = 65536; 196 197 let mut pool = crate::small_pool_config(); 198 pool.total_memories(2) 199 .max_memory_size(10 << 16) 200 .memory_protection_keys(MpkEnabled::Disable); 201 config.memory_reservation(1 << 20); 202 config.memory_guard_size(GUARD_SIZE); 203 config.guard_before_linear_memory(true); 204 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 205 let engine = Engine::new(&config)?; 206 207 let mut store = Store::new(&engine, ()); 208 209 let mem1 = { 210 let m = Module::new(&engine, "(module (memory (export \"\") 1 2))")?; 211 Instance::new(&mut store, &m, &[])? 212 .get_memory(&mut store, "") 213 .unwrap() 214 }; 215 let mem2 = { 216 let m = Module::new(&engine, "(module (memory (export \"\") 1))")?; 217 Instance::new(&mut store, &m, &[])? 218 .get_memory(&mut store, "") 219 .unwrap() 220 }; 221 222 unsafe fn assert_guards(store: &Store<()>, mem: &Memory) { 223 unsafe { 224 // guards before 225 println!("check pre-mem"); 226 assert_faults(mem.data_ptr(&store).offset(-(GUARD_SIZE as isize))); 227 228 // unmapped just after memory 229 println!("check mem"); 230 assert_faults(mem.data_ptr(&store).add(mem.data_size(&store))); 231 232 // guards after memory 233 println!("check post-mem"); 234 assert_faults(mem.data_ptr(&store).add(1 << 20)); 235 } 236 } 237 unsafe { 238 assert_guards(&store, &mem1); 239 assert_guards(&store, &mem2); 240 println!("growing"); 241 mem1.grow(&mut store, 1).unwrap(); 242 mem2.grow(&mut store, 1).unwrap(); 243 assert_guards(&store, &mem1); 244 assert_guards(&store, &mem2); 245 } 246 247 Ok(()) 248 } 249 250 #[wasmtime_test] 251 #[cfg_attr(miri, ignore)] 252 #[cfg_attr(asan, ignore)] 253 fn guards_present_pooling_mpk(config: &mut Config) -> Result<()> { 254 if !wasmtime::PoolingAllocationConfig::are_memory_protection_keys_available() { 255 println!("skipping `guards_present_pooling_mpk` test; mpk is not supported"); 256 return Ok(()); 257 } 258 259 const GUARD_SIZE: u64 = 65536; 260 let mut pool = crate::small_pool_config(); 261 pool.total_memories(4) 262 .max_memory_size(10 << 16) 263 .memory_protection_keys(MpkEnabled::Enable) 264 .max_memory_protection_keys(2); 265 config.memory_reservation(1 << 20); 266 config.memory_guard_size(GUARD_SIZE); 267 config.guard_before_linear_memory(true); 268 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 269 let engine = Engine::new(&config)?; 270 271 let mut store = Store::new(&engine, ()); 272 273 let mem1 = { 274 let m = Module::new(&engine, "(module (memory (export \"\") 1 2))")?; 275 Instance::new(&mut store, &m, &[])? 276 .get_memory(&mut store, "") 277 .unwrap() 278 }; 279 let mem2 = { 280 let m = Module::new(&engine, "(module (memory (export \"\") 1))")?; 281 Instance::new(&mut store, &m, &[])? 282 .get_memory(&mut store, "") 283 .unwrap() 284 }; 285 286 unsafe fn assert_guards(store: &Store<()>, mem: &Memory) { 287 unsafe { 288 // guards before 289 println!("check pre-mem"); 290 assert_faults(mem.data_ptr(&store).offset(-(GUARD_SIZE as isize))); 291 292 // unmapped just after memory 293 println!("check mem"); 294 assert_faults(mem.data_ptr(&store).add(mem.data_size(&store))); 295 296 // guards after memory 297 println!("check post-mem"); 298 assert_faults(mem.data_ptr(&store).add(1 << 20)); 299 } 300 } 301 unsafe { 302 assert_guards(&store, &mem1); 303 assert_guards(&store, &mem2); 304 println!("growing"); 305 mem1.grow(&mut store, 1).unwrap(); 306 mem2.grow(&mut store, 1).unwrap(); 307 assert_guards(&store, &mem1); 308 assert_guards(&store, &mem2); 309 } 310 311 Ok(()) 312 } 313 314 unsafe fn assert_faults(ptr: *mut u8) { 315 use std::io::Error; 316 #[cfg(unix)] 317 unsafe { 318 // There's probably a faster way to do this here, but, uh, when in rome? 319 match libc::fork() { 320 0 => { 321 *ptr = 4; 322 323 std::process::exit(0); 324 } 325 -1 => panic!("failed to fork: {}", Error::last_os_error()), 326 n => { 327 let mut status = 0; 328 assert!( 329 libc::waitpid(n, &mut status, 0) == n, 330 "failed to wait: {}", 331 Error::last_os_error() 332 ); 333 assert!(libc::WIFSIGNALED(status)); 334 } 335 } 336 } 337 #[cfg(windows)] 338 unsafe { 339 use windows_sys::Win32::System::Memory::*; 340 341 let mut info = std::mem::MaybeUninit::uninit(); 342 let r = VirtualQuery( 343 ptr as *const _, 344 info.as_mut_ptr(), 345 std::mem::size_of_val(&info), 346 ); 347 if r == 0 { 348 panic!("failed to VirtualAlloc: {}", Error::last_os_error()); 349 } 350 let info = info.assume_init(); 351 assert_eq!(info.AllocationProtect, PAGE_NOACCESS); 352 } 353 } 354 355 // Disable test on s390x because the large allocation may actually succeed; 356 // the whole 64-bit address space is available on this platform. 357 #[test] 358 #[cfg(not(target_arch = "s390x"))] 359 fn massive_64_bit_still_limited() -> Result<()> { 360 // Creating a 64-bit memory which exceeds the limits of the address space 361 // should still send a request to the `ResourceLimiter` to ensure that it 362 // gets at least some chance to see that oom was requested. 363 let mut config = Config::new(); 364 config.wasm_memory64(true); 365 let engine = Engine::new(&config)?; 366 367 let mut store = Store::new(&engine, MyLimiter { hit: false }); 368 store.limiter(|x| x); 369 let ty = MemoryType::new64(1 << 46, None); 370 assert!(Memory::new(&mut store, ty).is_err()); 371 assert!(store.data().hit); 372 373 return Ok(()); 374 375 struct MyLimiter { 376 hit: bool, 377 } 378 379 impl ResourceLimiter for MyLimiter { 380 fn memory_growing( 381 &mut self, 382 _current: usize, 383 _request: usize, 384 _max: Option<usize>, 385 ) -> Result<bool> { 386 self.hit = true; 387 Ok(true) 388 } 389 fn table_growing( 390 &mut self, 391 _current: usize, 392 _request: usize, 393 _max: Option<usize>, 394 ) -> Result<bool> { 395 unreachable!() 396 } 397 } 398 } 399 400 #[wasmtime_test] 401 #[cfg_attr(miri, ignore)] 402 fn tiny_static_heap(config: &mut Config) -> Result<()> { 403 // The size of the memory in the module below is the exact same size as 404 // the static memory size limit in the configuration. This is intended to 405 // specifically test that a load of all the valid addresses of the memory 406 // all pass bounds-checks in cranelift to help weed out any off-by-one bugs. 407 config.memory_reservation(1 << 16); 408 let engine = Engine::new(&config)?; 409 let mut store = Store::new(&engine, ()); 410 411 let module = Module::new( 412 &engine, 413 r#" 414 (module 415 (memory 1 1) 416 (func (export "run") 417 (local $i i32) 418 419 (loop 420 (if (i32.eq (local.get $i) (i32.const 65536)) 421 (then (return))) 422 (drop (i32.load8_u (local.get $i))) 423 (local.set $i (i32.add (local.get $i) (i32.const 1))) 424 br 0 425 ) 426 ) 427 ) 428 "#, 429 )?; 430 431 let i = Instance::new(&mut store, &module, &[])?; 432 let f = i.get_typed_func::<(), ()>(&mut store, "run")?; 433 f.call(&mut store, ())?; 434 Ok(()) 435 } 436 437 #[test] 438 fn static_forced_max() -> Result<()> { 439 let mut config = Config::new(); 440 config.memory_reservation(5 << 16); 441 config.memory_may_move(false); 442 let engine = Engine::new(&config)?; 443 let mut store = Store::new(&engine, ()); 444 445 let mem = Memory::new(&mut store, MemoryType::new(0, None))?; 446 mem.grow(&mut store, 5).unwrap(); 447 assert!(mem.grow(&mut store, 1).is_err()); 448 Ok(()) 449 } 450 451 #[wasmtime_test] 452 fn dynamic_extra_growth_unchanged_pointer(config: &mut Config) -> Result<()> { 453 const EXTRA_PAGES: u64 = 5; 454 config.memory_reservation(0); 455 // 5 wasm pages extra 456 config.memory_reservation_for_growth(EXTRA_PAGES * (1 << 16)); 457 let engine = Engine::new(&config)?; 458 let mut store = Store::new(&engine, ()); 459 460 fn assert_behaves_well(store: &mut Store<()>, mem: &Memory) -> Result<()> { 461 let ptr = mem.data_ptr(&store); 462 463 // Each growth here should retain the same linear pointer in memory and the 464 // memory shouldn't get moved. 465 for _ in 0..EXTRA_PAGES { 466 mem.grow(&mut *store, 1)?; 467 assert_eq!(ptr, mem.data_ptr(&store)); 468 } 469 470 // Growth afterwards though will be forced to move the pointer 471 mem.grow(&mut *store, 1)?; 472 let new_ptr = mem.data_ptr(&store); 473 assert_ne!(ptr, new_ptr); 474 475 for _ in 0..EXTRA_PAGES - 1 { 476 mem.grow(&mut *store, 1)?; 477 assert_eq!(new_ptr, mem.data_ptr(&store)); 478 } 479 Ok(()) 480 } 481 482 let mem = Memory::new(&mut store, MemoryType::new(10, None))?; 483 assert_behaves_well(&mut store, &mem)?; 484 485 let module = Module::new(&engine, r#"(module (memory (export "mem") 10))"#)?; 486 let instance = Instance::new(&mut store, &module, &[])?; 487 let mem = instance.get_memory(&mut store, "mem").unwrap(); 488 assert_behaves_well(&mut store, &mem)?; 489 490 let module = Module::new( 491 &engine, 492 r#" 493 (module 494 (memory (export "mem") 10) 495 (data (i32.const 0) "")) 496 "#, 497 )?; 498 let instance = Instance::new(&mut store, &module, &[])?; 499 let mem = instance.get_memory(&mut store, "mem").unwrap(); 500 assert_behaves_well(&mut store, &mem)?; 501 502 Ok(()) 503 } 504 505 // This test exercises trying to create memories of the maximum 64-bit memory 506 // size of `1 << 48` pages. This should always fail but in the process of 507 // determining this failure we shouldn't hit any overflows or anything like that 508 // (checked via debug-mode tests). 509 #[wasmtime_test] 510 fn memory64_maximum_minimum(config: &mut Config) -> Result<()> { 511 config.wasm_memory64(true); 512 let engine = Engine::new(&config)?; 513 let mut store = Store::new(&engine, ()); 514 515 assert!( 516 MemoryTypeBuilder::default() 517 .memory64(true) 518 .min(1 << 48) 519 .build() 520 .is_err() 521 ); 522 523 let module = Module::new( 524 &engine, 525 format!(r#"(module (import "" "" (memory i64 {})))"#, 1u64 << 48), 526 )?; 527 let mem_ty = module 528 .imports() 529 .next() 530 .unwrap() 531 .ty() 532 .unwrap_memory() 533 .clone(); 534 assert!(Memory::new(&mut store, mem_ty).is_err()); 535 536 let module = Module::new( 537 &engine, 538 &format!( 539 r#" 540 (module 541 (memory i64 {}) 542 ) 543 "#, 544 1u64 << 48, 545 ), 546 )?; 547 assert!(Instance::new(&mut store, &module, &[]).is_err()); 548 549 let module = Module::new( 550 &engine, 551 &format!( 552 r#" 553 (module 554 (memory i64 {}) 555 (data (i64.const 0) "") 556 ) 557 "#, 558 1u64 << 48, 559 ), 560 )?; 561 assert!(Instance::new(&mut store, &module, &[]).is_err()); 562 563 Ok(()) 564 } 565 566 #[test] 567 fn shared_memory_basics() -> Result<()> { 568 let engine = Engine::default(); 569 assert!(SharedMemory::new(&engine, MemoryType::new(1, None)).is_err()); 570 assert!(SharedMemory::new(&engine, MemoryType::new(1, Some(1))).is_err()); 571 assert!(SharedMemory::new(&engine, MemoryType::new64(1, None)).is_err()); 572 assert!(SharedMemory::new(&engine, MemoryType::new64(1, Some(1))).is_err()); 573 assert!( 574 MemoryTypeBuilder::default() 575 .shared(true) 576 .min(1) 577 .max(Some(0)) 578 .build() 579 .is_err() 580 ); 581 582 let memory = SharedMemory::new(&engine, MemoryType::shared(1, 1))?; 583 assert!(memory.ty().is_shared()); 584 assert_eq!(memory.ty().minimum(), 1); 585 assert_eq!(memory.ty().maximum(), Some(1)); 586 assert_eq!(memory.size(), 1); 587 assert_eq!(memory.data_size(), 65536); 588 assert_eq!(memory.data().len(), 65536); 589 assert!(memory.grow(1).is_err()); 590 591 // misaligned 592 assert_eq!(memory.atomic_notify(1, 100), Err(Trap::HeapMisaligned)); 593 assert_eq!( 594 memory.atomic_wait32(1, 100, None), 595 Err(Trap::HeapMisaligned) 596 ); 597 assert_eq!( 598 memory.atomic_wait64(1, 100, None), 599 Err(Trap::HeapMisaligned) 600 ); 601 602 // oob 603 assert_eq!( 604 memory.atomic_notify(1 << 20, 100), 605 Err(Trap::MemoryOutOfBounds) 606 ); 607 assert_eq!( 608 memory.atomic_wait32(1 << 20, 100, None), 609 Err(Trap::MemoryOutOfBounds) 610 ); 611 assert_eq!( 612 memory.atomic_wait64(1 << 20, 100, None), 613 Err(Trap::MemoryOutOfBounds) 614 ); 615 616 // ok 617 assert_eq!(memory.atomic_notify(8, 100), Ok(0)); 618 assert_eq!(memory.atomic_wait32(8, 1, None), Ok(WaitResult::Mismatch)); 619 assert_eq!(memory.atomic_wait64(8, 1, None), Ok(WaitResult::Mismatch)); 620 621 // timeout 622 let near_future = Duration::new(0, 100); 623 assert_eq!( 624 memory.atomic_wait32(8, 0, Some(near_future)), 625 Ok(WaitResult::TimedOut) 626 ); 627 assert_eq!( 628 memory.atomic_wait64(8, 0, Some(near_future)), 629 Ok(WaitResult::TimedOut) 630 ); 631 632 Ok(()) 633 } 634 635 #[test] 636 #[cfg_attr(miri, ignore)] 637 fn shared_memory_wait_notify() -> Result<()> { 638 const THREADS: usize = 8; 639 const COUNT: usize = 100_000; 640 641 let engine = Engine::default(); 642 let memory = SharedMemory::new(&engine, MemoryType::shared(1, 1))?; 643 let data = unsafe { AtomicU32::from_ptr(memory.data().as_ptr().cast_mut().cast()) }; 644 let locked = unsafe { AtomicU32::from_ptr(memory.data().as_ptr().add(4).cast_mut().cast()) }; 645 646 // Note that `SeqCst` is used here to not think much about the orderings 647 // here, and it also somewhat more closely mirrors what's happening in wasm. 648 let lock = || { 649 while locked.swap(1, SeqCst) == 1 { 650 memory.atomic_wait32(0, 1, None).unwrap(); 651 } 652 }; 653 let unlock = || { 654 locked.store(0, SeqCst); 655 memory.atomic_notify(0, 1).unwrap(); 656 }; 657 658 std::thread::scope(|s| { 659 for _ in 0..THREADS { 660 s.spawn(|| { 661 for _ in 0..COUNT { 662 lock(); 663 let next = data.load(SeqCst) + 1; 664 data.store(next, SeqCst); 665 unlock(); 666 } 667 }); 668 } 669 }); 670 671 assert_eq!(data.load(SeqCst), (THREADS * COUNT) as u32); 672 673 Ok(()) 674 } 675 676 #[wasmtime_test] 677 #[cfg_attr(miri, ignore)] 678 #[cfg(target_pointer_width = "64")] // requires large VM reservation 679 fn init_with_negative_segment(_: &mut Config) -> Result<()> { 680 let engine = Engine::default(); 681 let module = Module::new( 682 &engine, 683 r#" 684 (module 685 (memory 65536) 686 (data (i32.const 0x8000_0000) "x") 687 ) 688 "#, 689 )?; 690 let mut store = Store::new(&engine, ()); 691 Instance::new(&mut store, &module, &[])?; 692 Ok(()) 693 } 694 695 #[test] 696 fn non_page_aligned_static_memory() -> Result<()> { 697 let mut config = Config::new(); 698 config.memory_reservation(100_000); 699 config.memory_may_move(false); 700 let engine = Engine::new(&config)?; 701 let ty = MemoryType::new(1, None); 702 Memory::new(&mut Store::new(&engine, ()), ty)?; 703 Ok(()) 704 } 705 706 #[test] 707 fn new_memory_with_custom_page_size() -> Result<()> { 708 let engine = Engine::default(); 709 let mut store = Store::new(&engine, ()); 710 711 let ty = MemoryTypeBuilder::default() 712 .page_size_log2(0) 713 .min(4096) 714 .max(Some(9000)) 715 .build()?; 716 717 let mem = Memory::new(&mut store, ty)?; 718 assert_eq!(mem.data_size(&store), 4096); 719 assert_eq!(mem.size(&store), 4096); 720 721 mem.grow(&mut store, 9000 - 4096)?; 722 assert_eq!(mem.data_size(&store), 9000); 723 assert_eq!(mem.size(&store), 9000); 724 725 assert!(mem.grow(&mut store, 1).is_err()); 726 assert_eq!(mem.data_size(&store), 9000); 727 assert_eq!(mem.size(&store), 9000); 728 729 Ok(()) 730 } 731 732 #[wasmtime_test] 733 #[cfg_attr(miri, ignore)] 734 fn get_memory_type_with_custom_page_size_from_wasm(config: &mut Config) -> Result<()> { 735 config.wasm_custom_page_sizes(true); 736 let engine = Engine::new(&config)?; 737 let mut store = Store::new(&engine, ()); 738 739 let module = Module::new( 740 &engine, 741 r#" 742 (module 743 (memory (export "memory") 1 0xffffffff (pagesize 1)) 744 ) 745 "#, 746 )?; 747 748 let instance = Instance::new(&mut store, &module, &[])?; 749 let memory = instance.get_memory(&mut store, "memory").unwrap(); 750 let mem_ty = memory.ty(&store); 751 752 assert_eq!(mem_ty.minimum(), 1); 753 assert_eq!(mem_ty.maximum(), Some(0xffffffff)); 754 assert_eq!(mem_ty.page_size(), 1); 755 assert_eq!(mem_ty.page_size_log2(), 0); 756 757 Ok(()) 758 } 759 760 #[wasmtime_test] 761 fn configure_zero(config: &mut Config) -> Result<()> { 762 config.guard_before_linear_memory(false); 763 config.memory_guard_size(0); 764 config.memory_reservation(0); 765 config.memory_reservation_for_growth(0); 766 let engine = Engine::new(&config)?; 767 let mut store = Store::new(&engine, ()); 768 769 let ty = MemoryType::new(0, None); 770 let memory = Memory::new(&mut store, ty)?; 771 assert_eq!(memory.data_size(&store), 0); 772 773 Ok(()) 774 } 775