1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //! The mmap module provides a safe interface to mmap memory and ensures unmap is called when the 6 //! mmap object leaves scope. 7 8 use std::cmp::min; 9 use std::collections::BTreeMap; 10 use std::fmt::{self, Display}; 11 use std::io; 12 use std::mem::{size_of, ManuallyDrop}; 13 use std::os::unix::io::AsRawFd; 14 use std::ptr::{copy_nonoverlapping, null_mut, read_unaligned, write_unaligned}; 15 16 use libc::{self, c_int, c_void, read, write}; 17 18 use data_model::volatile_memory::*; 19 use data_model::DataInit; 20 21 use crate::{errno, pagesize}; 22 23 #[derive(Debug)] 24 pub enum Error { 25 /// Requested memory out of range. 26 InvalidAddress, 27 /// Requested offset is out of range of `libc::off_t`. 28 InvalidOffset, 29 /// Requested mapping is not page aligned 30 NotPageAligned, 31 /// Overlapping regions 32 Overlapping(usize, usize), 33 /// Requested memory range spans past the end of the region. 34 InvalidRange(usize, usize, usize), 35 /// `mmap` returned the given error. 36 SystemCallFailed(errno::Error), 37 /// Writing to memory failed 38 ReadToMemory(io::Error), 39 /// Reading from memory failed 40 WriteFromMemory(io::Error), 41 } 42 pub type Result<T> = std::result::Result<T, Error>; 43 44 impl Display for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 46 use self::Error::*; 47 48 match self { 49 InvalidAddress => write!(f, "requested memory out of range"), 50 InvalidOffset => write!(f, "requested offset is out of range of off_t"), 51 NotPageAligned => write!(f, "requested memory is not page aligned"), 52 Overlapping(offset, count) => write!( 53 f, 54 "requested memory range overlaps with existing region: offset={} size={}", 55 offset, count 56 ), 57 InvalidRange(offset, count, region_size) => write!( 58 f, 59 "requested memory range spans past the end of the region: offset={} count={} region_size={}", 60 offset, count, region_size, 61 ), 62 SystemCallFailed(e) => write!(f, "mmap system call failed: {}", e), 63 ReadToMemory(e) => write!(f, "failed to read from file to memory: {}", e), 64 WriteFromMemory(e) => write!(f, "failed to write from memory to file: {}", e), 65 } 66 } 67 } 68 69 /// Memory access type for anonymous shared memory mapping. 70 #[derive(Copy, Clone, Eq, PartialEq)] 71 pub struct Protection(c_int); 72 impl Protection { 73 /// Returns Protection allowing no access. 74 #[inline(always)] none() -> Protection75 pub fn none() -> Protection { 76 Protection(libc::PROT_NONE) 77 } 78 79 /// Returns Protection allowing read/write access. 80 #[inline(always)] read_write() -> Protection81 pub fn read_write() -> Protection { 82 Protection(libc::PROT_READ | libc::PROT_WRITE) 83 } 84 85 /// Returns Protection allowing read access. 86 #[inline(always)] read() -> Protection87 pub fn read() -> Protection { 88 Protection(libc::PROT_READ) 89 } 90 91 /// Set read events. 92 #[inline(always)] set_read(self) -> Protection93 pub fn set_read(self) -> Protection { 94 Protection(self.0 | libc::PROT_READ) 95 } 96 97 /// Set write events. 98 #[inline(always)] set_write(self) -> Protection99 pub fn set_write(self) -> Protection { 100 Protection(self.0 | libc::PROT_WRITE) 101 } 102 } 103 104 impl From<c_int> for Protection { from(f: c_int) -> Self105 fn from(f: c_int) -> Self { 106 Protection(f) 107 } 108 } 109 110 impl Into<c_int> for Protection { into(self) -> c_int111 fn into(self) -> c_int { 112 self.0 113 } 114 } 115 116 /// Wraps an anonymous shared memory mapping in the current process. 117 #[derive(Debug)] 118 pub struct MemoryMapping { 119 addr: *mut u8, 120 size: usize, 121 } 122 123 // Send and Sync aren't automatically inherited for the raw address pointer. 124 // Accessing that pointer is only done through the stateless interface which 125 // allows the object to be shared by multiple threads without a decrease in 126 // safety. 127 unsafe impl Send for MemoryMapping {} 128 unsafe impl Sync for MemoryMapping {} 129 130 impl MemoryMapping { 131 /// Creates an anonymous shared, read/write mapping of `size` bytes. 132 /// 133 /// # Arguments 134 /// * `size` - Size of memory region in bytes. new(size: usize) -> Result<MemoryMapping>135 pub fn new(size: usize) -> Result<MemoryMapping> { 136 MemoryMapping::new_protection(size, Protection::read_write()) 137 } 138 139 /// Creates an anonymous shared mapping of `size` bytes with `prot` protection. 140 /// 141 /// # Arguments 142 /// * `size` - Size of memory region in bytes. 143 /// * `prot` - Protection (e.g. readable/writable) of the memory region. new_protection(size: usize, prot: Protection) -> Result<MemoryMapping>144 pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> { 145 // This is safe because we are creating an anonymous mapping in a place not already used by 146 // any other area in this process. 147 unsafe { 148 MemoryMapping::try_mmap( 149 None, 150 size, 151 prot.into(), 152 libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE, 153 None, 154 ) 155 } 156 } 157 158 /// Maps the first `size` bytes of the given `fd` as read/write. 159 /// 160 /// # Arguments 161 /// * `fd` - File descriptor to mmap from. 162 /// * `size` - Size of memory region in bytes. from_fd(fd: &dyn AsRawFd, size: usize) -> Result<MemoryMapping>163 pub fn from_fd(fd: &dyn AsRawFd, size: usize) -> Result<MemoryMapping> { 164 MemoryMapping::from_fd_offset(fd, size, 0) 165 } 166 from_fd_offset(fd: &dyn AsRawFd, size: usize, offset: usize) -> Result<MemoryMapping>167 pub fn from_fd_offset(fd: &dyn AsRawFd, size: usize, offset: usize) -> Result<MemoryMapping> { 168 MemoryMapping::from_fd_offset_protection(fd, size, offset, Protection::read_write()) 169 } 170 171 /// Maps the `size` bytes starting at `offset` bytes of the given `fd` as read/write. 172 /// 173 /// # Arguments 174 /// * `fd` - File descriptor to mmap from. 175 /// * `size` - Size of memory region in bytes. 176 /// * `offset` - Offset in bytes from the beginning of `fd` to start the mmap. 177 /// * `prot` - Protection (e.g. readable/writable) of the memory region. from_fd_offset_protection( fd: &dyn AsRawFd, size: usize, offset: usize, prot: Protection, ) -> Result<MemoryMapping>178 pub fn from_fd_offset_protection( 179 fd: &dyn AsRawFd, 180 size: usize, 181 offset: usize, 182 prot: Protection, 183 ) -> Result<MemoryMapping> { 184 // This is safe because we are creating an anonymous mapping in a place not already used by 185 // any other area in this process. 186 unsafe { 187 MemoryMapping::try_mmap( 188 None, 189 size, 190 prot.into(), 191 libc::MAP_SHARED, 192 Some((fd, offset)), 193 ) 194 } 195 } 196 197 /// Creates an anonymous shared mapping of `size` bytes with `prot` protection. 198 /// Unsafe: unmaps any mmap'd regions already present at (addr..addr+size). 199 /// 200 /// # Arguments 201 /// * `addr` - Memory address to mmap at. 202 /// * `size` - Size of memory region in bytes. 203 /// * `prot` - Protection (e.g. readable/writable) of the memory region. new_protection_fixed( addr: *mut u8, size: usize, prot: Protection, ) -> Result<MemoryMapping>204 pub unsafe fn new_protection_fixed( 205 addr: *mut u8, 206 size: usize, 207 prot: Protection, 208 ) -> Result<MemoryMapping> { 209 MemoryMapping::try_mmap( 210 Some(addr), 211 size, 212 prot.into(), 213 libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE, 214 None, 215 ) 216 } 217 218 /// Maps the `size` bytes starting at `offset` bytes of the given `fd` with 219 /// `prot` protections. 220 /// Unsafe: unmaps any mmap'd regions already present at (addr..addr+size). 221 /// 222 /// # Arguments 223 /// * `addr` - Memory address to mmap at. 224 /// * `fd` - File descriptor to mmap from. 225 /// * `size` - Size of memory region in bytes. 226 /// * `offset` - Offset in bytes from the beginning of `fd` to start the mmap. 227 /// * `prot` - Protection (e.g. readable/writable) of the memory region. from_fd_offset_protection_fixed( addr: *mut u8, fd: &dyn AsRawFd, size: usize, offset: usize, prot: Protection, ) -> Result<MemoryMapping>228 pub unsafe fn from_fd_offset_protection_fixed( 229 addr: *mut u8, 230 fd: &dyn AsRawFd, 231 size: usize, 232 offset: usize, 233 prot: Protection, 234 ) -> Result<MemoryMapping> { 235 MemoryMapping::try_mmap( 236 Some(addr), 237 size, 238 prot.into(), 239 libc::MAP_SHARED | libc::MAP_NORESERVE, 240 Some((fd, offset)), 241 ) 242 } 243 244 /// Helper wrapper around libc::mmap that does some basic validation, and calls 245 /// madvise with MADV_DONTDUMP on the created mmap try_mmap( addr: Option<*mut u8>, size: usize, prot: c_int, flags: c_int, fd: Option<(&AsRawFd, usize)>, ) -> Result<MemoryMapping>246 unsafe fn try_mmap( 247 addr: Option<*mut u8>, 248 size: usize, 249 prot: c_int, 250 flags: c_int, 251 fd: Option<(&AsRawFd, usize)>, 252 ) -> Result<MemoryMapping> { 253 let mut flags = flags; 254 // If addr is provided, set the FIXED flag, and validate addr alignment 255 let addr = match addr { 256 Some(addr) => { 257 if (addr as usize) % pagesize() != 0 { 258 return Err(Error::NotPageAligned); 259 } 260 flags |= libc::MAP_FIXED; 261 addr as *mut libc::c_void 262 } 263 None => null_mut(), 264 }; 265 // If fd is provided, validate fd offset is within bounds 266 let (fd, offset) = match fd { 267 Some((fd, offset)) => { 268 if offset > libc::off_t::max_value() as usize { 269 return Err(Error::InvalidOffset); 270 } 271 (fd.as_raw_fd(), offset as libc::off_t) 272 } 273 None => (-1, 0), 274 }; 275 let addr = libc::mmap(addr, size, prot, flags, fd, offset); 276 if addr == libc::MAP_FAILED { 277 return Err(Error::SystemCallFailed(errno::Error::last())); 278 } 279 // This is safe because we call madvise with a valid address and size, and we check the 280 // return value. We only warn about an error because failure here is not fatal to the mmap. 281 if libc::madvise(addr, size, libc::MADV_DONTDUMP) == -1 { 282 warn!( 283 "failed madvise(MADV_DONTDUMP) on mmap: {}", 284 errno::Error::last() 285 ); 286 } 287 Ok(MemoryMapping { 288 addr: addr as *mut u8, 289 size, 290 }) 291 } 292 293 /// Returns a pointer to the beginning of the memory region. Should only be 294 /// used for passing this region to ioctls for setting guest memory. as_ptr(&self) -> *mut u8295 pub fn as_ptr(&self) -> *mut u8 { 296 self.addr 297 } 298 299 /// Returns the size of the memory region in bytes. size(&self) -> usize300 pub fn size(&self) -> usize { 301 self.size 302 } 303 304 /// Calls msync with MS_SYNC on the mapping. msync(&self) -> Result<()>305 pub fn msync(&self) -> Result<()> { 306 // This is safe since we use the exact address and length of a known 307 // good memory mapping. 308 let ret = unsafe { 309 libc::msync( 310 self.as_ptr() as *mut libc::c_void, 311 self.size(), 312 libc::MS_SYNC, 313 ) 314 }; 315 if ret == -1 { 316 return Err(Error::SystemCallFailed(errno::Error::last())); 317 } 318 Ok(()) 319 } 320 321 /// Writes a slice to the memory region at the specified offset. 322 /// Returns the number of bytes written. The number of bytes written can 323 /// be less than the length of the slice if there isn't enough room in the 324 /// memory region. 325 /// 326 /// # Examples 327 /// * Write a slice at offset 256. 328 /// 329 /// ``` 330 /// # use sys_util::MemoryMapping; 331 /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); 332 /// let res = mem_map.write_slice(&[1,2,3,4,5], 256); 333 /// assert!(res.is_ok()); 334 /// assert_eq!(res.unwrap(), 5); 335 /// ``` write_slice(&self, buf: &[u8], offset: usize) -> Result<usize>336 pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> { 337 match self.size.checked_sub(offset) { 338 Some(size_past_offset) => { 339 let bytes_copied = min(size_past_offset, buf.len()); 340 // The bytes_copied equation above ensures we don't copy bytes out of range of 341 // either buf or this slice. We also know that the buffers do not overlap because 342 // slices can never occupy the same memory as a volatile slice. 343 unsafe { 344 copy_nonoverlapping(buf.as_ptr(), self.as_ptr().add(offset), bytes_copied); 345 } 346 Ok(bytes_copied) 347 } 348 None => Err(Error::InvalidAddress), 349 } 350 } 351 352 /// Reads to a slice from the memory region at the specified offset. 353 /// Returns the number of bytes read. The number of bytes read can 354 /// be less than the length of the slice if there isn't enough room in the 355 /// memory region. 356 /// 357 /// # Examples 358 /// * Read a slice of size 16 at offset 256. 359 /// 360 /// ``` 361 /// # use sys_util::MemoryMapping; 362 /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); 363 /// let buf = &mut [0u8; 16]; 364 /// let res = mem_map.read_slice(buf, 256); 365 /// assert!(res.is_ok()); 366 /// assert_eq!(res.unwrap(), 16); 367 /// ``` read_slice(&self, buf: &mut [u8], offset: usize) -> Result<usize>368 pub fn read_slice(&self, buf: &mut [u8], offset: usize) -> Result<usize> { 369 match self.size.checked_sub(offset) { 370 Some(size_past_offset) => { 371 let bytes_copied = min(size_past_offset, buf.len()); 372 // The bytes_copied equation above ensures we don't copy bytes out of range of 373 // either buf or this slice. We also know that the buffers do not overlap because 374 // slices can never occupy the same memory as a volatile slice. 375 unsafe { 376 copy_nonoverlapping( 377 self.as_ptr().add(offset) as *const u8, 378 buf.as_mut_ptr(), 379 bytes_copied, 380 ); 381 } 382 Ok(bytes_copied) 383 } 384 None => Err(Error::InvalidAddress), 385 } 386 } 387 388 /// Writes an object to the memory region at the specified offset. 389 /// Returns Ok(()) if the object fits, or Err if it extends past the end. 390 /// 391 /// # Examples 392 /// * Write a u64 at offset 16. 393 /// 394 /// ``` 395 /// # use sys_util::MemoryMapping; 396 /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); 397 /// let res = mem_map.write_obj(55u64, 16); 398 /// assert!(res.is_ok()); 399 /// ``` write_obj<T: DataInit>(&self, val: T, offset: usize) -> Result<()>400 pub fn write_obj<T: DataInit>(&self, val: T, offset: usize) -> Result<()> { 401 self.range_end(offset, size_of::<T>())?; 402 // This is safe because we checked the bounds above. 403 unsafe { 404 write_unaligned(self.as_ptr().add(offset) as *mut T, val); 405 } 406 Ok(()) 407 } 408 409 /// Reads on object from the memory region at the given offset. 410 /// Reading from a volatile area isn't strictly safe as it could change 411 /// mid-read. However, as long as the type T is plain old data and can 412 /// handle random initialization, everything will be OK. 413 /// 414 /// # Examples 415 /// * Read a u64 written to offset 32. 416 /// 417 /// ``` 418 /// # use sys_util::MemoryMapping; 419 /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); 420 /// let res = mem_map.write_obj(55u64, 32); 421 /// assert!(res.is_ok()); 422 /// let num: u64 = mem_map.read_obj(32).unwrap(); 423 /// assert_eq!(55, num); 424 /// ``` read_obj<T: DataInit>(&self, offset: usize) -> Result<T>425 pub fn read_obj<T: DataInit>(&self, offset: usize) -> Result<T> { 426 self.range_end(offset, size_of::<T>())?; 427 // This is safe because by definition Copy types can have their bits set arbitrarily and 428 // still be valid. 429 unsafe { 430 Ok(read_unaligned( 431 self.as_ptr().add(offset) as *const u8 as *const T 432 )) 433 } 434 } 435 436 /// Reads data from a file descriptor and writes it to guest memory. 437 /// 438 /// # Arguments 439 /// * `mem_offset` - Begin writing memory at this offset. 440 /// * `src` - Read from `src` to memory. 441 /// * `count` - Read `count` bytes from `src` to memory. 442 /// 443 /// # Examples 444 /// 445 /// * Read bytes from /dev/urandom 446 /// 447 /// ``` 448 /// # use sys_util::MemoryMapping; 449 /// # use std::fs::File; 450 /// # use std::path::Path; 451 /// # fn test_read_random() -> Result<u32, ()> { 452 /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); 453 /// let mut file = File::open(Path::new("/dev/urandom")).map_err(|_| ())?; 454 /// mem_map.read_to_memory(32, &mut file, 128).map_err(|_| ())?; 455 /// let rand_val: u32 = mem_map.read_obj(40).map_err(|_| ())?; 456 /// # Ok(rand_val) 457 /// # } 458 /// ``` read_to_memory( &self, mut mem_offset: usize, src: &AsRawFd, mut count: usize, ) -> Result<()>459 pub fn read_to_memory( 460 &self, 461 mut mem_offset: usize, 462 src: &AsRawFd, 463 mut count: usize, 464 ) -> Result<()> { 465 self.range_end(mem_offset, count) 466 .map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?; 467 while count > 0 { 468 // The check above ensures that no memory outside this slice will get accessed by this 469 // read call. 470 match unsafe { 471 read( 472 src.as_raw_fd(), 473 self.as_ptr().add(mem_offset) as *mut c_void, 474 count, 475 ) 476 } { 477 0 => { 478 return Err(Error::ReadToMemory(io::Error::from( 479 io::ErrorKind::UnexpectedEof, 480 ))) 481 } 482 r if r < 0 => return Err(Error::ReadToMemory(io::Error::last_os_error())), 483 ret => { 484 let bytes_read = ret as usize; 485 match count.checked_sub(bytes_read) { 486 Some(count_remaining) => count = count_remaining, 487 None => break, 488 } 489 mem_offset += ret as usize; 490 } 491 } 492 } 493 Ok(()) 494 } 495 496 /// Writes data from memory to a file descriptor. 497 /// 498 /// # Arguments 499 /// * `mem_offset` - Begin reading memory from this offset. 500 /// * `dst` - Write from memory to `dst`. 501 /// * `count` - Read `count` bytes from memory to `src`. 502 /// 503 /// # Examples 504 /// 505 /// * Write 128 bytes to /dev/null 506 /// 507 /// ``` 508 /// # use sys_util::MemoryMapping; 509 /// # use std::fs::File; 510 /// # use std::path::Path; 511 /// # fn test_write_null() -> Result<(), ()> { 512 /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); 513 /// let mut file = File::open(Path::new("/dev/null")).map_err(|_| ())?; 514 /// mem_map.write_from_memory(32, &mut file, 128).map_err(|_| ())?; 515 /// # Ok(()) 516 /// # } 517 /// ``` write_from_memory( &self, mut mem_offset: usize, dst: &AsRawFd, mut count: usize, ) -> Result<()>518 pub fn write_from_memory( 519 &self, 520 mut mem_offset: usize, 521 dst: &AsRawFd, 522 mut count: usize, 523 ) -> Result<()> { 524 self.range_end(mem_offset, count) 525 .map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?; 526 while count > 0 { 527 // The check above ensures that no memory outside this slice will get accessed by this 528 // write call. 529 match unsafe { 530 write( 531 dst.as_raw_fd(), 532 self.as_ptr().add(mem_offset) as *const c_void, 533 count, 534 ) 535 } { 536 0 => { 537 return Err(Error::WriteFromMemory(io::Error::from( 538 io::ErrorKind::WriteZero, 539 ))) 540 } 541 ret if ret < 0 => return Err(Error::WriteFromMemory(io::Error::last_os_error())), 542 ret => { 543 let bytes_written = ret as usize; 544 match count.checked_sub(bytes_written) { 545 Some(count_remaining) => count = count_remaining, 546 None => break, 547 } 548 mem_offset += ret as usize; 549 } 550 } 551 } 552 Ok(()) 553 } 554 555 /// Uses madvise to tell the kernel to remove the specified range. Subsequent reads 556 /// to the pages in the range will return zero bytes. remove_range(&self, mem_offset: usize, count: usize) -> Result<()>557 pub fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()> { 558 self.range_end(mem_offset, count) 559 .map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?; 560 let ret = unsafe { 561 // madvising away the region is the same as the guest changing it. 562 // Next time it is read, it may return zero pages. 563 libc::madvise( 564 (self.addr as usize + mem_offset) as *mut _, 565 count, 566 libc::MADV_REMOVE, 567 ) 568 }; 569 if ret < 0 { 570 Err(Error::InvalidRange(mem_offset, count, self.size())) 571 } else { 572 Ok(()) 573 } 574 } 575 576 // Check that offset+count is valid and return the sum. range_end(&self, offset: usize, count: usize) -> Result<usize>577 fn range_end(&self, offset: usize, count: usize) -> Result<usize> { 578 let mem_end = offset.checked_add(count).ok_or(Error::InvalidAddress)?; 579 if mem_end > self.size() { 580 return Err(Error::InvalidAddress); 581 } 582 Ok(mem_end) 583 } 584 } 585 586 impl VolatileMemory for MemoryMapping { get_slice(&self, offset: u64, count: u64) -> VolatileMemoryResult<VolatileSlice>587 fn get_slice(&self, offset: u64, count: u64) -> VolatileMemoryResult<VolatileSlice> { 588 let mem_end = calc_offset(offset, count)?; 589 if mem_end > self.size as u64 { 590 return Err(VolatileMemoryError::OutOfBounds { addr: mem_end }); 591 } 592 593 // Safe because we checked that offset + count was within our range and we only ever hand 594 // out volatile accessors. 595 Ok(unsafe { VolatileSlice::new((self.addr as usize + offset as usize) as *mut _, count) }) 596 } 597 } 598 599 impl Drop for MemoryMapping { drop(&mut self)600 fn drop(&mut self) { 601 // This is safe because we mmap the area at addr ourselves, and nobody 602 // else is holding a reference to it. 603 unsafe { 604 libc::munmap(self.addr as *mut libc::c_void, self.size); 605 } 606 } 607 } 608 609 /// Tracks Fixed Memory Maps within an anonymous memory-mapped fixed-sized arena 610 /// in the current process. 611 pub struct MemoryMappingArena { 612 addr: *mut u8, 613 size: usize, 614 // When doing in-place swaps of MemoryMappings, the BTreeMap returns a owned 615 // instance of the old MemoryMapping. When the old MemoryMapping falls out 616 // of scope, it calls munmap on the same region as the new MemoryMapping 617 // that was just mapped in. To avoid accidentally munmapping the new, 618 // MemoryMapping, all mappings are wrapped in a ManuallyDrop, and then 619 // "forgotten" when removed from the BTreeMap 620 maps: BTreeMap<usize, ManuallyDrop<MemoryMapping>>, 621 } 622 623 // Send and Sync aren't automatically inherited for the raw address pointer. 624 // Accessing that pointer is only done through the stateless interface which 625 // allows the object to be shared by multiple threads without a decrease in 626 // safety. 627 unsafe impl Send for MemoryMappingArena {} 628 unsafe impl Sync for MemoryMappingArena {} 629 630 impl MemoryMappingArena { 631 /// Creates an mmap arena of `size` bytes. 632 /// 633 /// # Arguments 634 /// * `size` - Size of memory region in bytes. new(size: usize) -> Result<MemoryMappingArena>635 pub fn new(size: usize) -> Result<MemoryMappingArena> { 636 // Reserve the arena's memory using an anonymous read-only mmap. 637 // The actual MemoryMapping object is forgotten, with 638 // MemoryMappingArena manually calling munmap on drop. 639 let mmap = MemoryMapping::new_protection(size, Protection::none().set_read())?; 640 let addr = mmap.as_ptr(); 641 let size = mmap.size(); 642 std::mem::forget(mmap); 643 Ok(MemoryMappingArena { 644 addr, 645 size, 646 maps: BTreeMap::new(), 647 }) 648 } 649 650 /// Anonymously maps `size` bytes at `offset` bytes from the start of the arena. 651 /// `offset` must be page aligned. 652 /// 653 /// # Arguments 654 /// * `offset` - Page aligned offset into the arena in bytes. 655 /// * `size` - Size of memory region in bytes. 656 /// * `fd` - File descriptor to mmap from. add_anon(&mut self, offset: usize, size: usize) -> Result<()>657 pub fn add_anon(&mut self, offset: usize, size: usize) -> Result<()> { 658 self.try_add(offset, size, Protection::read_write(), None) 659 } 660 661 /// Maps `size` bytes from the start of the given `fd` at `offset` bytes from 662 /// the start of the arena. `offset` must be page aligned. 663 /// 664 /// # Arguments 665 /// * `offset` - Page aligned offset into the arena in bytes. 666 /// * `size` - Size of memory region in bytes. 667 /// * `fd` - File descriptor to mmap from. add_fd(&mut self, offset: usize, size: usize, fd: &dyn AsRawFd) -> Result<()>668 pub fn add_fd(&mut self, offset: usize, size: usize, fd: &dyn AsRawFd) -> Result<()> { 669 self.add_fd_offset(offset, size, fd, 0) 670 } 671 672 /// Maps `size` bytes starting at `fs_offset` bytes from within the given `fd` 673 /// at `offset` bytes from the start of the arena. `offset` must be page aligned. 674 /// 675 /// # Arguments 676 /// * `offset` - Page aligned offset into the arena in bytes. 677 /// * `size` - Size of memory region in bytes. 678 /// * `fd` - File descriptor to mmap from. 679 /// * `fd_offset` - Offset in bytes from the beginning of `fd` to start the mmap. add_fd_offset( &mut self, offset: usize, size: usize, fd: &dyn AsRawFd, fd_offset: usize, ) -> Result<()>680 pub fn add_fd_offset( 681 &mut self, 682 offset: usize, 683 size: usize, 684 fd: &dyn AsRawFd, 685 fd_offset: usize, 686 ) -> Result<()> { 687 self.add_fd_offset_protection(offset, size, fd, fd_offset, Protection::read_write()) 688 } 689 690 /// Maps `size` bytes starting at `fs_offset` bytes from within the given `fd` 691 /// at `offset` bytes from the start of the arena with `prot` protections. 692 /// `offset` must be page aligned. 693 /// 694 /// # Arguments 695 /// * `offset` - Page aligned offset into the arena in bytes. 696 /// * `size` - Size of memory region in bytes. 697 /// * `fd` - File descriptor to mmap from. 698 /// * `fd_offset` - Offset in bytes from the beginning of `fd` to start the mmap. 699 /// * `prot` - Protection (e.g. readable/writable) of the memory region. add_fd_offset_protection( &mut self, offset: usize, size: usize, fd: &dyn AsRawFd, fd_offset: usize, prot: Protection, ) -> Result<()>700 pub fn add_fd_offset_protection( 701 &mut self, 702 offset: usize, 703 size: usize, 704 fd: &dyn AsRawFd, 705 fd_offset: usize, 706 prot: Protection, 707 ) -> Result<()> { 708 self.try_add(offset, size, prot, Some((fd, fd_offset))) 709 } 710 711 /// Helper method that calls appropriate MemoryMapping constructor and adds 712 /// the resulting map into the arena. try_add( &mut self, offset: usize, size: usize, prot: Protection, fd: Option<(&AsRawFd, usize)>, ) -> Result<()>713 fn try_add( 714 &mut self, 715 offset: usize, 716 size: usize, 717 prot: Protection, 718 fd: Option<(&AsRawFd, usize)>, 719 ) -> Result<()> { 720 self.validate_range(offset, size)?; 721 722 // This is safe since the range has been validated. 723 let mmap = unsafe { 724 match fd { 725 Some((fd, fd_offset)) => MemoryMapping::from_fd_offset_protection_fixed( 726 (self.addr as usize + offset) as *mut u8, 727 fd, 728 size, 729 fd_offset, 730 prot, 731 )?, 732 None => MemoryMapping::new_protection_fixed( 733 (self.addr as usize + offset) as *mut u8, 734 size, 735 prot, 736 )?, 737 } 738 }; 739 740 self.maps.insert(offset, ManuallyDrop::new(mmap)); 741 Ok(()) 742 } 743 744 /// Removes a mapping at `offset` from the start of the arena. 745 /// Returns a boolean indicating if there was a mapping present at `offset`. 746 /// If none was present, this method is a noop. remove(&mut self, offset: usize) -> Result<bool>747 pub fn remove(&mut self, offset: usize) -> Result<bool> { 748 if let Some(mmap) = self.maps.remove(&offset) { 749 // Instead of munmapping the memory map, leaving an unprotected hole 750 // in the arena, swap this mmap with an anonymous protection. 751 // This is safe since the memory mapping perfectly overlaps with an 752 // existing, known good memory mapping. 753 let mmap = unsafe { 754 MemoryMapping::new_protection_fixed( 755 mmap.as_ptr(), 756 mmap.size(), 757 Protection::none().set_read(), 758 )? 759 }; 760 self.maps.insert(offset, ManuallyDrop::new(mmap)); 761 Ok(true) 762 } else { 763 Ok(false) 764 } 765 } 766 767 /// Calls msync with MS_SYNC on the mapping at `offset` from the start of 768 /// the arena. 769 /// Returns a boolean indicating if there was a mapping present at `offset`. 770 /// If none was present, this method is a noop. msync(&self, offset: usize) -> Result<bool>771 pub fn msync(&self, offset: usize) -> Result<bool> { 772 if let Some(mmap) = self.maps.get(&offset) { 773 mmap.msync()?; 774 Ok(true) 775 } else { 776 Ok(false) 777 } 778 } 779 780 /// Returns a pointer to the beginning of the memory region. Should only be 781 /// used for passing this region to ioctls for setting guest memory. as_ptr(&self) -> *mut u8782 pub fn as_ptr(&self) -> *mut u8 { 783 self.addr 784 } 785 786 /// Returns the size of the memory region in bytes. size(&self) -> usize787 pub fn size(&self) -> usize { 788 self.size 789 } 790 791 /// Validates `offset` and `size`. 792 /// Checks that offset..offset+size doesn't overlap with existing mappings. 793 /// Also ensures correct alignment, and checks for any overflow. 794 /// Note: offset..offset+size is considered valid if it _perfectly_ overlaps 795 /// with single other region. validate_range(&self, offset: usize, size: usize) -> Result<()>796 fn validate_range(&self, offset: usize, size: usize) -> Result<()> { 797 // Ensure offset is page-aligned 798 if offset % pagesize() != 0 { 799 return Err(Error::NotPageAligned); 800 } 801 // Ensure offset + size doesn't overflow 802 let end_offset = offset.checked_add(size).ok_or(Error::InvalidAddress)?; 803 // Ensure offset + size are within the arena bounds 804 if end_offset > self.size { 805 return Err(Error::InvalidAddress); 806 } 807 // Ensure offset..offset+size doesn't overlap with existing regions 808 // Find the offset + size of the first mapping before the desired offset 809 let (prev_offset, prev_size) = match self.maps.range(..offset).rev().next() { 810 Some((offset, mmap)) => (*offset, mmap.size()), 811 None => { 812 // Empty map 813 return Ok(()); 814 } 815 }; 816 if offset == prev_offset { 817 // Perfectly overlapping regions are allowed 818 if size != prev_size { 819 return Err(Error::Overlapping(offset, size)); 820 } 821 } else if offset < (prev_offset + prev_size) { 822 return Err(Error::Overlapping(offset, size)); 823 } 824 825 Ok(()) 826 } 827 } 828 829 impl Drop for MemoryMappingArena { drop(&mut self)830 fn drop(&mut self) { 831 // This is safe because we mmap the area at addr ourselves, and nobody 832 // else is holding a reference to it. 833 unsafe { 834 libc::munmap(self.addr as *mut libc::c_void, self.size); 835 } 836 } 837 } 838 839 #[cfg(test)] 840 mod tests { 841 use super::*; 842 use data_model::{VolatileMemory, VolatileMemoryError}; 843 use std::os::unix::io::FromRawFd; 844 845 #[test] basic_map()846 fn basic_map() { 847 let m = MemoryMapping::new(1024).unwrap(); 848 assert_eq!(1024, m.size()); 849 } 850 851 #[test] map_invalid_size()852 fn map_invalid_size() { 853 let res = MemoryMapping::new(0).unwrap_err(); 854 if let Error::SystemCallFailed(e) = res { 855 assert_eq!(e.errno(), libc::EINVAL); 856 } else { 857 panic!("unexpected error: {}", res); 858 } 859 } 860 861 #[test] map_invalid_fd()862 fn map_invalid_fd() { 863 let fd = unsafe { std::fs::File::from_raw_fd(-1) }; 864 let res = MemoryMapping::from_fd(&fd, 1024).unwrap_err(); 865 if let Error::SystemCallFailed(e) = res { 866 assert_eq!(e.errno(), libc::EBADF); 867 } else { 868 panic!("unexpected error: {}", res); 869 } 870 } 871 872 #[test] test_write_past_end()873 fn test_write_past_end() { 874 let m = MemoryMapping::new(5).unwrap(); 875 let res = m.write_slice(&[1, 2, 3, 4, 5, 6], 0); 876 assert!(res.is_ok()); 877 assert_eq!(res.unwrap(), 5); 878 } 879 880 #[test] slice_size()881 fn slice_size() { 882 let m = MemoryMapping::new(5).unwrap(); 883 let s = m.get_slice(2, 3).unwrap(); 884 assert_eq!(s.size(), 3); 885 } 886 887 #[test] slice_addr()888 fn slice_addr() { 889 let m = MemoryMapping::new(5).unwrap(); 890 let s = m.get_slice(2, 3).unwrap(); 891 assert_eq!(s.as_ptr(), unsafe { m.as_ptr().offset(2) }); 892 } 893 894 #[test] slice_store()895 fn slice_store() { 896 let m = MemoryMapping::new(5).unwrap(); 897 let r = m.get_ref(2).unwrap(); 898 r.store(9u16); 899 assert_eq!(m.read_obj::<u16>(2).unwrap(), 9); 900 } 901 902 #[test] slice_overflow_error()903 fn slice_overflow_error() { 904 let m = MemoryMapping::new(5).unwrap(); 905 let res = m.get_slice(std::u64::MAX, 3).unwrap_err(); 906 assert_eq!( 907 res, 908 VolatileMemoryError::Overflow { 909 base: std::u64::MAX, 910 offset: 3, 911 } 912 ); 913 } 914 #[test] slice_oob_error()915 fn slice_oob_error() { 916 let m = MemoryMapping::new(5).unwrap(); 917 let res = m.get_slice(3, 3).unwrap_err(); 918 assert_eq!(res, VolatileMemoryError::OutOfBounds { addr: 6 }); 919 } 920 921 #[test] from_fd_offset_invalid()922 fn from_fd_offset_invalid() { 923 let fd = unsafe { std::fs::File::from_raw_fd(-1) }; 924 let res = MemoryMapping::from_fd_offset(&fd, 4096, (libc::off_t::max_value() as usize) + 1) 925 .unwrap_err(); 926 match res { 927 Error::InvalidOffset => {} 928 e => panic!("unexpected error: {}", e), 929 } 930 } 931 932 #[test] arena_new()933 fn arena_new() { 934 let m = MemoryMappingArena::new(0x40000).unwrap(); 935 assert_eq!(m.size(), 0x40000); 936 } 937 938 #[test] arena_add()939 fn arena_add() { 940 let mut m = MemoryMappingArena::new(0x40000).unwrap(); 941 assert!(m.add_anon(0, pagesize() * 4).is_ok()); 942 } 943 944 #[test] arena_remove()945 fn arena_remove() { 946 let mut m = MemoryMappingArena::new(0x40000).unwrap(); 947 assert!(m.add_anon(0, pagesize() * 4).is_ok()); 948 assert!(m.remove(0).unwrap(), true); 949 assert!(m.remove(0).unwrap(), false); 950 } 951 952 #[test] arena_add_overlap_error()953 fn arena_add_overlap_error() { 954 let page = pagesize(); 955 let mut m = MemoryMappingArena::new(page * 4).unwrap(); 956 assert!(m.add_anon(0, page * 4).is_ok()); 957 let res = m.add_anon(page, page).unwrap_err(); 958 match res { 959 Error::Overlapping(a, o) => { 960 assert_eq!((a, o), (page, page)); 961 } 962 e => panic!("unexpected error: {}", e), 963 } 964 } 965 966 #[test] arena_add_alignment_error()967 fn arena_add_alignment_error() { 968 let mut m = MemoryMappingArena::new(pagesize() * 2).unwrap(); 969 assert!(m.add_anon(0, 0x100).is_ok()); 970 let res = m.add_anon(pagesize() + 1, 0x100).unwrap_err(); 971 match res { 972 Error::NotPageAligned => {} 973 e => panic!("unexpected error: {}", e), 974 } 975 } 976 977 #[test] arena_add_oob_error()978 fn arena_add_oob_error() { 979 let mut m = MemoryMappingArena::new(pagesize()).unwrap(); 980 let res = m.add_anon(0, pagesize() + 1).unwrap_err(); 981 match res { 982 Error::InvalidAddress => {} 983 e => panic!("unexpected error: {}", e), 984 } 985 } 986 } 987