1 //! Memory management for linear memories. 2 //! 3 //! This module implements the runtime data structures that manage linear 4 //! memories for WebAssembly. There's a number of types here each with various 5 //! purposes, and this is the high level relationships between types where an 6 //! arrow here means "builds on top of". 7 //! 8 //! ```text 9 //! ┌─────────────────────┐ 10 //! │ │ 11 //! │ Memory ├─────────────┐ 12 //! │ │ │ 13 //! └──────────┬──────────┘ │ 14 //! │ │ 15 //! │ │ 16 //! ▼ ▼ 17 //! ┌─────────────────────┐ ┌──────────────┐ 18 //! │ │ │ │ 19 //! │ LocalMemory │◄────┤ SharedMemory │ 20 //! │ │ │ │ 21 //! └──────────┬──────────┘ └──────────────┘ 22 //! │ 23 //! │ 24 //! ▼ 25 //! ┌─────────────────────┐ 26 //! │ │ 27 //! │ RuntimeLinearMemory ├─────────────┬───────────────┐ 28 //! │ │ │ │ 29 //! └──────────┬──────────┘ │ │ 30 //! │ │ │ 31 //! │ │ │ 32 //! ▼ ▼ ▼ 33 //! ┌─────────────────────┐ ┌──────────────┐ ┌─────┐ 34 //! │ │ │ │ │ │ 35 //! │ MmapMemory │ │ StaticMemory │ │ ... │ 36 //! │ │ │ │ │ │ 37 //! └─────────────────────┘ └──────────────┘ └─────┘ 38 //! ``` 39 //! 40 //! In more detail: 41 //! 42 //! * `Memory` - the root of what's actually stored in a wasm instance. This 43 //! implements the high-level embedder APIs one would expect from a wasm 44 //! linear memory. 45 //! 46 //! * `SharedMemory` - this is one of the variants of a local memory. A shared 47 //! memory contains `RwLock<LocalMemory>` where all the real bits happen 48 //! within the lock. 49 //! 50 //! * `LocalMemory` - this is an owned allocation of a linear memory which 51 //! maintains low-level state that's shared between `SharedMemory` and the 52 //! instance-local state of `Memory`. One example is that `LocalMemory::grow` 53 //! has most of the logic around memory growth. 54 //! 55 //! * `RuntimeLinearMemory` - this is a trait which `LocalMemory` delegates to. 56 //! This trait is intentionally relatively simple to be exposed in Wasmtime's 57 //! embedder API. This is exposed all the way through `wasmtime::Config` so 58 //! embedders can provide arbitrary implementations. 59 //! 60 //! * `MmapMemory` - this is an implementation of `RuntimeLinearMemory` in terms 61 //! of the platform's mmap primitive. 62 //! 63 //! * `StaticMemory` - this is an implementation of `RuntimeLinearMemory` 64 //! for the pooling allocator where the base pointer is already allocated 65 //! and contents are managed through `MemoryImageSlot`. 66 //! 67 //! Other important types for memories are `MemoryImage` and `MemoryImageSlot` 68 //! which manage CoW state for memories. This is implemented at the 69 //! `LocalMemory` layer. 70 //! 71 //! FIXME: don't have both RuntimeLinearMemory and wasmtime::LinearMemory, they 72 //! should be merged together. 73 //! 74 //! FIXME: don't have both RuntimeMemoryCreator and wasmtime::MemoryCreator, 75 //! they should be merged together. 76 77 use crate::prelude::*; 78 use crate::runtime::store::StoreResourceLimiter; 79 use crate::runtime::vm::vmcontext::VMMemoryDefinition; 80 #[cfg(has_virtual_memory)] 81 use crate::runtime::vm::{HostAlignedByteCount, MmapOffset}; 82 use crate::runtime::vm::{MemoryImage, MemoryImageSlot, SendSyncPtr}; 83 use alloc::sync::Arc; 84 use core::{ops::Range, ptr::NonNull}; 85 use wasmtime_environ::Tunables; 86 87 #[cfg(feature = "threads")] 88 use wasmtime_environ::Trap; 89 90 #[cfg(has_virtual_memory)] 91 mod mmap; 92 #[cfg(has_virtual_memory)] 93 pub use self::mmap::MmapMemory; 94 95 mod malloc; 96 pub use self::malloc::MallocMemory; 97 98 #[cfg(feature = "pooling-allocator")] 99 mod static_; 100 #[cfg(feature = "pooling-allocator")] 101 use self::static_::StaticMemory; 102 103 #[cfg(feature = "threads")] 104 mod shared_memory; 105 #[cfg(feature = "threads")] 106 pub use shared_memory::SharedMemory; 107 108 #[cfg(not(feature = "threads"))] 109 mod shared_memory_disabled; 110 #[cfg(not(feature = "threads"))] 111 pub use shared_memory_disabled::SharedMemory; 112 113 /// A memory allocator 114 pub trait RuntimeMemoryCreator: Send + Sync { 115 /// Create new RuntimeLinearMemory 116 fn new_memory( 117 &self, 118 ty: &wasmtime_environ::Memory, 119 tunables: &Tunables, 120 minimum: usize, 121 maximum: Option<usize>, 122 ) -> Result<Box<dyn RuntimeLinearMemory>>; 123 } 124 125 /// A default memory allocator used by Wasmtime 126 pub struct DefaultMemoryCreator; 127 128 impl RuntimeMemoryCreator for DefaultMemoryCreator { 129 /// Create new MmapMemory 130 fn new_memory( 131 &self, 132 ty: &wasmtime_environ::Memory, 133 tunables: &Tunables, 134 minimum: usize, 135 maximum: Option<usize>, 136 ) -> Result<Box<dyn RuntimeLinearMemory>> { 137 #[cfg(has_virtual_memory)] 138 if tunables.signals_based_traps 139 || tunables.memory_guard_size > 0 140 || tunables.memory_reservation > 0 141 || tunables.memory_init_cow 142 { 143 return Ok(Box::new(MmapMemory::new(ty, tunables, minimum, maximum)?)); 144 } 145 146 let _ = maximum; 147 Ok(Box::new(MallocMemory::new(ty, tunables, minimum)?)) 148 } 149 } 150 151 /// A linear memory and its backing storage. 152 pub trait RuntimeLinearMemory: Send + Sync { 153 /// Returns the number bytes that this linear memory can access. 154 fn byte_size(&self) -> usize; 155 156 /// Returns the maximal number of bytes the current allocation can access. 157 /// 158 /// Growth up to this value should not relocate the base pointer. 159 fn byte_capacity(&self) -> usize; 160 161 /// Grow memory to the specified amount of bytes. 162 /// 163 /// Returns an error if memory can't be grown by the specified amount 164 /// of bytes. 165 fn grow_to(&mut self, size: usize) -> Result<()>; 166 167 /// Returns a pointer to the base of this linear memory allocation. 168 /// 169 /// This is either a raw pointer, or a reference to an mmap along with an 170 /// offset within it. 171 fn base(&self) -> MemoryBase; 172 173 /// Get a `VMMemoryDefinition` for this linear memory. 174 fn vmmemory(&self) -> VMMemoryDefinition; 175 176 /// Internal method for Wasmtime when used in conjunction with CoW images. 177 /// This is used to inform the underlying memory that the size of memory has 178 /// changed. 179 /// 180 /// Note that this is hidden and panics by default as embedders using custom 181 /// memory without CoW images shouldn't have to worry about this. 182 #[doc(hidden)] 183 fn set_byte_size(&mut self, len: usize) { 184 let _ = len; 185 panic!("CoW images used with this memory and it doesn't support it"); 186 } 187 } 188 189 /// The base pointer of a memory allocation. 190 #[derive(Clone, Debug)] 191 pub enum MemoryBase { 192 /// A raw pointer into memory. 193 /// 194 /// This may or may not be host-page-aligned. 195 Raw(SendSyncPtr<u8>), 196 197 /// An mmap along with an offset into it. 198 #[cfg(has_virtual_memory)] 199 Mmap(MmapOffset), 200 } 201 202 impl MemoryBase { 203 /// Creates a new `MemoryBase` from a raw pointer. 204 /// 205 /// The pointer must be non-null, and it must be logically `Send + Sync`. 206 pub fn new_raw(ptr: *mut u8) -> Self { 207 Self::Raw(NonNull::new(ptr).expect("pointer is non-null").into()) 208 } 209 210 /// Returns the actual memory address in memory that is represented by this 211 /// base. 212 pub fn as_non_null(&self) -> NonNull<u8> { 213 match self { 214 Self::Raw(ptr) => ptr.as_non_null(), 215 #[cfg(has_virtual_memory)] 216 Self::Mmap(mmap_offset) => mmap_offset.as_non_null(), 217 } 218 } 219 220 /// Same as `as_non_null`, but different return type. 221 pub fn as_mut_ptr(&self) -> *mut u8 { 222 self.as_non_null().as_ptr() 223 } 224 } 225 226 /// Representation of a runtime wasm linear memory. 227 pub enum Memory { 228 Local(LocalMemory), 229 Shared(SharedMemory), 230 } 231 232 impl Memory { 233 /// Create a new dynamic (movable) memory instance for the specified plan. 234 pub async fn new_dynamic( 235 ty: &wasmtime_environ::Memory, 236 tunables: &Tunables, 237 creator: &dyn RuntimeMemoryCreator, 238 memory_image: Option<&Arc<MemoryImage>>, 239 limiter: Option<&mut StoreResourceLimiter<'_>>, 240 ) -> Result<Self> { 241 let (minimum, maximum) = Self::limit_new(ty, limiter).await?; 242 let allocation = creator.new_memory(ty, tunables, minimum, maximum)?; 243 244 let memory = LocalMemory::new(ty, tunables, allocation, memory_image)?; 245 Ok(if ty.shared { 246 Memory::Shared(SharedMemory::wrap(ty, memory)?) 247 } else { 248 Memory::Local(memory) 249 }) 250 } 251 252 /// Create a new static (immovable) memory instance for the specified plan. 253 #[cfg(feature = "pooling-allocator")] 254 pub async fn new_static( 255 ty: &wasmtime_environ::Memory, 256 tunables: &Tunables, 257 base: MemoryBase, 258 base_capacity: usize, 259 memory_image: MemoryImageSlot, 260 limiter: Option<&mut StoreResourceLimiter<'_>>, 261 ) -> Result<Self> { 262 let (minimum, maximum) = Self::limit_new(ty, limiter).await?; 263 let pooled_memory = StaticMemory::new(base, base_capacity, minimum, maximum)?; 264 let allocation = Box::new(pooled_memory); 265 266 // Configure some defaults a bit differently for this memory within the 267 // `LocalMemory` structure created, notably we already have 268 // `memory_image` and regardless of configuration settings this memory 269 // can't move its base pointer since it's a fixed allocation. 270 let mut memory = LocalMemory::new(ty, tunables, allocation, None)?; 271 assert!(memory.memory_image.is_none()); 272 memory.memory_image = Some(memory_image); 273 memory.memory_may_move = false; 274 275 Ok(if ty.shared { 276 // FIXME(#4244): not supported with the pooling allocator (which 277 // `new_static` is always used with), see `MemoryPool::validate` as 278 // well). 279 todo!("using shared memory with the pooling allocator is a work in progress"); 280 } else { 281 Memory::Local(memory) 282 }) 283 } 284 285 /// Calls the `store`'s limiter to optionally prevent a memory from being allocated. 286 /// 287 /// Returns a tuple of the minimum size, optional maximum size, and log(page 288 /// size) of the memory, all in bytes. 289 pub(crate) async fn limit_new( 290 ty: &wasmtime_environ::Memory, 291 limiter: Option<&mut StoreResourceLimiter<'_>>, 292 ) -> Result<(usize, Option<usize>)> { 293 let page_size = usize::try_from(ty.page_size()).unwrap(); 294 295 // This is the absolute possible maximum that the module can try to 296 // allocate, which is our entire address space minus a wasm page. That 297 // shouldn't ever actually work in terms of an allocation because 298 // presumably the kernel wants *something* for itself, but this is used 299 // to pass to the `store`'s limiter for a requested size 300 // to approximate the scale of the request that the wasm module is 301 // making. This is necessary because the limiter works on `usize` bytes 302 // whereas we're working with possibly-overflowing `u64` calculations 303 // here. To actually faithfully represent the byte requests of modules 304 // we'd have to represent things as `u128`, but that's kinda 305 // overkill for this purpose. 306 let absolute_max = 0usize.wrapping_sub(page_size); 307 308 // If the minimum memory size overflows the size of our own address 309 // space, then we can't satisfy this request, but defer the error to 310 // later so the `store` can be informed that an effective oom is 311 // happening. 312 let minimum = ty 313 .minimum_byte_size() 314 .ok() 315 .and_then(|m| usize::try_from(m).ok()); 316 317 // The plan stores the maximum size in units of wasm pages, but we 318 // use units of bytes. Unlike for the `minimum` size we silently clamp 319 // the effective maximum size to the limits of what we can track. If the 320 // maximum size exceeds `usize` or `u64` then there's no need to further 321 // keep track of it as some sort of runtime limit will kick in long 322 // before we reach the statically declared maximum size. 323 let maximum = ty 324 .maximum_byte_size() 325 .ok() 326 .and_then(|m| usize::try_from(m).ok()); 327 328 // Inform the store's limiter what's about to happen. This will let the 329 // limiter reject anything if necessary, and this also guarantees that 330 // we should call the limiter for all requested memories, even if our 331 // `minimum` calculation overflowed. This means that the `minimum` we're 332 // informing the limiter is lossy and may not be 100% accurate, but for 333 // now the expected uses of limiter means that's ok. 334 if let Some(limiter) = limiter { 335 if !limiter 336 .memory_growing(0, minimum.unwrap_or(absolute_max), maximum) 337 .await? 338 { 339 bail!( 340 "memory minimum size of {} pages exceeds memory limits", 341 ty.limits.min 342 ); 343 } 344 } 345 346 // At this point we need to actually handle overflows, so bail out with 347 // an error if we made it this far. 348 let minimum = minimum.ok_or_else(|| { 349 format_err!( 350 "memory minimum size of {} pages exceeds memory limits", 351 ty.limits.min 352 ) 353 })?; 354 355 Ok((minimum, maximum)) 356 } 357 358 /// Returns this memory's page size, in bytes. 359 pub fn page_size(&self) -> u64 { 360 match self { 361 Memory::Local(mem) => mem.page_size(), 362 Memory::Shared(mem) => mem.page_size(), 363 } 364 } 365 366 /// Returns the size of this memory, in bytes. 367 pub fn byte_size(&self) -> usize { 368 match self { 369 Memory::Local(mem) => mem.byte_size(), 370 Memory::Shared(mem) => mem.byte_size(), 371 } 372 } 373 374 /// Returns whether or not this memory needs initialization. It 375 /// may not if it already has initial content thanks to a CoW 376 /// mechanism. 377 pub(crate) fn needs_init(&self) -> bool { 378 match self { 379 Memory::Local(mem) => mem.needs_init(), 380 Memory::Shared(mem) => mem.needs_init(), 381 } 382 } 383 384 /// Grow memory by the specified amount of wasm pages. 385 /// 386 /// Returns `None` if memory can't be grown by the specified amount 387 /// of wasm pages. Returns `Some` with the old size of memory, in bytes, on 388 /// successful growth. 389 /// 390 /// # Safety 391 /// 392 /// Resizing the memory can reallocate the memory buffer for dynamic memories. 393 /// An instance's `VMContext` may have pointers to the memory's base and will 394 /// need to be fixed up after growing the memory. 395 /// 396 /// Generally, prefer using `InstanceHandle::memory_grow`, which encapsulates 397 /// this unsafety. 398 /// 399 /// Ensure that the provided Store is not used to get access any Memory 400 /// which lives inside it. 401 pub async unsafe fn grow( 402 &mut self, 403 delta_pages: u64, 404 limiter: Option<&mut StoreResourceLimiter<'_>>, 405 ) -> Result<Option<usize>, Error> { 406 let result = match self { 407 Memory::Local(mem) => mem.grow(delta_pages, limiter).await?, 408 Memory::Shared(mem) => mem.grow(delta_pages)?, 409 }; 410 match result { 411 Some((old, _new)) => Ok(Some(old)), 412 None => Ok(None), 413 } 414 } 415 416 /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. 417 pub fn vmmemory(&self) -> VMMemoryDefinition { 418 match self { 419 Memory::Local(mem) => mem.vmmemory(), 420 // `vmmemory()` is used for writing the `VMMemoryDefinition` of a 421 // memory into its `VMContext`; this should never be possible for a 422 // shared memory because the only `VMMemoryDefinition` for it should 423 // be stored in its own `def` field. 424 Memory::Shared(_) => unreachable!(), 425 } 426 } 427 428 /// Consume the memory, returning its [`MemoryImageSlot`] if any is present. 429 /// The image should only be present for a subset of memories created with 430 /// [`Memory::new_static()`]. 431 #[cfg(feature = "pooling-allocator")] 432 pub fn unwrap_static_image(self) -> MemoryImageSlot { 433 match self { 434 Memory::Local(mem) => mem.unwrap_static_image(), 435 Memory::Shared(_) => panic!("expected a local memory"), 436 } 437 } 438 439 /// Is this a shared memory? 440 pub fn is_shared_memory(&self) -> bool { 441 matches!(self, Memory::Shared(_)) 442 } 443 444 /// If the [Memory] is a [SharedMemory], unwrap it and return a clone to 445 /// that shared memory. 446 pub fn as_shared_memory(&self) -> Option<&SharedMemory> { 447 match self { 448 Memory::Local(_) => None, 449 Memory::Shared(mem) => Some(mem), 450 } 451 } 452 453 /// Implementation of `memory.atomic.notify` for all memories. 454 #[cfg(feature = "threads")] 455 pub fn atomic_notify(&mut self, addr: u64, count: u32) -> Result<u32, Trap> { 456 match self.as_shared_memory() { 457 Some(m) => m.atomic_notify(addr, count), 458 None => { 459 validate_atomic_addr(&self.vmmemory(), addr, 4, 4)?; 460 Ok(0) 461 } 462 } 463 } 464 465 /// Implementation of `memory.atomic.wait32` for all memories. 466 #[cfg(feature = "threads")] 467 pub fn atomic_wait32( 468 &mut self, 469 addr: u64, 470 expected: u32, 471 timeout: Option<core::time::Duration>, 472 ) -> Result<crate::WaitResult, Trap> { 473 match self.as_shared_memory() { 474 Some(m) => m.atomic_wait32(addr, expected, timeout), 475 None => { 476 validate_atomic_addr(&self.vmmemory(), addr, 4, 4)?; 477 Err(Trap::AtomicWaitNonSharedMemory) 478 } 479 } 480 } 481 482 /// Implementation of `memory.atomic.wait64` for all memories. 483 #[cfg(feature = "threads")] 484 pub fn atomic_wait64( 485 &mut self, 486 addr: u64, 487 expected: u64, 488 timeout: Option<core::time::Duration>, 489 ) -> Result<crate::WaitResult, Trap> { 490 match self.as_shared_memory() { 491 Some(m) => m.atomic_wait64(addr, expected, timeout), 492 None => { 493 validate_atomic_addr(&self.vmmemory(), addr, 8, 8)?; 494 Err(Trap::AtomicWaitNonSharedMemory) 495 } 496 } 497 } 498 499 /// Returns the range of bytes that WebAssembly should be able to address in 500 /// this linear memory. Note that this includes guard pages which wasm can 501 /// hit. 502 pub fn wasm_accessible(&self) -> Range<usize> { 503 match self { 504 Memory::Local(mem) => mem.wasm_accessible(), 505 Memory::Shared(mem) => mem.wasm_accessible(), 506 } 507 } 508 } 509 510 /// An owned allocation of a wasm linear memory. 511 /// 512 /// This might be part of a `Memory` via `Memory::Local` but it might also be 513 /// the implementation basis for a `SharedMemory` behind an `RwLock` for 514 /// example. 515 pub struct LocalMemory { 516 alloc: Box<dyn RuntimeLinearMemory>, 517 ty: wasmtime_environ::Memory, 518 memory_may_move: bool, 519 memory_guard_size: usize, 520 memory_reservation: usize, 521 522 /// An optional CoW mapping that provides the initial content of this 523 /// memory. 524 memory_image: Option<MemoryImageSlot>, 525 } 526 527 impl LocalMemory { 528 pub fn new( 529 ty: &wasmtime_environ::Memory, 530 tunables: &Tunables, 531 alloc: Box<dyn RuntimeLinearMemory>, 532 memory_image: Option<&Arc<MemoryImage>>, 533 ) -> Result<LocalMemory> { 534 // If a memory image was specified, try to create the MemoryImageSlot on 535 // top of our mmap. 536 let memory_image = match memory_image { 537 #[cfg(has_virtual_memory)] 538 Some(image) => { 539 // We currently don't support memory_image if 540 // `RuntimeLinearMemory::byte_size` is not a multiple of the host page 541 // size. See https://github.com/bytecodealliance/wasmtime/issues/9660. 542 if let Ok(byte_size) = HostAlignedByteCount::new(alloc.byte_size()) { 543 // memory_image is CoW-based so it is expected to be backed 544 // by an mmap. 545 let mmap_base = match alloc.base() { 546 MemoryBase::Mmap(offset) => offset, 547 MemoryBase::Raw { .. } => { 548 unreachable!("memory_image is Some only for mmap-based memories") 549 } 550 }; 551 552 let mut slot = 553 MemoryImageSlot::create(mmap_base, byte_size, alloc.byte_capacity()); 554 // On drop, we will unmap our mmap'd range that this slot 555 // was mapped on top of, so there is no need for the slot to 556 // wipe it with an anonymous mapping first. 557 // 558 // Note that this code would be incorrect if clear-on-drop 559 // were enabled. That's because: 560 // 561 // * In the struct definition, `memory_image` above is listed 562 // after `alloc`. 563 // * Rust drops fields in the order they're defined, so 564 // `memory_image` would be dropped after `alloc`. 565 // * `alloc` can represent either owned memory (i.e. the mmap is 566 // freed on drop) or logically borrowed memory (something else 567 // manages the mmap). 568 // * If `alloc` is borrowed memory, then this isn't an issue. 569 // * But if `alloc` is owned memory, then it would first drop 570 // the mmap, and then `memory_image` would try to remap 571 // part of that same memory as part of clear-on-drop. 572 // 573 // A lot of this really suggests representing the ownership 574 // via Rust lifetimes -- that would be a major refactor, 575 // though. 576 slot.no_clear_on_drop(); 577 slot.instantiate(alloc.byte_size(), Some(image), ty, tunables)?; 578 Some(slot) 579 } else { 580 None 581 } 582 } 583 #[cfg(not(has_virtual_memory))] 584 Some(_) => unreachable!(), 585 None => None, 586 }; 587 Ok(LocalMemory { 588 ty: *ty, 589 alloc, 590 memory_may_move: ty.memory_may_move(tunables), 591 memory_image, 592 memory_guard_size: tunables.memory_guard_size.try_into().unwrap(), 593 memory_reservation: tunables.memory_reservation.try_into().unwrap(), 594 }) 595 } 596 597 pub fn page_size(&self) -> u64 { 598 self.ty.page_size() 599 } 600 601 /// Grows a memory by `delta_pages`. 602 /// 603 /// This performs the necessary checks on the growth before delegating to 604 /// the underlying `grow_to` implementation. 605 /// 606 /// The `store` is used only for error reporting. 607 pub async fn grow( 608 &mut self, 609 delta_pages: u64, 610 mut limiter: Option<&mut StoreResourceLimiter<'_>>, 611 ) -> Result<Option<(usize, usize)>, Error> { 612 let old_byte_size = self.alloc.byte_size(); 613 614 // Wasm spec: when growing by 0 pages, always return the current size. 615 if delta_pages == 0 { 616 return Ok(Some((old_byte_size, old_byte_size))); 617 } 618 619 let page_size = usize::try_from(self.page_size()).unwrap(); 620 621 // The largest wasm-page-aligned region of memory is possible to 622 // represent in a `usize`. This will be impossible for the system to 623 // actually allocate. 624 let absolute_max = 0usize.wrapping_sub(page_size); 625 626 // Calculate the byte size of the new allocation. Let it overflow up to 627 // `usize::MAX`, then clamp it down to `absolute_max`. 628 let new_byte_size = usize::try_from(delta_pages) 629 .unwrap_or(usize::MAX) 630 .saturating_mul(page_size) 631 .saturating_add(old_byte_size) 632 .min(absolute_max); 633 634 let maximum = self 635 .ty 636 .maximum_byte_size() 637 .ok() 638 .and_then(|n| usize::try_from(n).ok()); 639 640 // Store limiter gets first chance to reject memory_growing. 641 if let Some(limiter) = &mut limiter { 642 if !limiter 643 .memory_growing(old_byte_size, new_byte_size, maximum) 644 .await? 645 { 646 return Ok(None); 647 } 648 } 649 650 // Save the original base pointer to assert the invariant that growth up 651 // to the byte capacity never relocates the base pointer. 652 let base_ptr_before = self.alloc.base().as_mut_ptr(); 653 let required_to_not_move_memory = new_byte_size <= self.alloc.byte_capacity(); 654 655 let result = (|| -> Result<()> { 656 // Never exceed maximum, even if limiter permitted it. 657 if let Some(max) = maximum { 658 if new_byte_size > max { 659 bail!("Memory maximum size exceeded"); 660 } 661 } 662 663 // If memory isn't allowed to move then don't let growth happen 664 // beyond the initial capacity 665 if !self.memory_may_move && new_byte_size > self.alloc.byte_capacity() { 666 bail!("Memory maximum size exceeded"); 667 } 668 669 // If we have a CoW image overlay then let it manage accessible 670 // bytes. Once the heap limit is modified inform the underlying 671 // allocation that the size has changed. 672 // 673 // If the growth is going beyond the size of the heap image then 674 // discard it. This should only happen for `MmapMemory` where 675 // `no_clear_on_drop` is set so the destructor doesn't do anything. 676 // For now be maximally sure about this by asserting that memory can 677 // indeed move and that we're on unix. If this wants to run 678 // somewhere else like Windows or with other allocations this may 679 // need adjusting. 680 if let Some(image) = &mut self.memory_image { 681 if new_byte_size <= self.alloc.byte_capacity() { 682 image.set_heap_limit(new_byte_size)?; 683 self.alloc.set_byte_size(new_byte_size); 684 return Ok(()); 685 } 686 assert!(cfg!(unix)); 687 assert!(self.memory_may_move); 688 self.memory_image = None; 689 } 690 691 // And failing all that fall back to the underlying allocation to 692 // grow it. 693 self.alloc.grow_to(new_byte_size) 694 })(); 695 696 match result { 697 Ok(()) => { 698 // On successful growth double-check that the base pointer 699 // didn't move if it shouldn't have. 700 if required_to_not_move_memory { 701 assert_eq!(base_ptr_before, self.alloc.base().as_mut_ptr()); 702 } 703 704 Ok(Some((old_byte_size, new_byte_size))) 705 } 706 Err(e) => { 707 // FIXME: shared memories may not have an associated store to 708 // report the growth failure to but the error should not be 709 // dropped 710 // (https://github.com/bytecodealliance/wasmtime/issues/4240). 711 if let Some(limiter) = limiter { 712 limiter.memory_grow_failed(e)?; 713 } 714 Ok(None) 715 } 716 } 717 } 718 719 pub fn vmmemory(&self) -> VMMemoryDefinition { 720 self.alloc.vmmemory() 721 } 722 723 pub fn byte_size(&self) -> usize { 724 self.alloc.byte_size() 725 } 726 727 pub fn needs_init(&self) -> bool { 728 match &self.memory_image { 729 Some(image) => !image.has_image(), 730 None => true, 731 } 732 } 733 734 pub fn wasm_accessible(&self) -> Range<usize> { 735 let base = self.alloc.base().as_mut_ptr() as usize; 736 // From the base add: 737 // 738 // * max(capacity, reservation) -- all memory is guaranteed to have at 739 // least `memory_reservation`, but capacity may go beyond that. 740 // * memory_guard_size - wasm is allowed to hit the guard page for 741 // sigsegv for example. 742 // 743 // and this computes the range that wasm is allowed to load from and 744 // deterministically trap or succeed. 745 let end = 746 base + self.alloc.byte_capacity().max(self.memory_reservation) + self.memory_guard_size; 747 base..end 748 } 749 750 #[cfg(feature = "pooling-allocator")] 751 pub fn unwrap_static_image(self) -> MemoryImageSlot { 752 self.memory_image.unwrap() 753 } 754 } 755 756 /// In the configurations where bounds checks were elided in JIT code (because 757 /// we are using static memories with virtual memory guard pages) this manual 758 /// check is here so we don't segfault from Rust. For other configurations, 759 /// these checks are required anyways. 760 #[cfg(feature = "threads")] 761 pub fn validate_atomic_addr( 762 def: &VMMemoryDefinition, 763 addr: u64, 764 access_size: u64, 765 access_alignment: u64, 766 ) -> Result<*mut u8, Trap> { 767 debug_assert!(access_alignment.is_power_of_two()); 768 if !(addr % access_alignment == 0) { 769 return Err(Trap::HeapMisaligned); 770 } 771 772 let length = u64::try_from(def.current_length()).unwrap(); 773 if !(addr.saturating_add(access_size) < length) { 774 return Err(Trap::MemoryOutOfBounds); 775 } 776 777 let addr = usize::try_from(addr).unwrap(); 778 Ok(def.base.as_ptr().wrapping_add(addr)) 779 } 780