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