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