1 // Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
2 //
3 // Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 //
5 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
6 // Use of this source code is governed by a BSD-style license that can be
7 // found in the LICENSE-BSD-3-Clause file.
8 //
9 // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
10 
11 //! Helper structure for working with mmaped memory regions in Unix.
12 
13 use std::io;
14 use std::os::unix::io::AsRawFd;
15 use std::ptr::null_mut;
16 use std::result;
17 
18 use crate::bitmap::{Bitmap, BS};
19 use crate::guest_memory::FileOffset;
20 use crate::mmap::{check_file_offset, NewBitmap};
21 use crate::volatile_memory::{self, VolatileMemory, VolatileSlice};
22 
23 /// Error conditions that may arise when creating a new `MmapRegion` object.
24 #[derive(Debug, thiserror::Error)]
25 pub enum Error {
26     /// The specified file offset and length cause overflow when added.
27     #[error("The specified file offset and length cause overflow when added")]
28     InvalidOffsetLength,
29     /// The specified pointer to the mapping is not page-aligned.
30     #[error("The specified pointer to the mapping is not page-aligned")]
31     InvalidPointer,
32     /// The forbidden `MAP_FIXED` flag was specified.
33     #[error("The forbidden `MAP_FIXED` flag was specified")]
34     MapFixed,
35     /// Mappings using the same fd overlap in terms of file offset and length.
36     #[error("Mappings using the same fd overlap in terms of file offset and length")]
37     MappingOverlap,
38     /// A mapping with offset + length > EOF was attempted.
39     #[error("The specified file offset and length is greater then file length")]
40     MappingPastEof,
41     /// The `mmap` call returned an error.
42     #[error("{0}")]
43     Mmap(io::Error),
44     /// Seeking the end of the file returned an error.
45     #[error("Error seeking the end of the file: {0}")]
46     SeekEnd(io::Error),
47     /// Seeking the start of the file returned an error.
48     #[error("Error seeking the start of the file: {0}")]
49     SeekStart(io::Error),
50 }
51 
52 pub type Result<T> = result::Result<T, Error>;
53 
54 /// A factory struct to build `MmapRegion` objects.
55 pub struct MmapRegionBuilder<B = ()> {
56     size: usize,
57     prot: i32,
58     flags: i32,
59     file_offset: Option<FileOffset>,
60     raw_ptr: Option<*mut u8>,
61     hugetlbfs: Option<bool>,
62     bitmap: B,
63 }
64 
65 impl<B: Bitmap + Default> MmapRegionBuilder<B> {
66     /// Create a new `MmapRegionBuilder` using the default value for
67     /// the inner `Bitmap` object.
new(size: usize) -> Self68     pub fn new(size: usize) -> Self {
69         Self::new_with_bitmap(size, B::default())
70     }
71 }
72 
73 impl<B: Bitmap> MmapRegionBuilder<B> {
74     /// Create a new `MmapRegionBuilder` using the provided `Bitmap` object.
75     ///
76     /// When instantiating the builder for a region that does not require dirty bitmap
77     /// bitmap tracking functionality, we can specify a trivial `Bitmap` implementation
78     /// such as `()`.
new_with_bitmap(size: usize, bitmap: B) -> Self79     pub fn new_with_bitmap(size: usize, bitmap: B) -> Self {
80         MmapRegionBuilder {
81             size,
82             prot: 0,
83             flags: libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
84             file_offset: None,
85             raw_ptr: None,
86             hugetlbfs: None,
87             bitmap,
88         }
89     }
90 
91     /// Create the `MmapRegion` object with the specified mmap memory protection flag `prot`.
with_mmap_prot(mut self, prot: i32) -> Self92     pub fn with_mmap_prot(mut self, prot: i32) -> Self {
93         self.prot = prot;
94         self
95     }
96 
97     /// Create the `MmapRegion` object with the specified mmap `flags`.
with_mmap_flags(mut self, flags: i32) -> Self98     pub fn with_mmap_flags(mut self, flags: i32) -> Self {
99         self.flags = flags;
100         self
101     }
102 
103     /// Create the `MmapRegion` object with the specified `file_offset`.
with_file_offset(mut self, file_offset: FileOffset) -> Self104     pub fn with_file_offset(mut self, file_offset: FileOffset) -> Self {
105         self.file_offset = Some(file_offset);
106         self
107     }
108 
109     /// Create the `MmapRegion` object with the specified `hugetlbfs` flag.
with_hugetlbfs(mut self, hugetlbfs: bool) -> Self110     pub fn with_hugetlbfs(mut self, hugetlbfs: bool) -> Self {
111         self.hugetlbfs = Some(hugetlbfs);
112         self
113     }
114 
115     /// Create the `MmapRegion` object with pre-mmapped raw pointer.
116     ///
117     /// # Safety
118     ///
119     /// To use this safely, the caller must guarantee that `raw_addr` and `self.size` define a
120     /// region within a valid mapping that is already present in the process.
with_raw_mmap_pointer(mut self, raw_ptr: *mut u8) -> Self121     pub unsafe fn with_raw_mmap_pointer(mut self, raw_ptr: *mut u8) -> Self {
122         self.raw_ptr = Some(raw_ptr);
123         self
124     }
125 
126     /// Build the `MmapRegion` object.
build(self) -> Result<MmapRegion<B>>127     pub fn build(self) -> Result<MmapRegion<B>> {
128         if self.raw_ptr.is_some() {
129             return self.build_raw();
130         }
131 
132         // Forbid MAP_FIXED, as it doesn't make sense in this context, and is pretty dangerous
133         // in general.
134         if self.flags & libc::MAP_FIXED != 0 {
135             return Err(Error::MapFixed);
136         }
137 
138         let (fd, offset) = if let Some(ref f_off) = self.file_offset {
139             check_file_offset(f_off, self.size)?;
140             (f_off.file().as_raw_fd(), f_off.start())
141         } else {
142             (-1, 0)
143         };
144 
145         #[cfg(not(miri))]
146         // SAFETY: This is safe because we're not allowing MAP_FIXED, and invalid parameters
147         // cannot break Rust safety guarantees (things may change if we're mapping /dev/mem or
148         // some wacky file).
149         let addr = unsafe {
150             libc::mmap(
151                 null_mut(),
152                 self.size,
153                 self.prot,
154                 self.flags,
155                 fd,
156                 offset as libc::off_t,
157             )
158         };
159 
160         #[cfg(not(miri))]
161         if addr == libc::MAP_FAILED {
162             return Err(Error::Mmap(io::Error::last_os_error()));
163         }
164 
165         #[cfg(miri)]
166         if self.size == 0 {
167             return Err(Error::Mmap(io::Error::from_raw_os_error(libc::EINVAL)));
168         }
169 
170         // Miri does not support the mmap syscall, so we use rust's allocator for miri tests
171         #[cfg(miri)]
172         let addr = unsafe {
173             std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(self.size, 8).unwrap())
174         };
175 
176         Ok(MmapRegion {
177             addr: addr as *mut u8,
178             size: self.size,
179             bitmap: self.bitmap,
180             file_offset: self.file_offset,
181             prot: self.prot,
182             flags: self.flags,
183             owned: true,
184             hugetlbfs: self.hugetlbfs,
185         })
186     }
187 
build_raw(self) -> Result<MmapRegion<B>>188     fn build_raw(self) -> Result<MmapRegion<B>> {
189         // SAFETY: Safe because this call just returns the page size and doesn't have any side
190         // effects.
191         let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize;
192         let addr = self.raw_ptr.unwrap();
193 
194         // Check that the pointer to the mapping is page-aligned.
195         if (addr as usize) & (page_size - 1) != 0 {
196             return Err(Error::InvalidPointer);
197         }
198 
199         Ok(MmapRegion {
200             addr,
201             size: self.size,
202             bitmap: self.bitmap,
203             file_offset: self.file_offset,
204             prot: self.prot,
205             flags: self.flags,
206             owned: false,
207             hugetlbfs: self.hugetlbfs,
208         })
209     }
210 }
211 
212 /// Helper structure for working with mmaped memory regions in Unix.
213 ///
214 /// The structure is used for accessing the guest's physical memory by mmapping it into
215 /// the current process.
216 ///
217 /// # Limitations
218 /// When running a 64-bit virtual machine on a 32-bit hypervisor, only part of the guest's
219 /// physical memory may be mapped into the current process due to the limited virtual address
220 /// space size of the process.
221 #[derive(Debug)]
222 pub struct MmapRegion<B = ()> {
223     addr: *mut u8,
224     size: usize,
225     bitmap: B,
226     file_offset: Option<FileOffset>,
227     prot: i32,
228     flags: i32,
229     owned: bool,
230     hugetlbfs: Option<bool>,
231 }
232 
233 // SAFETY: Send and Sync aren't automatically inherited for the raw address pointer.
234 // Accessing that pointer is only done through the stateless interface which
235 // allows the object to be shared by multiple threads without a decrease in
236 // safety.
237 unsafe impl<B: Send> Send for MmapRegion<B> {}
238 // SAFETY: See comment above.
239 unsafe impl<B: Sync> Sync for MmapRegion<B> {}
240 
241 impl<B: NewBitmap> MmapRegion<B> {
242     /// Creates a shared anonymous mapping of `size` bytes.
243     ///
244     /// # Arguments
245     /// * `size` - The size of the memory region in bytes.
new(size: usize) -> Result<Self>246     pub fn new(size: usize) -> Result<Self> {
247         MmapRegionBuilder::new_with_bitmap(size, B::with_len(size))
248             .with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE)
249             .with_mmap_flags(libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE)
250             .build()
251     }
252 
253     /// Creates a shared file mapping of `size` bytes.
254     ///
255     /// # Arguments
256     /// * `file_offset` - The mapping will be created at offset `file_offset.start` in the file
257     ///                   referred to by `file_offset.file`.
258     /// * `size` - The size of the memory region in bytes.
from_file(file_offset: FileOffset, size: usize) -> Result<Self>259     pub fn from_file(file_offset: FileOffset, size: usize) -> Result<Self> {
260         MmapRegionBuilder::new_with_bitmap(size, B::with_len(size))
261             .with_file_offset(file_offset)
262             .with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE)
263             .with_mmap_flags(libc::MAP_NORESERVE | libc::MAP_SHARED)
264             .build()
265     }
266 
267     /// Creates a mapping based on the provided arguments.
268     ///
269     /// # Arguments
270     /// * `file_offset` - if provided, the method will create a file mapping at offset
271     ///                   `file_offset.start` in the file referred to by `file_offset.file`.
272     /// * `size` - The size of the memory region in bytes.
273     /// * `prot` - The desired memory protection of the mapping.
274     /// * `flags` - This argument determines whether updates to the mapping are visible to other
275     ///             processes mapping the same region, and whether updates are carried through to
276     ///             the underlying file.
build( file_offset: Option<FileOffset>, size: usize, prot: i32, flags: i32, ) -> Result<Self>277     pub fn build(
278         file_offset: Option<FileOffset>,
279         size: usize,
280         prot: i32,
281         flags: i32,
282     ) -> Result<Self> {
283         let mut builder = MmapRegionBuilder::new_with_bitmap(size, B::with_len(size))
284             .with_mmap_prot(prot)
285             .with_mmap_flags(flags);
286         if let Some(v) = file_offset {
287             builder = builder.with_file_offset(v);
288         }
289         builder.build()
290     }
291 
292     /// Creates a `MmapRegion` instance for an externally managed mapping.
293     ///
294     /// This method is intended to be used exclusively in situations in which the mapping backing
295     /// the region is provided by an entity outside the control of the caller (e.g. the dynamic
296     /// linker).
297     ///
298     /// # Arguments
299     /// * `addr` - Pointer to the start of the mapping. Must be page-aligned.
300     /// * `size` - The size of the memory region in bytes.
301     /// * `prot` - Must correspond to the memory protection attributes of the existing mapping.
302     /// * `flags` - Must correspond to the flags that were passed to `mmap` for the creation of
303     ///             the existing mapping.
304     ///
305     /// # Safety
306     ///
307     /// To use this safely, the caller must guarantee that `addr` and `size` define a region within
308     /// a valid mapping that is already present in the process.
build_raw(addr: *mut u8, size: usize, prot: i32, flags: i32) -> Result<Self>309     pub unsafe fn build_raw(addr: *mut u8, size: usize, prot: i32, flags: i32) -> Result<Self> {
310         MmapRegionBuilder::new_with_bitmap(size, B::with_len(size))
311             .with_raw_mmap_pointer(addr)
312             .with_mmap_prot(prot)
313             .with_mmap_flags(flags)
314             .build()
315     }
316 }
317 
318 impl<B: Bitmap> MmapRegion<B> {
319     /// Returns a pointer to the beginning of the memory region. Mutable accesses performed
320     /// using the resulting pointer are not automatically accounted for by the dirty bitmap
321     /// tracking functionality.
322     ///
323     /// Should only be used for passing this region to ioctls for setting guest memory.
as_ptr(&self) -> *mut u8324     pub fn as_ptr(&self) -> *mut u8 {
325         self.addr
326     }
327 
328     /// Returns the size of this region.
size(&self) -> usize329     pub fn size(&self) -> usize {
330         self.size
331     }
332 
333     /// Returns information regarding the offset into the file backing this region (if any).
file_offset(&self) -> Option<&FileOffset>334     pub fn file_offset(&self) -> Option<&FileOffset> {
335         self.file_offset.as_ref()
336     }
337 
338     /// Returns the value of the `prot` parameter passed to `mmap` when mapping this region.
prot(&self) -> i32339     pub fn prot(&self) -> i32 {
340         self.prot
341     }
342 
343     /// Returns the value of the `flags` parameter passed to `mmap` when mapping this region.
flags(&self) -> i32344     pub fn flags(&self) -> i32 {
345         self.flags
346     }
347 
348     /// Returns `true` if the mapping is owned by this `MmapRegion` instance.
owned(&self) -> bool349     pub fn owned(&self) -> bool {
350         self.owned
351     }
352 
353     /// Checks whether this region and `other` are backed by overlapping
354     /// [`FileOffset`](struct.FileOffset.html) objects.
355     ///
356     /// This is mostly a sanity check available for convenience, as different file descriptors
357     /// can alias the same file.
fds_overlap<T: Bitmap>(&self, other: &MmapRegion<T>) -> bool358     pub fn fds_overlap<T: Bitmap>(&self, other: &MmapRegion<T>) -> bool {
359         if let Some(f_off1) = self.file_offset() {
360             if let Some(f_off2) = other.file_offset() {
361                 if f_off1.file().as_raw_fd() == f_off2.file().as_raw_fd() {
362                     let s1 = f_off1.start();
363                     let s2 = f_off2.start();
364                     let l1 = self.len() as u64;
365                     let l2 = other.len() as u64;
366 
367                     if s1 < s2 {
368                         return s1 + l1 > s2;
369                     } else {
370                         return s2 + l2 > s1;
371                     }
372                 }
373             }
374         }
375         false
376     }
377 
378     /// Set the hugetlbfs of the region
set_hugetlbfs(&mut self, hugetlbfs: bool)379     pub fn set_hugetlbfs(&mut self, hugetlbfs: bool) {
380         self.hugetlbfs = Some(hugetlbfs)
381     }
382 
383     /// Returns `true` if the region is hugetlbfs
is_hugetlbfs(&self) -> Option<bool>384     pub fn is_hugetlbfs(&self) -> Option<bool> {
385         self.hugetlbfs
386     }
387 
388     /// Returns a reference to the inner bitmap object.
bitmap(&self) -> &B389     pub fn bitmap(&self) -> &B {
390         &self.bitmap
391     }
392 }
393 
394 impl<B: Bitmap> VolatileMemory for MmapRegion<B> {
395     type B = B;
396 
len(&self) -> usize397     fn len(&self) -> usize {
398         self.size
399     }
400 
get_slice( &self, offset: usize, count: usize, ) -> volatile_memory::Result<VolatileSlice<BS<B>>>401     fn get_slice(
402         &self,
403         offset: usize,
404         count: usize,
405     ) -> volatile_memory::Result<VolatileSlice<BS<B>>> {
406         let _ = self.compute_end_offset(offset, count)?;
407 
408         Ok(
409             // SAFETY: Safe because we checked that offset + count was within our range and we only
410             // ever hand out volatile accessors.
411             unsafe {
412                 VolatileSlice::with_bitmap(
413                     self.addr.add(offset),
414                     count,
415                     self.bitmap.slice_at(offset),
416                     None,
417                 )
418             },
419         )
420     }
421 }
422 
423 impl<B> Drop for MmapRegion<B> {
drop(&mut self)424     fn drop(&mut self) {
425         if self.owned {
426             // SAFETY: This is safe because we mmap the area at addr ourselves, and nobody
427             // else is holding a reference to it.
428             unsafe {
429                 #[cfg(not(miri))]
430                 libc::munmap(self.addr as *mut libc::c_void, self.size);
431 
432                 #[cfg(miri)]
433                 std::alloc::dealloc(
434                     self.addr,
435                     std::alloc::Layout::from_size_align(self.size, 8).unwrap(),
436                 );
437             }
438         }
439     }
440 }
441 
442 #[cfg(test)]
443 mod tests {
444     #![allow(clippy::undocumented_unsafe_blocks)]
445     use super::*;
446 
447     use std::io::Write;
448     use std::slice;
449     use std::sync::Arc;
450     use vmm_sys_util::tempfile::TempFile;
451 
452     use crate::bitmap::AtomicBitmap;
453 
454     type MmapRegion = super::MmapRegion<()>;
455 
456     // Adding a helper method to extract the errno within an Error::Mmap(e), or return a
457     // distinctive value when the error is represented by another variant.
458     impl Error {
raw_os_error(&self) -> i32459         pub fn raw_os_error(&self) -> i32 {
460             match self {
461                 Error::Mmap(e) => e.raw_os_error().unwrap(),
462                 _ => std::i32::MIN,
463             }
464         }
465     }
466 
467     #[test]
test_mmap_region_new()468     fn test_mmap_region_new() {
469         assert!(MmapRegion::new(0).is_err());
470 
471         let size = 4096;
472 
473         let r = MmapRegion::new(4096).unwrap();
474         assert_eq!(r.size(), size);
475         assert!(r.file_offset().is_none());
476         assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
477         assert_eq!(
478             r.flags(),
479             libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE
480         );
481     }
482 
483     #[test]
test_mmap_region_set_hugetlbfs()484     fn test_mmap_region_set_hugetlbfs() {
485         assert!(MmapRegion::new(0).is_err());
486 
487         let size = 4096;
488 
489         let r = MmapRegion::new(size).unwrap();
490         assert_eq!(r.size(), size);
491         assert!(r.file_offset().is_none());
492         assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
493         assert_eq!(
494             r.flags(),
495             libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE
496         );
497         assert_eq!(r.is_hugetlbfs(), None);
498 
499         let mut r = MmapRegion::new(size).unwrap();
500         r.set_hugetlbfs(false);
501         assert_eq!(r.size(), size);
502         assert!(r.file_offset().is_none());
503         assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
504         assert_eq!(
505             r.flags(),
506             libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE
507         );
508         assert_eq!(r.is_hugetlbfs(), Some(false));
509 
510         let mut r = MmapRegion::new(size).unwrap();
511         r.set_hugetlbfs(true);
512         assert_eq!(r.size(), size);
513         assert!(r.file_offset().is_none());
514         assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
515         assert_eq!(
516             r.flags(),
517             libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE
518         );
519         assert_eq!(r.is_hugetlbfs(), Some(true));
520     }
521 
522     #[test]
523     #[cfg(not(miri))] // Miri cannot mmap files
test_mmap_region_from_file()524     fn test_mmap_region_from_file() {
525         let mut f = TempFile::new().unwrap().into_file();
526         let offset: usize = 0;
527         let buf1 = [1u8, 2, 3, 4, 5];
528 
529         f.write_all(buf1.as_ref()).unwrap();
530         let r = MmapRegion::from_file(FileOffset::new(f, offset as u64), buf1.len()).unwrap();
531 
532         assert_eq!(r.size(), buf1.len() - offset);
533         assert_eq!(r.file_offset().unwrap().start(), offset as u64);
534         assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
535         assert_eq!(r.flags(), libc::MAP_NORESERVE | libc::MAP_SHARED);
536 
537         let buf2 = unsafe { slice::from_raw_parts(r.as_ptr(), buf1.len() - offset) };
538         assert_eq!(&buf1[offset..], buf2);
539     }
540 
541     #[test]
542     #[cfg(not(miri))] // Miri cannot mmap files
test_mmap_region_build()543     fn test_mmap_region_build() {
544         let a = Arc::new(TempFile::new().unwrap().into_file());
545 
546         let prot = libc::PROT_READ | libc::PROT_WRITE;
547         let flags = libc::MAP_NORESERVE | libc::MAP_PRIVATE;
548         let offset = 4096;
549         let size = 1000;
550 
551         // Offset + size will overflow.
552         let r = MmapRegion::build(
553             Some(FileOffset::from_arc(a.clone(), std::u64::MAX)),
554             size,
555             prot,
556             flags,
557         );
558         assert_eq!(format!("{:?}", r.unwrap_err()), "InvalidOffsetLength");
559 
560         // Offset + size is greater than the size of the file (which is 0 at this point).
561         let r = MmapRegion::build(
562             Some(FileOffset::from_arc(a.clone(), offset)),
563             size,
564             prot,
565             flags,
566         );
567         assert_eq!(format!("{:?}", r.unwrap_err()), "MappingPastEof");
568 
569         // MAP_FIXED was specified among the flags.
570         let r = MmapRegion::build(
571             Some(FileOffset::from_arc(a.clone(), offset)),
572             size,
573             prot,
574             flags | libc::MAP_FIXED,
575         );
576         assert_eq!(format!("{:?}", r.unwrap_err()), "MapFixed");
577 
578         // Let's resize the file.
579         assert_eq!(unsafe { libc::ftruncate(a.as_raw_fd(), 1024 * 10) }, 0);
580 
581         // The offset is not properly aligned.
582         let r = MmapRegion::build(
583             Some(FileOffset::from_arc(a.clone(), offset - 1)),
584             size,
585             prot,
586             flags,
587         );
588         assert_eq!(r.unwrap_err().raw_os_error(), libc::EINVAL);
589 
590         // The build should be successful now.
591         let r =
592             MmapRegion::build(Some(FileOffset::from_arc(a, offset)), size, prot, flags).unwrap();
593 
594         assert_eq!(r.size(), size);
595         assert_eq!(r.file_offset().unwrap().start(), offset);
596         assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
597         assert_eq!(r.flags(), libc::MAP_NORESERVE | libc::MAP_PRIVATE);
598         assert!(r.owned());
599 
600         let region_size = 0x10_0000;
601         let bitmap = AtomicBitmap::new(region_size, 0x1000);
602         let builder = MmapRegionBuilder::new_with_bitmap(region_size, bitmap)
603             .with_hugetlbfs(true)
604             .with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE);
605         assert_eq!(builder.size, region_size);
606         assert_eq!(builder.hugetlbfs, Some(true));
607         assert_eq!(builder.prot, libc::PROT_READ | libc::PROT_WRITE);
608 
609         crate::bitmap::tests::test_volatile_memory(&(builder.build().unwrap()));
610     }
611 
612     #[test]
613     #[cfg(not(miri))] // Causes warnings due to the pointer casts
test_mmap_region_build_raw()614     fn test_mmap_region_build_raw() {
615         let addr = 0;
616         let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
617         let prot = libc::PROT_READ | libc::PROT_WRITE;
618         let flags = libc::MAP_NORESERVE | libc::MAP_PRIVATE;
619 
620         let r = unsafe { MmapRegion::build_raw((addr + 1) as *mut u8, size, prot, flags) };
621         assert_eq!(format!("{:?}", r.unwrap_err()), "InvalidPointer");
622 
623         let r = unsafe { MmapRegion::build_raw(addr as *mut u8, size, prot, flags).unwrap() };
624 
625         assert_eq!(r.size(), size);
626         assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
627         assert_eq!(r.flags(), libc::MAP_NORESERVE | libc::MAP_PRIVATE);
628         assert!(!r.owned());
629     }
630 
631     #[test]
632     #[cfg(not(miri))] // Miri cannot mmap files
test_mmap_region_fds_overlap()633     fn test_mmap_region_fds_overlap() {
634         let a = Arc::new(TempFile::new().unwrap().into_file());
635         assert_eq!(unsafe { libc::ftruncate(a.as_raw_fd(), 1024 * 10) }, 0);
636 
637         let r1 = MmapRegion::from_file(FileOffset::from_arc(a.clone(), 0), 4096).unwrap();
638         let r2 = MmapRegion::from_file(FileOffset::from_arc(a.clone(), 4096), 4096).unwrap();
639         assert!(!r1.fds_overlap(&r2));
640 
641         let r1 = MmapRegion::from_file(FileOffset::from_arc(a.clone(), 0), 5000).unwrap();
642         assert!(r1.fds_overlap(&r2));
643 
644         let r2 = MmapRegion::from_file(FileOffset::from_arc(a, 0), 1000).unwrap();
645         assert!(r1.fds_overlap(&r2));
646 
647         // Different files, so there's not overlap.
648         let new_file = TempFile::new().unwrap().into_file();
649         // Resize before mapping.
650         assert_eq!(
651             unsafe { libc::ftruncate(new_file.as_raw_fd(), 1024 * 10) },
652             0
653         );
654         let r2 = MmapRegion::from_file(FileOffset::new(new_file, 0), 5000).unwrap();
655         assert!(!r1.fds_overlap(&r2));
656 
657         // R2 is not file backed, so no overlap.
658         let r2 = MmapRegion::new(5000).unwrap();
659         assert!(!r1.fds_overlap(&r2));
660     }
661 
662     #[test]
test_dirty_tracking()663     fn test_dirty_tracking() {
664         // Using the `crate` prefix because we aliased `MmapRegion` to `MmapRegion<()>` for
665         // the rest of the unit tests above.
666         let m = crate::MmapRegion::<AtomicBitmap>::new(0x1_0000).unwrap();
667         crate::bitmap::tests::test_volatile_memory(&m);
668     }
669 }
670