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