1use super::sys::DecommitBehavior;
5use crate::Engine;
6use crate::prelude::*;
7use crate::runtime::vm::sys::vm::{self, MemoryImageSource, PageMap, reset_with_pagemap};
8use crate::runtime::vm::{
9 HostAlignedByteCount, MmapOffset, ModuleMemoryImageSource, host_page_size,
10};
11use alloc::sync::Arc;
12use core::fmt;
13use core::ops::Range;
14use wasmtime_environ::prelude::TryPrimaryMap;
15use wasmtime_environ::{DefinedMemoryIndex, MemoryInitialization, MemoryTunables, Module};
16
17pub struct ModuleMemoryImages {
22 memories: TryPrimaryMap<DefinedMemoryIndex, Option<Arc<MemoryImage>>>,
23}
24
25impl ModuleMemoryImages {
26 pub fn get_memory_image(&self, defined_index: DefinedMemoryIndex) -> Option<&Arc<MemoryImage>> {
28 self.memories[defined_index].as_ref()
29 }
30}
31
32pub struct MemoryImage {
34 source: MemoryImageSource,
40
41 len: HostAlignedByteCount,
48
49 source_offset: u64,
61
62 linear_memory_offset: HostAlignedByteCount,
66
67 module_source: Arc<dyn ModuleMemoryImageSource>,
69
70 module_source_offset: usize,
73}
74
75impl MemoryImage {
76 fn new(
77 engine: &Engine,
78 page_size: u32,
79 linear_memory_offset: HostAlignedByteCount,
80 module_source: &Arc<impl ModuleMemoryImageSource>,
81 data_range: Range<usize>,
82 ) -> Result<Option<MemoryImage>> {
83 let assert_page_aligned = |val: usize| {
84 assert_eq!(val % (page_size as usize), 0);
85 };
86 let len =
88 HostAlignedByteCount::new(data_range.len()).expect("memory image data is page-aligned");
89
90 let data = &module_source.wasm_data()[data_range.clone()];
105 if !engine.config().force_memory_init_memfd {
106 if let Some(mmap) = module_source.mmap() {
107 let start = mmap.as_ptr() as usize;
108 let end = start + mmap.len();
109 let data_start = data.as_ptr() as usize;
110 let data_end = data_start + data.len();
111 assert!(start <= data_start && data_end <= end);
112 assert_page_aligned(start);
113 assert_page_aligned(data_start);
114 assert_page_aligned(data_end);
115
116 #[cfg(feature = "std")]
117 if let Some(file) = mmap.original_file() {
118 if let Some(source) = MemoryImageSource::from_file(file) {
119 return Ok(Some(MemoryImage {
120 source,
121 source_offset: u64::try_from(data_start - start).unwrap(),
122 linear_memory_offset,
123 len,
124 module_source: module_source.clone(),
125 module_source_offset: data_range.start,
126 }));
127 }
128 }
129 }
130 }
131
132 if let Some(source) = MemoryImageSource::from_data(data)? {
135 return Ok(Some(MemoryImage {
136 source,
137 source_offset: 0,
138 linear_memory_offset,
139 len,
140 module_source: module_source.clone(),
141 module_source_offset: data_range.start,
142 }));
143 }
144
145 Ok(None)
146 }
147
148 unsafe fn map_at(&self, mmap_base: &MmapOffset) -> Result<()> {
149 unsafe {
150 mmap_base.map_image_at(
151 &self.source,
152 self.source_offset,
153 self.linear_memory_offset,
154 self.len,
155 )
156 }
157 }
158
159 unsafe fn remap_as_zeros_at(&self, base: *mut u8) -> Result<()> {
160 unsafe {
161 self.source.remap_as_zeros_at(
162 base.add(self.linear_memory_offset.byte_count()),
163 self.len.byte_count(),
164 )?;
165 }
166 Ok(())
167 }
168}
169
170impl ModuleMemoryImages {
171 pub fn new(
175 engine: &Engine,
176 module: &Module,
177 source: &Arc<impl ModuleMemoryImageSource>,
178 ) -> Result<Option<ModuleMemoryImages>> {
179 let map = match &module.memory_initialization {
180 MemoryInitialization::Static { map } => map,
181 _ => return Ok(None),
182 };
183 let mut memories = TryPrimaryMap::with_capacity(map.len())?;
184 let page_size = crate::runtime::vm::host_page_size();
185 let page_size = u32::try_from(page_size).unwrap();
186 for (memory_index, init) in map {
187 let defined_memory = match module.defined_memory_index(memory_index) {
191 Some(idx) => idx,
192 None => return Ok(None),
193 };
194
195 let (offset, runtime_index) = match init {
198 Some(init) => init,
199 None => {
200 memories.push(None)?;
201 continue;
202 }
203 };
204
205 let data_range = &module.runtime_data[*runtime_index];
206 let data_range = usize::try_from(data_range.start).unwrap()
207 ..usize::try_from(data_range.end).unwrap();
208
209 if module.memories[memory_index]
210 .minimum_byte_size()
211 .map_or(false, |mem_initial_len| {
212 *offset + u64::try_from(data_range.len()).unwrap() > mem_initial_len
213 })
214 {
215 return Ok(None);
224 }
225
226 let offset_usize = match usize::try_from(*offset) {
227 Ok(offset) => offset,
228 Err(_) => return Ok(None),
229 };
230 let offset = HostAlignedByteCount::new(offset_usize)
231 .expect("memory init offset is a multiple of the host page size");
232
233 let image = match MemoryImage::new(engine, page_size, offset, source, data_range)? {
236 Some(image) => image,
237 None => return Ok(None),
238 };
239
240 let idx = memories.push(Some(try_new::<Arc<_>>(image)?))?;
241 assert_eq!(idx, defined_memory);
242 }
243
244 Ok(Some(ModuleMemoryImages { memories }))
245 }
246}
247
248pub struct MemoryImageSlot {
305 base: MmapOffset,
308
309 static_size: usize,
311
312 image: Option<Arc<MemoryImage>>,
317
318 accessible: HostAlignedByteCount,
327
328 dirty: bool,
340}
341
342impl fmt::Debug for MemoryImageSlot {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 f.debug_struct("MemoryImageSlot")
345 .field("base", &self.base)
346 .field("static_size", &self.static_size)
347 .field("accessible", &self.accessible)
348 .field("dirty", &self.dirty)
349 .finish_non_exhaustive()
350 }
351}
352
353impl MemoryImageSlot {
354 pub(crate) fn create(
363 base: MmapOffset,
364 accessible: HostAlignedByteCount,
365 static_size: usize,
366 ) -> Self {
367 MemoryImageSlot {
368 base,
369 static_size,
370 accessible,
371 image: None,
372 dirty: false,
373 }
374 }
375
376 pub(crate) fn set_heap_limit(&mut self, size_bytes: usize) -> Result<()> {
377 let size_bytes_aligned = HostAlignedByteCount::new_rounded_up(size_bytes)?;
378 assert!(size_bytes <= self.static_size);
379 assert!(size_bytes_aligned.byte_count() <= self.static_size);
380
381 if size_bytes_aligned <= self.accessible {
388 return Ok(());
389 }
390
391 self.set_protection(self.accessible..size_bytes_aligned, true)?;
393 self.accessible = size_bytes_aligned;
394
395 Ok(())
396 }
397
398 pub(crate) fn instantiate(
418 &mut self,
419 initial_size_bytes: usize,
420 maybe_image: Option<&Arc<MemoryImage>>,
421 ty: &wasmtime_environ::Memory,
422 memory_tunables: &MemoryTunables<'_>,
423 ) -> Result<()> {
424 assert!(!self.dirty);
425 assert!(
426 initial_size_bytes <= self.static_size,
427 "initial_size_bytes <= self.static_size failed: \
428 initial_size_bytes={initial_size_bytes}, self.static_size={}",
429 self.static_size
430 );
431 let initial_size_bytes_page_aligned =
432 HostAlignedByteCount::new_rounded_up(initial_size_bytes)?;
433
434 let images_equal = match (self.image.as_ref(), maybe_image) {
443 (Some(a), Some(b)) if Arc::ptr_eq(a, b) => true,
444 (None, None) => true,
445 _ => false,
446 };
447 if !images_equal {
448 self.remove_image()?;
449 }
450
451 if self.accessible < initial_size_bytes_page_aligned {
455 self.set_protection(self.accessible..initial_size_bytes_page_aligned, true)?;
456 self.accessible = initial_size_bytes_page_aligned;
457 }
458
459 let host_page_size_log2 = u8::try_from(host_page_size().ilog2()).unwrap();
465 if initial_size_bytes_page_aligned < self.accessible
466 && (memory_tunables.guard_size() > 0
467 || ty.can_use_virtual_memory(memory_tunables.tunables(), host_page_size_log2))
468 {
469 self.set_protection(initial_size_bytes_page_aligned..self.accessible, false)?;
470 self.accessible = initial_size_bytes_page_aligned;
471 }
472
473 assert!(initial_size_bytes <= self.accessible.byte_count());
477 assert!(initial_size_bytes_page_aligned <= self.accessible);
478 if !images_equal {
479 if let Some(image) = maybe_image.as_ref() {
480 assert!(
481 image
482 .linear_memory_offset
483 .checked_add(image.len)
484 .unwrap()
485 .byte_count()
486 <= initial_size_bytes
487 );
488 if !image.len.is_zero() {
489 unsafe {
490 image.map_at(&self.base)?;
491 }
492 }
493 }
494 self.image = maybe_image.cloned();
495 }
496
497 self.dirty = true;
500
501 Ok(())
502 }
503
504 pub(crate) fn remove_image(&mut self) -> Result<()> {
505 if let Some(image) = &self.image {
506 unsafe {
507 image.remap_as_zeros_at(self.base.as_mut_ptr())?;
508 }
509 self.image = None;
510 }
511 Ok(())
512 }
513
514 #[allow(dead_code, reason = "only used in some cfgs")]
525 pub(crate) fn clear_and_remain_ready(
526 &mut self,
527 pagemap: Option<&PageMap>,
528 keep_resident: HostAlignedByteCount,
529 decommit: impl FnMut(*mut u8, usize),
530 ) -> Result<usize> {
531 assert!(self.dirty);
532
533 let bytes_resident =
534 unsafe { self.reset_all_memory_contents(pagemap, keep_resident, decommit)? };
535
536 self.dirty = false;
537 Ok(bytes_resident)
538 }
539
540 #[allow(dead_code, reason = "only used in some cfgs")]
541 unsafe fn reset_all_memory_contents(
542 &mut self,
543 pagemap: Option<&PageMap>,
544 keep_resident: HostAlignedByteCount,
545 decommit: impl FnMut(*mut u8, usize),
546 ) -> Result<usize> {
547 match vm::decommit_behavior() {
548 DecommitBehavior::Zero => {
549 self.reset_with_anon_memory()?;
556 Ok(0)
557 }
558 DecommitBehavior::RestoreOriginalMapping => {
559 let bytes_resident =
560 unsafe { self.reset_with_original_mapping(pagemap, keep_resident, decommit) };
561 Ok(bytes_resident)
562 }
563 }
564 }
565
566 #[allow(dead_code, reason = "only used in some cfgs")]
567 unsafe fn reset_with_original_mapping(
568 &mut self,
569 pagemap: Option<&PageMap>,
570 keep_resident: HostAlignedByteCount,
571 decommit: impl FnMut(*mut u8, usize),
572 ) -> usize {
573 assert_eq!(
574 vm::decommit_behavior(),
575 DecommitBehavior::RestoreOriginalMapping
576 );
577
578 unsafe {
579 return match &self.image {
580 Some(image) => reset_with_pagemap(
584 pagemap,
585 self.base.as_mut_ptr(),
586 self.accessible,
587 keep_resident,
588 |region| manually_reset_region(self.base.as_mut_ptr().addr(), image, region),
589 decommit,
590 ),
591
592 None => reset_with_pagemap(
595 pagemap,
596 self.base.as_mut_ptr(),
597 self.accessible,
598 keep_resident,
599 |region| region.fill(0),
600 decommit,
601 ),
602 };
603 }
604
605 fn manually_reset_region(base_addr: usize, image: &MemoryImage, mut region: &mut [u8]) {
616 let image_start = image.linear_memory_offset.byte_count();
617 let image_end = image_start + image.len.byte_count();
618 let mut region_start = region.as_ptr().addr() - base_addr;
619 let region_end = region_start + region.len();
620 let image_bytes = image.module_source.wasm_data();
621 let image_bytes = &image_bytes[image.module_source_offset..][..image.len.byte_count()];
622
623 if let Some(len_before_image) = image_start.checked_sub(region_start) {
625 let len = len_before_image.min(region.len());
626 let (a, b) = region.split_at_mut(len);
627 a.fill(0);
628 region = b;
629 region_start += len;
630
631 if region.is_empty() {
632 return;
633 }
634 }
635
636 debug_assert_eq!(region_end - region_start, region.len());
637 debug_assert!(region_start >= image_start);
638
639 if let Some(len_in_image) = image_end.checked_sub(region_start) {
642 let len = len_in_image.min(region.len());
643 let (a, b) = region.split_at_mut(len);
644 a.copy_from_slice(&image_bytes[region_start - image_start..][..len]);
645 region = b;
646 region_start += len;
647
648 if region.is_empty() {
649 return;
650 }
651 }
652
653 debug_assert_eq!(region_end - region_start, region.len());
654 debug_assert!(region_start >= image_end);
655
656 region.fill(0);
658 }
659 }
660
661 fn set_protection(&self, range: Range<HostAlignedByteCount>, readwrite: bool) -> Result<()> {
662 let len = range
663 .end
664 .checked_sub(range.start)
665 .expect("range.start <= range.end");
666 assert!(range.end.byte_count() <= self.static_size);
667 if len.is_zero() {
668 return Ok(());
669 }
670
671 unsafe {
674 let start = self.base.as_mut_ptr().add(range.start.byte_count());
675 if readwrite {
676 vm::expose_existing_mapping(start, len.byte_count())?;
677 } else {
678 vm::hide_existing_mapping(start, len.byte_count())?;
679 }
680 }
681
682 Ok(())
683 }
684
685 pub(crate) fn has_image(&self) -> bool {
686 self.image.is_some()
687 }
688
689 #[allow(dead_code, reason = "only used in some cfgs")]
690 pub(crate) fn is_dirty(&self) -> bool {
691 self.dirty
692 }
693
694 pub(crate) fn reset_with_anon_memory(&mut self) -> Result<()> {
697 if self.static_size == 0 {
698 assert!(self.image.is_none());
699 assert_eq!(self.accessible, 0);
700 return Ok(());
701 }
702
703 unsafe {
704 vm::erase_existing_mapping(self.base.as_mut_ptr(), self.static_size)?;
705 }
706
707 self.image = None;
708 self.accessible = HostAlignedByteCount::ZERO;
709
710 Ok(())
711 }
712}
713
714#[cfg(all(test, target_os = "linux", not(miri)))]
715mod test {
716 use super::*;
717 use crate::runtime::vm::mmap::{AlignedLength, Mmap};
718 use crate::runtime::vm::sys::vm::decommit_pages;
719 use crate::runtime::vm::{HostAlignedByteCount, MmapVec, host_page_size};
720 use std::sync::Arc;
721 use wasmtime_environ::{IndexType, Limits, Memory, MemoryKind, Tunables};
722
723 fn create_memfd_with_data(offset: usize, data: &[u8]) -> Result<MemoryImage> {
724 let linear_memory_offset =
726 HostAlignedByteCount::new(offset).expect("offset is page-aligned");
727 let image_len = HostAlignedByteCount::new_rounded_up(data.len()).unwrap();
729
730 let mut source = TestDataSource {
731 data: vec![0; image_len.byte_count()],
732 };
733 source.data[..data.len()].copy_from_slice(data);
734
735 return Ok(MemoryImage {
736 source: MemoryImageSource::from_data(data)?.unwrap(),
737 len: image_len,
738 source_offset: 0,
739 linear_memory_offset,
740 module_source: Arc::new(source),
741 module_source_offset: 0,
742 });
743
744 struct TestDataSource {
745 data: Vec<u8>,
746 }
747
748 impl ModuleMemoryImageSource for TestDataSource {
749 fn wasm_data(&self) -> &[u8] {
750 &self.data
751 }
752 fn mmap(&self) -> Option<&MmapVec> {
753 None
754 }
755 }
756 }
757
758 fn dummy_memory() -> Memory {
759 Memory {
760 idx_type: IndexType::I32,
761 limits: Limits { min: 0, max: None },
762 shared: false,
763 page_size_log2: Memory::DEFAULT_PAGE_SIZE_LOG2,
764 }
765 }
766
767 fn mmap_4mib_inaccessible() -> Arc<Mmap<AlignedLength>> {
768 let four_mib = HostAlignedByteCount::new(4 << 20).expect("4 MiB is page aligned");
769 Arc::new(Mmap::accessible_reserved(HostAlignedByteCount::ZERO, four_mib).unwrap())
770 }
771
772 unsafe fn with_slice_mut(
785 mmap: &Arc<Mmap<AlignedLength>>,
786 range: Range<usize>,
787 f: impl FnOnce(&mut [u8]) + 'static,
788 ) {
789 let ptr = mmap.as_ptr().cast_mut();
790 let slice = unsafe {
791 core::slice::from_raw_parts_mut(ptr.add(range.start), range.end - range.start)
792 };
793 f(slice);
794 }
795
796 #[test]
797 fn instantiate_no_image() {
798 let ty = dummy_memory();
799 let tunables = Tunables {
800 memory_reservation: 4 << 30,
801 ..Tunables::default_miri()
802 };
803 let mmap = mmap_4mib_inaccessible();
805 let mut memfd =
807 MemoryImageSlot::create(mmap.zero_offset(), HostAlignedByteCount::ZERO, 4 << 20);
808 assert!(!memfd.is_dirty());
809 memfd
811 .instantiate(
812 64 << 10,
813 None,
814 &ty,
815 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
816 )
817 .unwrap();
818 assert!(memfd.is_dirty());
819
820 unsafe {
823 with_slice_mut(&mmap, 0..65536, |slice| {
824 assert_eq!(0, slice[0]);
825 assert_eq!(0, slice[65535]);
826 slice[1024] = 42;
827 assert_eq!(42, slice[1024]);
828 });
829 }
830
831 memfd.set_heap_limit(128 << 10).unwrap();
833 let slice = unsafe { mmap.slice(0..1 << 20) };
834 assert_eq!(42, slice[1024]);
835 assert_eq!(0, slice[131071]);
836 memfd
839 .clear_and_remain_ready(None, HostAlignedByteCount::ZERO, |ptr, len| unsafe {
840 decommit_pages(ptr, len).unwrap()
841 })
842 .unwrap();
843 assert!(!memfd.is_dirty());
844 memfd
845 .instantiate(
846 64 << 10,
847 None,
848 &ty,
849 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
850 )
851 .unwrap();
852 let slice = unsafe { mmap.slice(0..65536) };
853 assert_eq!(0, slice[1024]);
854 }
855
856 #[test]
857 fn instantiate_image() {
858 let page_size = host_page_size();
859 let ty = dummy_memory();
860 let tunables = Tunables {
861 memory_reservation: 4 << 30,
862 ..Tunables::default_miri()
863 };
864 let mmap = mmap_4mib_inaccessible();
866 let mut memfd =
868 MemoryImageSlot::create(mmap.zero_offset(), HostAlignedByteCount::ZERO, 4 << 20);
869 let image = Arc::new(create_memfd_with_data(page_size, &[1, 2, 3, 4]).unwrap());
871 memfd
873 .instantiate(
874 64 << 10,
875 Some(&image),
876 &ty,
877 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
878 )
879 .unwrap();
880 assert!(memfd.has_image());
881
882 unsafe {
883 with_slice_mut(&mmap, 0..65536, move |slice| {
884 assert_eq!(&[1, 2, 3, 4], &slice[page_size..][..4]);
885 slice[page_size] = 5;
886 });
887 }
888
889 memfd
891 .clear_and_remain_ready(None, HostAlignedByteCount::ZERO, |ptr, len| unsafe {
892 decommit_pages(ptr, len).unwrap()
893 })
894 .unwrap();
895 memfd
896 .instantiate(
897 64 << 10,
898 Some(&image),
899 &ty,
900 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
901 )
902 .unwrap();
903 let slice = unsafe { mmap.slice(0..65536) };
904 assert_eq!(&[1, 2, 3, 4], &slice[page_size..][..4]);
905
906 memfd
908 .clear_and_remain_ready(None, HostAlignedByteCount::ZERO, |ptr, len| unsafe {
909 decommit_pages(ptr, len).unwrap()
910 })
911 .unwrap();
912 memfd
913 .instantiate(
914 64 << 10,
915 None,
916 &ty,
917 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
918 )
919 .unwrap();
920 assert!(!memfd.has_image());
921 let slice = unsafe { mmap.slice(0..65536) };
922 assert_eq!(&[0, 0, 0, 0], &slice[page_size..][..4]);
923
924 memfd
926 .clear_and_remain_ready(None, HostAlignedByteCount::ZERO, |ptr, len| unsafe {
927 decommit_pages(ptr, len).unwrap()
928 })
929 .unwrap();
930 memfd
931 .instantiate(
932 64 << 10,
933 Some(&image),
934 &ty,
935 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
936 )
937 .unwrap();
938 let slice = unsafe { mmap.slice(0..65536) };
939 assert_eq!(&[1, 2, 3, 4], &slice[page_size..][..4]);
940
941 let image2 = Arc::new(create_memfd_with_data(page_size, &[10, 11, 12, 13]).unwrap());
943 memfd
944 .clear_and_remain_ready(None, HostAlignedByteCount::ZERO, |ptr, len| unsafe {
945 decommit_pages(ptr, len).unwrap()
946 })
947 .unwrap();
948 memfd
949 .instantiate(
950 128 << 10,
951 Some(&image2),
952 &ty,
953 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
954 )
955 .unwrap();
956 let slice = unsafe { mmap.slice(0..65536) };
957 assert_eq!(&[10, 11, 12, 13], &slice[page_size..][..4]);
958
959 memfd
962 .clear_and_remain_ready(None, HostAlignedByteCount::ZERO, |ptr, len| unsafe {
963 decommit_pages(ptr, len).unwrap()
964 })
965 .unwrap();
966 memfd
967 .instantiate(
968 64 << 10,
969 Some(&image),
970 &ty,
971 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
972 )
973 .unwrap();
974 let slice = unsafe { mmap.slice(0..65536) };
975 assert_eq!(&[1, 2, 3, 4], &slice[page_size..][..4]);
976 }
977
978 #[test]
979 #[cfg(target_os = "linux")]
980 fn memset_instead_of_madvise() {
981 let page_size = host_page_size();
982 let ty = dummy_memory();
983 let tunables = Tunables {
984 memory_reservation: 100 << 16,
985 ..Tunables::default_miri()
986 };
987 let mmap = mmap_4mib_inaccessible();
988 let mut memfd =
989 MemoryImageSlot::create(mmap.zero_offset(), HostAlignedByteCount::ZERO, 4 << 20);
990
991 for image_off in [0, page_size, page_size * 2] {
993 let image = Arc::new(create_memfd_with_data(image_off, &[1, 2, 3, 4]).unwrap());
994 for amt_to_memset in [0, page_size, page_size * 10, 1 << 20, 10 << 20] {
995 let amt_to_memset = HostAlignedByteCount::new(amt_to_memset).unwrap();
996 memfd
997 .instantiate(
998 64 << 10,
999 Some(&image),
1000 &ty,
1001 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
1002 )
1003 .unwrap();
1004 assert!(memfd.has_image());
1005
1006 unsafe {
1007 with_slice_mut(&mmap, 0..64 << 10, move |slice| {
1008 if image_off > 0 {
1009 assert_eq!(slice[image_off - 1], 0);
1010 }
1011 assert_eq!(slice[image_off + 5], 0);
1012 assert_eq!(&[1, 2, 3, 4], &slice[image_off..][..4]);
1013 slice[image_off] = 5;
1014 assert_eq!(&[5, 2, 3, 4], &slice[image_off..][..4]);
1015 })
1016 };
1017
1018 memfd
1019 .clear_and_remain_ready(None, amt_to_memset, |ptr, len| unsafe {
1020 decommit_pages(ptr, len).unwrap()
1021 })
1022 .unwrap();
1023 }
1024 }
1025
1026 for amt_to_memset in [0, page_size, page_size * 10, 1 << 20, 10 << 20] {
1028 let amt_to_memset = HostAlignedByteCount::new(amt_to_memset).unwrap();
1029 memfd
1030 .instantiate(
1031 64 << 10,
1032 None,
1033 &ty,
1034 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
1035 )
1036 .unwrap();
1037
1038 unsafe {
1039 with_slice_mut(&mmap, 0..64 << 10, |slice| {
1040 for chunk in slice.chunks_mut(1024) {
1041 assert_eq!(chunk[0], 0);
1042 chunk[0] = 5;
1043 }
1044 });
1045 }
1046 memfd
1047 .clear_and_remain_ready(None, amt_to_memset, |ptr, len| unsafe {
1048 decommit_pages(ptr, len).unwrap()
1049 })
1050 .unwrap();
1051 }
1052 }
1053
1054 #[test]
1055 #[cfg(target_os = "linux")]
1056 fn dynamic() {
1057 let page_size = host_page_size();
1058 let ty = dummy_memory();
1059 let tunables = Tunables {
1060 memory_reservation: 0,
1061 memory_reservation_for_growth: 200,
1062 ..Tunables::default_miri()
1063 };
1064
1065 let mmap = mmap_4mib_inaccessible();
1066 let mut memfd =
1067 MemoryImageSlot::create(mmap.zero_offset(), HostAlignedByteCount::ZERO, 4 << 20);
1068 let image = Arc::new(create_memfd_with_data(page_size, &[1, 2, 3, 4]).unwrap());
1069 let initial = 64 << 10;
1070
1071 memfd
1074 .instantiate(
1075 initial,
1076 Some(&image),
1077 &ty,
1078 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
1079 )
1080 .unwrap();
1081 assert!(memfd.has_image());
1082
1083 unsafe {
1084 with_slice_mut(&mmap, 0..(64 << 10) + page_size, move |slice| {
1085 assert_eq!(&[1, 2, 3, 4], &slice[page_size..][..4]);
1086 slice[page_size] = 5;
1087 assert_eq!(&[5, 2, 3, 4], &slice[page_size..][..4]);
1088 });
1089 }
1090
1091 memfd
1092 .clear_and_remain_ready(None, HostAlignedByteCount::ZERO, |ptr, len| unsafe {
1093 decommit_pages(ptr, len).unwrap()
1094 })
1095 .unwrap();
1096 let slice = unsafe { mmap.slice(0..(64 << 10) + page_size) };
1097 assert_eq!(&[1, 2, 3, 4], &slice[page_size..][..4]);
1098
1099 memfd
1102 .instantiate(
1103 initial,
1104 Some(&image),
1105 &ty,
1106 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
1107 )
1108 .unwrap();
1109 assert_eq!(&[1, 2, 3, 4], &slice[page_size..][..4]);
1110
1111 memfd.set_heap_limit(initial * 2).unwrap();
1112
1113 unsafe {
1114 with_slice_mut(&mmap, 0..(64 << 10) + page_size, move |slice| {
1115 assert_eq!(&[0, 0], &slice[initial..initial + 2]);
1116 slice[initial] = 100;
1117 assert_eq!(&[100, 0], &slice[initial..initial + 2]);
1118 });
1119 }
1120
1121 memfd
1122 .clear_and_remain_ready(None, HostAlignedByteCount::ZERO, |ptr, len| unsafe {
1123 decommit_pages(ptr, len).unwrap()
1124 })
1125 .unwrap();
1126
1127 assert_eq!(&[0, 0], &slice[initial..initial + 2]);
1129
1130 memfd
1133 .instantiate(
1134 initial,
1135 Some(&image),
1136 &ty,
1137 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
1138 )
1139 .unwrap();
1140 assert_eq!(&[0, 0], &slice[initial..initial + 2]);
1141 memfd.set_heap_limit(initial * 2).unwrap();
1142
1143 unsafe {
1144 with_slice_mut(&mmap, 0..(64 << 10) + page_size, move |slice| {
1145 assert_eq!(&[0, 0], &slice[initial..initial + 2]);
1146 slice[initial] = 100;
1147 assert_eq!(&[100, 0], &slice[initial..initial + 2]);
1148 });
1149 }
1150
1151 memfd
1152 .clear_and_remain_ready(None, HostAlignedByteCount::ZERO, |ptr, len| unsafe {
1153 decommit_pages(ptr, len).unwrap()
1154 })
1155 .unwrap();
1156
1157 memfd
1159 .instantiate(
1160 64 << 10,
1161 None,
1162 &ty,
1163 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
1164 )
1165 .unwrap();
1166 assert!(!memfd.has_image());
1167 assert_eq!(&[0, 0, 0, 0], &slice[page_size..][..4]);
1168 assert_eq!(&[0, 0], &slice[initial..initial + 2]);
1169 }
1170
1171 #[test]
1172 fn reset_with_pagemap() {
1173 let page_size = host_page_size();
1174 let ty = dummy_memory();
1175 let tunables = Tunables {
1176 memory_reservation: 100 << 16,
1177 ..Tunables::default_miri()
1178 };
1179 let mmap = mmap_4mib_inaccessible();
1180 let mmap_len = page_size * 9;
1181 let mut memfd =
1182 MemoryImageSlot::create(mmap.zero_offset(), HostAlignedByteCount::ZERO, mmap_len);
1183 let pagemap = PageMap::new();
1184 let pagemap = pagemap.as_ref();
1185
1186 let mut data = vec![0; 3 * page_size];
1187 for (i, chunk) in data.chunks_mut(page_size).enumerate() {
1188 for slot in chunk {
1189 *slot = u8::try_from(i + 1).unwrap();
1190 }
1191 }
1192 let image = Arc::new(create_memfd_with_data(3 * page_size, &data).unwrap());
1193
1194 memfd
1195 .instantiate(
1196 mmap_len,
1197 Some(&image),
1198 &ty,
1199 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
1200 )
1201 .unwrap();
1202
1203 let keep_resident = HostAlignedByteCount::new(mmap_len).unwrap();
1204 let assert_pristine_after_reset = |memfd: &mut MemoryImageSlot| unsafe {
1205 memfd
1207 .clear_and_remain_ready(pagemap, keep_resident, |ptr, len| {
1208 decommit_pages(ptr, len).unwrap()
1209 })
1210 .unwrap();
1211
1212 with_slice_mut(&mmap, 0..mmap_len, move |slice| {
1215 for (i, chunk) in slice.chunks(page_size).enumerate() {
1216 let expected = match i {
1217 0..3 => 0,
1218 3..6 => u8::try_from(i).unwrap() - 2,
1219 6..9 => 0,
1220 _ => unreachable!(),
1221 };
1222 for slot in chunk {
1223 assert_eq!(*slot, expected);
1224 }
1225 }
1226 });
1227
1228 memfd
1231 .instantiate(
1232 mmap_len,
1233 Some(&image),
1234 &ty,
1235 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
1236 )
1237 .unwrap();
1238 memfd
1239 .clear_and_remain_ready(pagemap, HostAlignedByteCount::ZERO, |ptr, len| {
1240 decommit_pages(ptr, len).unwrap()
1241 })
1242 .unwrap();
1243
1244 memfd
1246 .instantiate(
1247 mmap_len,
1248 Some(&image),
1249 &ty,
1250 &MemoryTunables::new(&tunables, MemoryKind::LinearMemory),
1251 )
1252 .unwrap();
1253 };
1254
1255 let write_page = |_memfd: &mut MemoryImageSlot, page: usize| unsafe {
1256 with_slice_mut(
1257 &mmap,
1258 page * page_size..(page + 1) * page_size,
1259 move |slice| slice.fill(0xff),
1260 );
1261 };
1262
1263 assert_pristine_after_reset(&mut memfd);
1269
1270 for i in 0..9 {
1271 write_page(&mut memfd, i);
1272 assert_pristine_after_reset(&mut memfd);
1273 }
1274 write_page(&mut memfd, 0);
1275 write_page(&mut memfd, 1);
1276 assert_pristine_after_reset(&mut memfd);
1277 write_page(&mut memfd, 1);
1278 assert_pristine_after_reset(&mut memfd);
1279 write_page(&mut memfd, 2);
1280 write_page(&mut memfd, 3);
1281 assert_pristine_after_reset(&mut memfd);
1282 write_page(&mut memfd, 3);
1283 write_page(&mut memfd, 4);
1284 write_page(&mut memfd, 5);
1285 assert_pristine_after_reset(&mut memfd);
1286 write_page(&mut memfd, 0);
1287 write_page(&mut memfd, 1);
1288 write_page(&mut memfd, 2);
1289 assert_pristine_after_reset(&mut memfd);
1290 write_page(&mut memfd, 0);
1291 write_page(&mut memfd, 3);
1292 write_page(&mut memfd, 6);
1293 assert_pristine_after_reset(&mut memfd);
1294 write_page(&mut memfd, 2);
1295 write_page(&mut memfd, 3);
1296 write_page(&mut memfd, 4);
1297 write_page(&mut memfd, 5);
1298 write_page(&mut memfd, 6);
1299 assert_pristine_after_reset(&mut memfd);
1300 write_page(&mut memfd, 4);
1301 write_page(&mut memfd, 5);
1302 write_page(&mut memfd, 6);
1303 write_page(&mut memfd, 7);
1304 assert_pristine_after_reset(&mut memfd);
1305 write_page(&mut memfd, 4);
1306 write_page(&mut memfd, 5);
1307 write_page(&mut memfd, 8);
1308 assert_pristine_after_reset(&mut memfd);
1309 }
1310}