1 //! Memory management for linear memories. 2 //! 3 //! `RuntimeLinearMemory` is to WebAssembly linear memories what `Table` is to WebAssembly tables. 4 5 use crate::prelude::*; 6 use crate::runtime::vm::mmap::Mmap; 7 use crate::runtime::vm::vmcontext::VMMemoryDefinition; 8 use crate::runtime::vm::{ 9 round_usize_up_to_host_pages, usize_is_multiple_of_host_page_size, MemoryImage, 10 MemoryImageSlot, SendSyncPtr, SharedMemory, Store, WaitResult, 11 }; 12 use alloc::sync::Arc; 13 use core::ops::Range; 14 use core::ptr::NonNull; 15 use core::time::Duration; 16 use wasmtime_environ::{MemoryPlan, MemoryStyle, Trap}; 17 18 /// A memory allocator 19 pub trait RuntimeMemoryCreator: Send + Sync { 20 /// Create new RuntimeLinearMemory 21 fn new_memory( 22 &self, 23 plan: &MemoryPlan, 24 minimum: usize, 25 maximum: Option<usize>, 26 // Optionally, a memory image for CoW backing. 27 memory_image: Option<&Arc<MemoryImage>>, 28 ) -> Result<Box<dyn RuntimeLinearMemory>>; 29 } 30 31 /// A default memory allocator used by Wasmtime 32 pub struct DefaultMemoryCreator; 33 34 impl RuntimeMemoryCreator for DefaultMemoryCreator { 35 /// Create new MmapMemory 36 fn new_memory( 37 &self, 38 plan: &MemoryPlan, 39 minimum: usize, 40 maximum: Option<usize>, 41 memory_image: Option<&Arc<MemoryImage>>, 42 ) -> Result<Box<dyn RuntimeLinearMemory>> { 43 Ok(Box::new(MmapMemory::new( 44 plan, 45 minimum, 46 maximum, 47 memory_image, 48 )?)) 49 } 50 } 51 52 /// A linear memory's backing storage. 53 /// 54 /// This does not a full Wasm linear memory, as it may 55 pub trait RuntimeLinearMemory: Send + Sync { 56 /// Returns the log2 of this memory's page size, in bytes. 57 fn page_size_log2(&self) -> u8; 58 59 /// Returns this memory's page size, in bytes. 60 fn page_size(&self) -> u64 { 61 let log2 = self.page_size_log2(); 62 debug_assert!(log2 == 16 || log2 == 0); 63 1 << self.page_size_log2() 64 } 65 66 /// Returns the number of allocated bytes. 67 fn byte_size(&self) -> usize; 68 69 /// Returns the maximum number of bytes the memory can grow to. 70 /// Returns `None` if the memory is unbounded. 71 fn maximum_byte_size(&self) -> Option<usize>; 72 73 /// Grows a memory by `delta_pages`. 74 /// 75 /// This performs the necessary checks on the growth before delegating to 76 /// the underlying `grow_to` implementation. A default implementation of 77 /// this memory is provided here since this is assumed to be the same for 78 /// most kinds of memory; one exception is shared memory, which must perform 79 /// all the steps of the default implementation *plus* the required locking. 80 /// 81 /// The `store` is used only for error reporting. 82 fn grow( 83 &mut self, 84 delta_pages: u64, 85 mut store: Option<&mut dyn Store>, 86 ) -> Result<Option<(usize, usize)>, Error> { 87 let old_byte_size = self.byte_size(); 88 89 // Wasm spec: when growing by 0 pages, always return the current size. 90 if delta_pages == 0 { 91 return Ok(Some((old_byte_size, old_byte_size))); 92 } 93 94 let page_size = usize::try_from(self.page_size()).unwrap(); 95 96 // The largest wasm-page-aligned region of memory is possible to 97 // represent in a `usize`. This will be impossible for the system to 98 // actually allocate. 99 let absolute_max = 0usize.wrapping_sub(page_size); 100 101 // Calculate the byte size of the new allocation. Let it overflow up to 102 // `usize::MAX`, then clamp it down to `absolute_max`. 103 let new_byte_size = usize::try_from(delta_pages) 104 .unwrap_or(usize::MAX) 105 .saturating_mul(page_size) 106 .saturating_add(old_byte_size) 107 .min(absolute_max); 108 109 let maximum = self.maximum_byte_size(); 110 111 // Store limiter gets first chance to reject memory_growing. 112 if let Some(store) = &mut store { 113 if !store.memory_growing(old_byte_size, new_byte_size, maximum)? { 114 return Ok(None); 115 } 116 } 117 118 // Never exceed maximum, even if limiter permitted it. 119 if let Some(max) = maximum { 120 if new_byte_size > max { 121 if let Some(store) = store { 122 // FIXME: shared memories may not have an associated store 123 // to report the growth failure to but the error should not 124 // be dropped 125 // (https://github.com/bytecodealliance/wasmtime/issues/4240). 126 store.memory_grow_failed(format_err!("Memory maximum size exceeded"))?; 127 } 128 return Ok(None); 129 } 130 } 131 132 match self.grow_to(new_byte_size) { 133 Ok(_) => Ok(Some((old_byte_size, new_byte_size))), 134 Err(e) => { 135 // FIXME: shared memories may not have an associated store to 136 // report the growth failure to but the error should not be 137 // dropped 138 // (https://github.com/bytecodealliance/wasmtime/issues/4240). 139 if let Some(store) = store { 140 store.memory_grow_failed(e)?; 141 } 142 Ok(None) 143 } 144 } 145 } 146 147 /// Grow memory to the specified amount of bytes. 148 /// 149 /// Returns an error if memory can't be grown by the specified amount 150 /// of bytes. 151 fn grow_to(&mut self, size: usize) -> Result<()>; 152 153 /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm 154 /// code. 155 fn vmmemory(&mut self) -> VMMemoryDefinition; 156 157 /// Does this memory need initialization? It may not if it already 158 /// has initial contents courtesy of the `MemoryImage` passed to 159 /// `RuntimeMemoryCreator::new_memory()`. 160 fn needs_init(&self) -> bool; 161 162 /// Used for optional dynamic downcasting. 163 fn as_any_mut(&mut self) -> &mut dyn core::any::Any; 164 165 /// Returns the range of addresses that may be reached by WebAssembly. 166 /// 167 /// This starts at the base of linear memory and ends at the end of the 168 /// guard pages, if any. 169 fn wasm_accessible(&self) -> Range<usize>; 170 } 171 172 /// A linear memory instance. 173 #[derive(Debug)] 174 pub struct MmapMemory { 175 // The underlying allocation. 176 mmap: Mmap, 177 178 // The current length of this Wasm memory, in bytes. 179 // 180 // This region starts at `pre_guard_size` offset from the base of `mmap`. It 181 // is always accessible, which means that if the Wasm page size is smaller 182 // than the host page size, there may be some trailing region in the `mmap` 183 // that is accessible but should not be accessed. (We rely on explicit 184 // bounds checks in the compiled code to protect this region.) 185 len: usize, 186 187 // The optional maximum accessible size, in bytes, for this linear memory. 188 // 189 // Note that this maximum does not factor in guard pages, so this isn't the 190 // maximum size of the linear address space reservation for this memory. 191 // 192 // This is *not* always a multiple of the host page size, and 193 // `self.accessible()` may go past `self.maximum` when Wasm is using a small 194 // custom page size due to `self.accessible()`'s rounding up to the host 195 // page size. 196 maximum: Option<usize>, 197 198 // The log2 of this Wasm memory's page size, in bytes. 199 page_size_log2: u8, 200 201 // The amount of extra bytes to reserve whenever memory grows. This is 202 // specified so that the cost of repeated growth is amortized. 203 extra_to_reserve_on_growth: usize, 204 205 // Size in bytes of extra guard pages before the start and after the end to 206 // optimize loads and stores with constant offsets. 207 pre_guard_size: usize, 208 offset_guard_size: usize, 209 210 // An optional CoW mapping that provides the initial content of this 211 // MmapMemory, if mapped. 212 memory_image: Option<MemoryImageSlot>, 213 } 214 215 impl MmapMemory { 216 /// Create a new linear memory instance with specified minimum and maximum 217 /// number of wasm pages. 218 pub fn new( 219 plan: &MemoryPlan, 220 minimum: usize, 221 mut maximum: Option<usize>, 222 memory_image: Option<&Arc<MemoryImage>>, 223 ) -> Result<Self> { 224 // It's a programmer error for these two configuration values to exceed 225 // the host available address space, so panic if such a configuration is 226 // found (mostly an issue for hypothetical 32-bit hosts). 227 let offset_guard_bytes = usize::try_from(plan.offset_guard_size).unwrap(); 228 let pre_guard_bytes = usize::try_from(plan.pre_guard_size).unwrap(); 229 230 // Ensure that our guard regions are multiples of the host page size. 231 let offset_guard_bytes = round_usize_up_to_host_pages(offset_guard_bytes)?; 232 let pre_guard_bytes = round_usize_up_to_host_pages(pre_guard_bytes)?; 233 234 let (alloc_bytes, extra_to_reserve_on_growth) = match plan.style { 235 // Dynamic memories start with the minimum size plus the `reserve` 236 // amount specified to grow into. 237 MemoryStyle::Dynamic { reserve } => ( 238 round_usize_up_to_host_pages(minimum)?, 239 round_usize_up_to_host_pages(usize::try_from(reserve).unwrap())?, 240 ), 241 242 // Static memories will never move in memory and consequently get 243 // their entire allocation up-front with no extra room to grow into. 244 // Note that the `maximum` is adjusted here to whatever the smaller 245 // of the two is, the `maximum` given or the `bound` specified for 246 // this memory. 247 MemoryStyle::Static { byte_reservation } => { 248 assert!(byte_reservation >= plan.memory.minimum_byte_size().unwrap()); 249 let bound_bytes = usize::try_from(byte_reservation).unwrap(); 250 let bound_bytes = round_usize_up_to_host_pages(bound_bytes)?; 251 maximum = Some(bound_bytes.min(maximum.unwrap_or(usize::MAX))); 252 (bound_bytes, 0) 253 } 254 }; 255 assert!(usize_is_multiple_of_host_page_size(alloc_bytes)); 256 257 let request_bytes = pre_guard_bytes 258 .checked_add(alloc_bytes) 259 .and_then(|i| i.checked_add(extra_to_reserve_on_growth)) 260 .and_then(|i| i.checked_add(offset_guard_bytes)) 261 .ok_or_else(|| format_err!("cannot allocate {} with guard regions", minimum))?; 262 assert!(usize_is_multiple_of_host_page_size(request_bytes)); 263 264 let mut mmap = Mmap::accessible_reserved(0, request_bytes)?; 265 266 if minimum > 0 { 267 let accessible = round_usize_up_to_host_pages(minimum)?; 268 mmap.make_accessible(pre_guard_bytes, accessible)?; 269 } 270 271 // If a memory image was specified, try to create the MemoryImageSlot on 272 // top of our mmap. 273 let memory_image = match memory_image { 274 Some(image) => { 275 let base = unsafe { mmap.as_mut_ptr().add(pre_guard_bytes) }; 276 let mut slot = MemoryImageSlot::create( 277 base.cast(), 278 minimum, 279 alloc_bytes + extra_to_reserve_on_growth, 280 ); 281 slot.instantiate(minimum, Some(image), &plan)?; 282 // On drop, we will unmap our mmap'd range that this slot was 283 // mapped on top of, so there is no need for the slot to wipe 284 // it with an anonymous mapping first. 285 slot.no_clear_on_drop(); 286 Some(slot) 287 } 288 None => None, 289 }; 290 291 Ok(Self { 292 mmap, 293 len: minimum, 294 maximum, 295 page_size_log2: plan.memory.page_size_log2, 296 pre_guard_size: pre_guard_bytes, 297 offset_guard_size: offset_guard_bytes, 298 extra_to_reserve_on_growth, 299 memory_image, 300 }) 301 } 302 303 /// Get the length of the accessible portion of the underlying `mmap`. This 304 /// is the same region as `self.len` but rounded up to a multiple of the 305 /// host page size. 306 fn accessible(&self) -> usize { 307 let accessible = 308 round_usize_up_to_host_pages(self.len).expect("accessible region always fits in usize"); 309 debug_assert!(accessible <= self.mmap.len() - self.offset_guard_size - self.pre_guard_size); 310 accessible 311 } 312 } 313 314 impl RuntimeLinearMemory for MmapMemory { 315 fn page_size_log2(&self) -> u8 { 316 self.page_size_log2 317 } 318 319 fn byte_size(&self) -> usize { 320 self.len 321 } 322 323 fn maximum_byte_size(&self) -> Option<usize> { 324 self.maximum 325 } 326 327 fn grow_to(&mut self, new_size: usize) -> Result<()> { 328 assert!(usize_is_multiple_of_host_page_size(self.offset_guard_size)); 329 assert!(usize_is_multiple_of_host_page_size(self.pre_guard_size)); 330 assert!(usize_is_multiple_of_host_page_size(self.mmap.len())); 331 332 let new_accessible = round_usize_up_to_host_pages(new_size)?; 333 if new_accessible > self.mmap.len() - self.offset_guard_size - self.pre_guard_size { 334 // If the new size of this heap exceeds the current size of the 335 // allocation we have, then this must be a dynamic heap. Use 336 // `new_size` to calculate a new size of an allocation, allocate it, 337 // and then copy over the memory from before. 338 let request_bytes = self 339 .pre_guard_size 340 .checked_add(new_accessible) 341 .and_then(|s| s.checked_add(self.extra_to_reserve_on_growth)) 342 .and_then(|s| s.checked_add(self.offset_guard_size)) 343 .ok_or_else(|| format_err!("overflow calculating size of memory allocation"))?; 344 assert!(usize_is_multiple_of_host_page_size(request_bytes)); 345 346 let mut new_mmap = Mmap::accessible_reserved(0, request_bytes)?; 347 new_mmap.make_accessible(self.pre_guard_size, new_accessible)?; 348 349 // This method has an exclusive reference to `self.mmap` and just 350 // created `new_mmap` so it should be safe to acquire references 351 // into both of them and copy between them. 352 unsafe { 353 let range = self.pre_guard_size..self.pre_guard_size + self.len; 354 let src = self.mmap.slice(range.clone()); 355 let dst = new_mmap.slice_mut(range); 356 dst.copy_from_slice(src); 357 } 358 359 // Now drop the MemoryImageSlot, if any. We've lost the CoW 360 // advantages by explicitly copying all data, but we have 361 // preserved all of its content; so we no longer need the 362 // mapping. We need to do this before we (implicitly) drop the 363 // `mmap` field by overwriting it below. 364 drop(self.memory_image.take()); 365 366 self.mmap = new_mmap; 367 } else if let Some(image) = self.memory_image.as_mut() { 368 // MemoryImageSlot has its own growth mechanisms; defer to its 369 // implementation. 370 image.set_heap_limit(new_size)?; 371 } else { 372 // If the new size of this heap fits within the existing allocation 373 // then all we need to do is to make the new pages accessible. This 374 // can happen either for "static" heaps which always hit this case, 375 // or "dynamic" heaps which have some space reserved after the 376 // initial allocation to grow into before the heap is moved in 377 // memory. 378 assert!(new_size > self.len); 379 assert!(self.maximum.map_or(true, |max| new_size <= max)); 380 assert!(new_size <= self.mmap.len() - self.offset_guard_size - self.pre_guard_size); 381 382 let new_accessible = round_usize_up_to_host_pages(new_size)?; 383 assert!( 384 new_accessible <= self.mmap.len() - self.offset_guard_size - self.pre_guard_size, 385 ); 386 387 // If the Wasm memory's page size is smaller than the host's page 388 // size, then we might not need to actually change permissions, 389 // since we are forced to round our accessible range up to the 390 // host's page size. 391 if new_accessible > self.accessible() { 392 self.mmap.make_accessible( 393 self.pre_guard_size + self.accessible(), 394 new_accessible - self.accessible(), 395 )?; 396 } 397 } 398 399 self.len = new_size; 400 401 Ok(()) 402 } 403 404 fn vmmemory(&mut self) -> VMMemoryDefinition { 405 VMMemoryDefinition { 406 base: unsafe { self.mmap.as_mut_ptr().add(self.pre_guard_size) }, 407 current_length: self.len.into(), 408 } 409 } 410 411 fn needs_init(&self) -> bool { 412 // If we're using a CoW mapping, then no initialization 413 // is needed. 414 self.memory_image.is_none() 415 } 416 417 fn as_any_mut(&mut self) -> &mut dyn core::any::Any { 418 self 419 } 420 421 fn wasm_accessible(&self) -> Range<usize> { 422 let base = self.mmap.as_ptr() as usize + self.pre_guard_size; 423 let end = base + (self.mmap.len() - self.pre_guard_size); 424 base..end 425 } 426 } 427 428 /// A "static" memory where the lifetime of the backing memory is managed 429 /// elsewhere. Currently used with the pooling allocator. 430 struct StaticMemory { 431 /// The base pointer of this static memory, wrapped up in a send/sync 432 /// wrapper. 433 base: SendSyncPtr<u8>, 434 435 /// The byte capacity of the `base` pointer. 436 capacity: usize, 437 438 /// The current size, in bytes, of this memory. 439 size: usize, 440 441 /// The log2 of this memory's page size. 442 page_size_log2: u8, 443 444 /// The size, in bytes, of the virtual address allocation starting at `base` 445 /// and going to the end of the guard pages at the end of the linear memory. 446 memory_and_guard_size: usize, 447 448 /// The image management, if any, for this memory. Owned here and 449 /// returned to the pooling allocator when termination occurs. 450 memory_image: MemoryImageSlot, 451 } 452 453 impl StaticMemory { 454 fn new( 455 base_ptr: *mut u8, 456 base_capacity: usize, 457 initial_size: usize, 458 maximum_size: Option<usize>, 459 page_size_log2: u8, 460 memory_image: MemoryImageSlot, 461 memory_and_guard_size: usize, 462 ) -> Result<Self> { 463 if base_capacity < initial_size { 464 bail!( 465 "initial memory size of {} exceeds the pooling allocator's \ 466 configured maximum memory size of {} bytes", 467 initial_size, 468 base_capacity, 469 ); 470 } 471 472 // Only use the part of the slice that is necessary. 473 let base_capacity = match maximum_size { 474 Some(max) if max < base_capacity => max, 475 _ => base_capacity, 476 }; 477 478 Ok(Self { 479 base: SendSyncPtr::new(NonNull::new(base_ptr).unwrap()), 480 capacity: base_capacity, 481 size: initial_size, 482 page_size_log2, 483 memory_image, 484 memory_and_guard_size, 485 }) 486 } 487 } 488 489 impl RuntimeLinearMemory for StaticMemory { 490 fn page_size_log2(&self) -> u8 { 491 self.page_size_log2 492 } 493 494 fn byte_size(&self) -> usize { 495 self.size 496 } 497 498 fn maximum_byte_size(&self) -> Option<usize> { 499 Some(self.capacity) 500 } 501 502 fn grow_to(&mut self, new_byte_size: usize) -> Result<()> { 503 // Never exceed the static memory size; this check should have been made 504 // prior to arriving here. 505 assert!(new_byte_size <= self.capacity); 506 507 self.memory_image.set_heap_limit(new_byte_size)?; 508 509 // Update our accounting of the available size. 510 self.size = new_byte_size; 511 Ok(()) 512 } 513 514 fn vmmemory(&mut self) -> VMMemoryDefinition { 515 VMMemoryDefinition { 516 base: self.base.as_ptr(), 517 current_length: self.size.into(), 518 } 519 } 520 521 fn needs_init(&self) -> bool { 522 !self.memory_image.has_image() 523 } 524 525 fn as_any_mut(&mut self) -> &mut dyn core::any::Any { 526 self 527 } 528 529 fn wasm_accessible(&self) -> Range<usize> { 530 let base = self.base.as_ptr() as usize; 531 let end = base + self.memory_and_guard_size; 532 base..end 533 } 534 } 535 536 /// Representation of a runtime wasm linear memory. 537 pub struct Memory(pub(crate) Box<dyn RuntimeLinearMemory>); 538 539 impl Memory { 540 /// Create a new dynamic (movable) memory instance for the specified plan. 541 pub fn new_dynamic( 542 plan: &MemoryPlan, 543 creator: &dyn RuntimeMemoryCreator, 544 store: &mut dyn Store, 545 memory_image: Option<&Arc<MemoryImage>>, 546 ) -> Result<Self> { 547 let (minimum, maximum) = Self::limit_new(plan, Some(store))?; 548 let allocation = creator.new_memory(plan, minimum, maximum, memory_image)?; 549 let allocation = if plan.memory.shared { 550 Box::new(SharedMemory::wrap(plan, allocation, plan.memory)?) 551 } else { 552 allocation 553 }; 554 Ok(Memory(allocation)) 555 } 556 557 /// Create a new static (immovable) memory instance for the specified plan. 558 pub fn new_static( 559 plan: &MemoryPlan, 560 base_ptr: *mut u8, 561 base_capacity: usize, 562 memory_image: MemoryImageSlot, 563 memory_and_guard_size: usize, 564 store: &mut dyn Store, 565 ) -> Result<Self> { 566 let (minimum, maximum) = Self::limit_new(plan, Some(store))?; 567 let pooled_memory = StaticMemory::new( 568 base_ptr, 569 base_capacity, 570 minimum, 571 maximum, 572 plan.memory.page_size_log2, 573 memory_image, 574 memory_and_guard_size, 575 )?; 576 let allocation = Box::new(pooled_memory); 577 let allocation: Box<dyn RuntimeLinearMemory> = if plan.memory.shared { 578 // FIXME: since the pooling allocator owns the memory allocation 579 // (which is torn down with the instance), the current shared memory 580 // implementation will cause problems; see 581 // https://github.com/bytecodealliance/wasmtime/issues/4244. 582 todo!("using shared memory with the pooling allocator is a work in progress"); 583 } else { 584 allocation 585 }; 586 Ok(Memory(allocation)) 587 } 588 589 /// Calls the `store`'s limiter to optionally prevent a memory from being allocated. 590 /// 591 /// Returns a tuple of the minimum size, optional maximum size, and log(page 592 /// size) of the memory, all in bytes. 593 pub(crate) fn limit_new( 594 plan: &MemoryPlan, 595 store: Option<&mut dyn Store>, 596 ) -> Result<(usize, Option<usize>)> { 597 let page_size = usize::try_from(plan.memory.page_size()).unwrap(); 598 599 // This is the absolute possible maximum that the module can try to 600 // allocate, which is our entire address space minus a wasm page. That 601 // shouldn't ever actually work in terms of an allocation because 602 // presumably the kernel wants *something* for itself, but this is used 603 // to pass to the `store`'s limiter for a requested size 604 // to approximate the scale of the request that the wasm module is 605 // making. This is necessary because the limiter works on `usize` bytes 606 // whereas we're working with possibly-overflowing `u64` calculations 607 // here. To actually faithfully represent the byte requests of modules 608 // we'd have to represent things as `u128`, but that's kinda 609 // overkill for this purpose. 610 let absolute_max = 0usize.wrapping_sub(page_size); 611 612 // Sanity-check what should already be true from wasm module validation. 613 if let Ok(size) = plan.memory.minimum_byte_size() { 614 assert!(size <= u64::try_from(absolute_max).unwrap()); 615 } 616 if let Ok(max) = plan.memory.maximum_byte_size() { 617 assert!(max <= u64::try_from(absolute_max).unwrap()); 618 } 619 620 // If the minimum memory size overflows the size of our own address 621 // space, then we can't satisfy this request, but defer the error to 622 // later so the `store` can be informed that an effective oom is 623 // happening. 624 let minimum = plan 625 .memory 626 .minimum_byte_size() 627 .ok() 628 .and_then(|m| usize::try_from(m).ok()); 629 630 // The plan stores the maximum size in units of wasm pages, but we 631 // use units of bytes. Unlike for the `minimum` size we silently clamp 632 // the effective maximum size to the limits of what we can track. If the 633 // maximum size exceeds `usize` or `u64` then there's no need to further 634 // keep track of it as some sort of runtime limit will kick in long 635 // before we reach the statically declared maximum size. 636 let maximum = plan 637 .memory 638 .maximum_byte_size() 639 .ok() 640 .and_then(|m| usize::try_from(m).ok()); 641 642 // Inform the store's limiter what's about to happen. This will let the 643 // limiter reject anything if necessary, and this also guarantees that 644 // we should call the limiter for all requested memories, even if our 645 // `minimum` calculation overflowed. This means that the `minimum` we're 646 // informing the limiter is lossy and may not be 100% accurate, but for 647 // now the expected uses of limiter means that's ok. 648 if let Some(store) = store { 649 // We ignore the store limits for shared memories since they are 650 // technically not created within a store (though, trickily, they 651 // may be associated with one in order to get a `vmctx`). 652 if !plan.memory.shared { 653 if !store.memory_growing(0, minimum.unwrap_or(absolute_max), maximum)? { 654 bail!( 655 "memory minimum size of {} pages exceeds memory limits", 656 plan.memory.minimum 657 ); 658 } 659 } 660 } 661 662 // At this point we need to actually handle overflows, so bail out with 663 // an error if we made it this far. 664 let minimum = minimum.ok_or_else(|| { 665 format_err!( 666 "memory minimum size of {} pages exceeds memory limits", 667 plan.memory.minimum 668 ) 669 })?; 670 671 Ok((minimum, maximum)) 672 } 673 674 /// Returns this memory's page size, in bytes. 675 pub fn page_size(&self) -> u64 { 676 self.0.page_size() 677 } 678 679 /// Returns the number of allocated wasm pages. 680 pub fn byte_size(&self) -> usize { 681 self.0.byte_size() 682 } 683 684 /// Returns whether or not this memory needs initialization. It 685 /// may not if it already has initial content thanks to a CoW 686 /// mechanism. 687 pub(crate) fn needs_init(&self) -> bool { 688 self.0.needs_init() 689 } 690 691 /// Grow memory by the specified amount of wasm pages. 692 /// 693 /// Returns `None` if memory can't be grown by the specified amount 694 /// of wasm pages. Returns `Some` with the old size of memory, in bytes, on 695 /// successful growth. 696 /// 697 /// # Safety 698 /// 699 /// Resizing the memory can reallocate the memory buffer for dynamic memories. 700 /// An instance's `VMContext` may have pointers to the memory's base and will 701 /// need to be fixed up after growing the memory. 702 /// 703 /// Generally, prefer using `InstanceHandle::memory_grow`, which encapsulates 704 /// this unsafety. 705 /// 706 /// Ensure that the provided Store is not used to get access any Memory 707 /// which lives inside it. 708 pub unsafe fn grow( 709 &mut self, 710 delta_pages: u64, 711 store: Option<&mut dyn Store>, 712 ) -> Result<Option<usize>, Error> { 713 self.0 714 .grow(delta_pages, store) 715 .map(|opt| opt.map(|(old, _new)| old)) 716 } 717 718 /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. 719 pub fn vmmemory(&mut self) -> VMMemoryDefinition { 720 self.0.vmmemory() 721 } 722 723 /// Consume the memory, returning its [`MemoryImageSlot`] if any is present. 724 /// The image should only be present for a subset of memories created with 725 /// [`Memory::new_static()`]. 726 #[cfg(feature = "pooling-allocator")] 727 pub fn unwrap_static_image(mut self) -> MemoryImageSlot { 728 let mem = self.0.as_any_mut().downcast_mut::<StaticMemory>().unwrap(); 729 core::mem::replace(&mut mem.memory_image, MemoryImageSlot::dummy()) 730 } 731 732 /// If the [Memory] is a [SharedMemory], unwrap it and return a clone to 733 /// that shared memory. 734 pub fn as_shared_memory(&mut self) -> Option<&mut SharedMemory> { 735 let as_any = self.0.as_any_mut(); 736 if let Some(m) = as_any.downcast_mut::<SharedMemory>() { 737 Some(m) 738 } else { 739 None 740 } 741 } 742 743 /// Implementation of `memory.atomic.notify` for all memories. 744 pub fn atomic_notify(&mut self, addr: u64, count: u32) -> Result<u32, Trap> { 745 match self.0.as_any_mut().downcast_mut::<SharedMemory>() { 746 Some(m) => m.atomic_notify(addr, count), 747 None => { 748 validate_atomic_addr(&self.vmmemory(), addr, 4, 4)?; 749 Ok(0) 750 } 751 } 752 } 753 754 /// Implementation of `memory.atomic.wait32` for all memories. 755 pub fn atomic_wait32( 756 &mut self, 757 addr: u64, 758 expected: u32, 759 timeout: Option<Duration>, 760 ) -> Result<WaitResult, Trap> { 761 match self.0.as_any_mut().downcast_mut::<SharedMemory>() { 762 Some(m) => m.atomic_wait32(addr, expected, timeout), 763 None => { 764 validate_atomic_addr(&self.vmmemory(), addr, 4, 4)?; 765 Err(Trap::AtomicWaitNonSharedMemory) 766 } 767 } 768 } 769 770 /// Implementation of `memory.atomic.wait64` for all memories. 771 pub fn atomic_wait64( 772 &mut self, 773 addr: u64, 774 expected: u64, 775 timeout: Option<Duration>, 776 ) -> Result<WaitResult, Trap> { 777 match self.0.as_any_mut().downcast_mut::<SharedMemory>() { 778 Some(m) => m.atomic_wait64(addr, expected, timeout), 779 None => { 780 validate_atomic_addr(&self.vmmemory(), addr, 8, 8)?; 781 Err(Trap::AtomicWaitNonSharedMemory) 782 } 783 } 784 } 785 786 /// Returns the range of bytes that WebAssembly should be able to address in 787 /// this linear memory. Note that this includes guard pages which wasm can 788 /// hit. 789 pub fn wasm_accessible(&self) -> Range<usize> { 790 self.0.wasm_accessible() 791 } 792 } 793 794 /// In the configurations where bounds checks were elided in JIT code (because 795 /// we are using static memories with virtual memory guard pages) this manual 796 /// check is here so we don't segfault from Rust. For other configurations, 797 /// these checks are required anyways. 798 pub fn validate_atomic_addr( 799 def: &VMMemoryDefinition, 800 addr: u64, 801 access_size: u64, 802 access_alignment: u64, 803 ) -> Result<*mut u8, Trap> { 804 debug_assert!(access_alignment.is_power_of_two()); 805 if !(addr % access_alignment == 0) { 806 return Err(Trap::HeapMisaligned); 807 } 808 809 let length = u64::try_from(def.current_length()).unwrap(); 810 if !(addr.saturating_add(access_size) < length) { 811 return Err(Trap::MemoryOutOfBounds); 812 } 813 814 Ok(def.base.wrapping_add(addr as usize)) 815 } 816