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