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::fmt::{self, Display};
10 use std::io;
11 use std::mem::size_of;
12 use std::os::unix::io::AsRawFd;
13 use std::ptr::{copy_nonoverlapping, null_mut, read_unaligned, write_unaligned};
14 
15 use libc::{self, c_int, c_void, read, write};
16 
17 use data_model::volatile_memory::*;
18 use data_model::DataInit;
19 
20 use crate::{errno, pagesize};
21 
22 #[derive(Debug)]
23 pub enum Error {
24     /// `add_fd_mapping` is not supported.
25     AddFdMappingIsUnsupported,
26     /// Requested memory out of range.
27     InvalidAddress,
28     /// Invalid argument provided when building mmap.
29     InvalidArgument,
30     /// Requested offset is out of range of `libc::off_t`.
31     InvalidOffset,
32     /// Requested mapping is not page aligned
33     NotPageAligned,
34     /// Requested memory range spans past the end of the region.
35     InvalidRange(usize, usize, usize),
36     /// `mmap` returned the given error.
37     SystemCallFailed(errno::Error),
38     /// Writing to memory failed
39     ReadToMemory(io::Error),
40     /// `remove_mapping` is not supported
41     RemoveMappingIsUnsupported,
42     /// Reading from memory failed
43     WriteFromMemory(io::Error),
44 }
45 pub type Result<T> = std::result::Result<T, Error>;
46 
47 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result48     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49         use self::Error::*;
50 
51         match self {
52             AddFdMappingIsUnsupported => write!(f, "`add_fd_mapping` is unsupported"),
53             InvalidAddress => write!(f, "requested memory out of range"),
54             InvalidArgument => write!(f, "invalid argument provided when creating mapping"),
55             InvalidOffset => write!(f, "requested offset is out of range of off_t"),
56             NotPageAligned => write!(f, "requested memory is not page aligned"),
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 related system call failed: {}", e),
63             ReadToMemory(e) => write!(f, "failed to read from file to memory: {}", e),
64             RemoveMappingIsUnsupported => write!(f, "`remove_mapping` is unsupported"),
65             WriteFromMemory(e) => write!(f, "failed to write from memory to file: {}", e),
66         }
67     }
68 }
69 
70 /// Memory access type for anonymous shared memory mapping.
71 #[derive(Copy, Clone, Eq, PartialEq)]
72 pub struct Protection(c_int);
73 impl Protection {
74     /// Returns Protection allowing no access.
75     #[inline(always)]
none() -> Protection76     pub fn none() -> Protection {
77         Protection(libc::PROT_NONE)
78     }
79 
80     /// Returns Protection allowing read/write access.
81     #[inline(always)]
read_write() -> Protection82     pub fn read_write() -> Protection {
83         Protection(libc::PROT_READ | libc::PROT_WRITE)
84     }
85 
86     /// Returns Protection allowing read access.
87     #[inline(always)]
read() -> Protection88     pub fn read() -> Protection {
89         Protection(libc::PROT_READ)
90     }
91 
92     /// Set read events.
93     #[inline(always)]
set_read(self) -> Protection94     pub fn set_read(self) -> Protection {
95         Protection(self.0 | libc::PROT_READ)
96     }
97 
98     /// Set write events.
99     #[inline(always)]
set_write(self) -> Protection100     pub fn set_write(self) -> Protection {
101         Protection(self.0 | libc::PROT_WRITE)
102     }
103 }
104 
105 impl From<c_int> for Protection {
from(f: c_int) -> Self106     fn from(f: c_int) -> Self {
107         Protection(f)
108     }
109 }
110 
111 impl From<Protection> for c_int {
from(p: Protection) -> c_int112     fn from(p: Protection) -> c_int {
113         p.0
114     }
115 }
116 
117 /// Validates that `offset`..`offset+range_size` lies within the bounds of a memory mapping of
118 /// `mmap_size` bytes.  Also checks for any overflow.
validate_includes_range(mmap_size: usize, offset: usize, range_size: usize) -> Result<()>119 fn validate_includes_range(mmap_size: usize, offset: usize, range_size: usize) -> Result<()> {
120     // Ensure offset + size doesn't overflow
121     let end_offset = offset
122         .checked_add(range_size)
123         .ok_or(Error::InvalidAddress)?;
124     // Ensure offset + size are within the mapping bounds
125     if end_offset <= mmap_size {
126         Ok(())
127     } else {
128         Err(Error::InvalidAddress)
129     }
130 }
131 
132 /// A range of memory that can be msynced, for abstracting over different types of memory mappings.
133 ///
134 /// Safe when implementers guarantee `ptr`..`ptr+size` is an mmaped region owned by this object that
135 /// can't be unmapped during the `MappedRegion`'s lifetime.
136 pub unsafe trait MappedRegion: Send + Sync {
137     /// Returns a pointer to the beginning of the memory region. Should only be
138     /// used for passing this region to ioctls for setting guest memory.
as_ptr(&self) -> *mut u8139     fn as_ptr(&self) -> *mut u8;
140 
141     /// Returns the size of the memory region in bytes.
size(&self) -> usize142     fn size(&self) -> usize;
143 
144     /// Maps `size` bytes starting at `fd_offset` bytes from within the given `fd`
145     /// at `offset` bytes from the start of the region with `prot` protections.
146     /// `offset` must be page aligned.
147     ///
148     /// # Arguments
149     /// * `offset` - Page aligned offset into the arena in bytes.
150     /// * `size` - Size of memory region in bytes.
151     /// * `fd` - File descriptor to mmap from.
152     /// * `fd_offset` - Offset in bytes from the beginning of `fd` to start the mmap.
153     /// * `prot` - Protection (e.g. readable/writable) of the memory region.
add_fd_mapping( &mut self, _offset: usize, _size: usize, _fd: &dyn AsRawFd, _fd_offset: u64, _prot: Protection, ) -> Result<()>154     fn add_fd_mapping(
155         &mut self,
156         _offset: usize,
157         _size: usize,
158         _fd: &dyn AsRawFd,
159         _fd_offset: u64,
160         _prot: Protection,
161     ) -> Result<()> {
162         Err(Error::AddFdMappingIsUnsupported)
163     }
164 
165     /// Remove `size`-byte mapping starting at `offset`.
remove_mapping(&mut self, _offset: usize, _size: usize) -> Result<()>166     fn remove_mapping(&mut self, _offset: usize, _size: usize) -> Result<()> {
167         Err(Error::RemoveMappingIsUnsupported)
168     }
169 }
170 
171 impl dyn MappedRegion {
172     /// Calls msync with MS_SYNC on a mapping of `size` bytes starting at `offset` from the start of
173     /// the region.  `offset`..`offset+size` must be contained within the `MappedRegion`.
msync(&self, offset: usize, size: usize) -> Result<()>174     pub fn msync(&self, offset: usize, size: usize) -> Result<()> {
175         validate_includes_range(self.size(), offset, size)?;
176 
177         // Safe because the MemoryMapping/MemoryMappingArena interface ensures our pointer and size
178         // are correct, and we've validated that `offset`..`offset+size` is in the range owned by
179         // this `MappedRegion`.
180         let ret = unsafe {
181             libc::msync(
182                 (self.as_ptr() as usize + offset) as *mut libc::c_void,
183                 size,
184                 libc::MS_SYNC,
185             )
186         };
187         if ret != -1 {
188             Ok(())
189         } else {
190             Err(Error::SystemCallFailed(errno::Error::last()))
191         }
192     }
193 }
194 
195 /// Wraps an anonymous shared memory mapping in the current process.
196 #[derive(Debug)]
197 pub struct MemoryMapping {
198     addr: *mut u8,
199     size: usize,
200 }
201 
202 // Send and Sync aren't automatically inherited for the raw address pointer.
203 // Accessing that pointer is only done through the stateless interface which
204 // allows the object to be shared by multiple threads without a decrease in
205 // safety.
206 unsafe impl Send for MemoryMapping {}
207 unsafe impl Sync for MemoryMapping {}
208 
209 impl MemoryMapping {
210     /// Creates an anonymous shared, read/write mapping of `size` bytes.
211     ///
212     /// # Arguments
213     /// * `size` - Size of memory region in bytes.
new(size: usize) -> Result<MemoryMapping>214     pub fn new(size: usize) -> Result<MemoryMapping> {
215         MemoryMapping::new_protection(size, Protection::read_write())
216     }
217 
218     /// Creates an anonymous shared mapping of `size` bytes with `prot` protection.
219     ///
220     /// # Arguments
221     /// * `size` - Size of memory region in bytes.
222     /// * `prot` - Protection (e.g. readable/writable) of the memory region.
new_protection(size: usize, prot: Protection) -> Result<MemoryMapping>223     pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> {
224         // This is safe because we are creating an anonymous mapping in a place not already used by
225         // any other area in this process.
226         unsafe {
227             MemoryMapping::try_mmap(
228                 None,
229                 size,
230                 prot.into(),
231                 libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE,
232                 None,
233             )
234         }
235     }
236 
237     /// Maps the first `size` bytes of the given `fd` as read/write.
238     ///
239     /// # Arguments
240     /// * `fd` - File descriptor to mmap from.
241     /// * `size` - Size of memory region in bytes.
from_fd(fd: &dyn AsRawFd, size: usize) -> Result<MemoryMapping>242     pub fn from_fd(fd: &dyn AsRawFd, size: usize) -> Result<MemoryMapping> {
243         MemoryMapping::from_fd_offset(fd, size, 0)
244     }
245 
from_fd_offset(fd: &dyn AsRawFd, size: usize, offset: u64) -> Result<MemoryMapping>246     pub fn from_fd_offset(fd: &dyn AsRawFd, size: usize, offset: u64) -> Result<MemoryMapping> {
247         MemoryMapping::from_fd_offset_protection(fd, size, offset, Protection::read_write())
248     }
249 
250     /// Maps the `size` bytes starting at `offset` bytes of the given `fd` as read/write.
251     ///
252     /// # Arguments
253     /// * `fd` - File descriptor to mmap from.
254     /// * `size` - Size of memory region in bytes.
255     /// * `offset` - Offset in bytes from the beginning of `fd` to start the mmap.
256     /// * `flags` - flags passed directly to mmap.
257     /// * `prot` - Protection (e.g. readable/writable) of the memory region.
from_fd_offset_flags( fd: &dyn AsRawFd, size: usize, offset: u64, flags: c_int, prot: Protection, ) -> Result<MemoryMapping>258     fn from_fd_offset_flags(
259         fd: &dyn AsRawFd,
260         size: usize,
261         offset: u64,
262         flags: c_int,
263         prot: Protection,
264     ) -> Result<MemoryMapping> {
265         unsafe {
266             // This is safe because we are creating an anonymous mapping in a place not already used
267             // by any other area in this process.
268             MemoryMapping::try_mmap(None, size, prot.into(), flags, Some((fd, offset)))
269         }
270     }
271 
272     /// Maps the `size` bytes starting at `offset` bytes of the given `fd` as read/write.
273     ///
274     /// # Arguments
275     /// * `fd` - File descriptor to mmap from.
276     /// * `size` - Size of memory region in bytes.
277     /// * `offset` - Offset in bytes from the beginning of `fd` to start the mmap.
278     /// * `prot` - Protection (e.g. readable/writable) of the memory region.
from_fd_offset_protection( fd: &dyn AsRawFd, size: usize, offset: u64, prot: Protection, ) -> Result<MemoryMapping>279     pub fn from_fd_offset_protection(
280         fd: &dyn AsRawFd,
281         size: usize,
282         offset: u64,
283         prot: Protection,
284     ) -> Result<MemoryMapping> {
285         MemoryMapping::from_fd_offset_flags(fd, size, offset, libc::MAP_SHARED, prot)
286     }
287 
288     /// Maps `size` bytes starting at `offset` from the given `fd` as read/write, and requests
289     /// that the pages are pre-populated.
290     /// # Arguments
291     /// * `fd` - File descriptor to mmap from.
292     /// * `size` - Size of memory region in bytes.
293     /// * `offset` - Offset in bytes from the beginning of `fd` to start the mmap.
from_fd_offset_protection_populate( fd: &dyn AsRawFd, size: usize, offset: u64, prot: Protection, populate: bool, ) -> Result<MemoryMapping>294     pub fn from_fd_offset_protection_populate(
295         fd: &dyn AsRawFd,
296         size: usize,
297         offset: u64,
298         prot: Protection,
299         populate: bool,
300     ) -> Result<MemoryMapping> {
301         let mut flags = libc::MAP_SHARED;
302         if populate {
303             flags |= libc::MAP_POPULATE;
304         }
305         MemoryMapping::from_fd_offset_flags(fd, size, offset, flags, prot)
306     }
307 
308     /// Creates an anonymous shared mapping of `size` bytes with `prot` protection.
309     ///
310     /// # Arguments
311     ///
312     /// * `addr` - Memory address to mmap at.
313     /// * `size` - Size of memory region in bytes.
314     /// * `prot` - Protection (e.g. readable/writable) of the memory region.
315     ///
316     /// # Safety
317     ///
318     /// This function should not be called before the caller unmaps any mmap'd regions already
319     /// present at `(addr..addr+size)`.
new_protection_fixed( addr: *mut u8, size: usize, prot: Protection, ) -> Result<MemoryMapping>320     pub unsafe fn new_protection_fixed(
321         addr: *mut u8,
322         size: usize,
323         prot: Protection,
324     ) -> Result<MemoryMapping> {
325         MemoryMapping::try_mmap(
326             Some(addr),
327             size,
328             prot.into(),
329             libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE,
330             None,
331         )
332     }
333 
334     /// Maps the `size` bytes starting at `offset` bytes of the given `fd` with
335     /// `prot` protections.
336     ///
337     /// # Arguments
338     ///
339     /// * `addr` - Memory address to mmap at.
340     /// * `fd` - File descriptor to mmap from.
341     /// * `size` - Size of memory region in bytes.
342     /// * `offset` - Offset in bytes from the beginning of `fd` to start the mmap.
343     /// * `prot` - Protection (e.g. readable/writable) of the memory region.
344     ///
345     /// # Safety
346     ///
347     /// This function should not be called before the caller unmaps any mmap'd regions already
348     /// present at `(addr..addr+size)`.
from_fd_offset_protection_fixed( addr: *mut u8, fd: &dyn AsRawFd, size: usize, offset: u64, prot: Protection, ) -> Result<MemoryMapping>349     pub unsafe fn from_fd_offset_protection_fixed(
350         addr: *mut u8,
351         fd: &dyn AsRawFd,
352         size: usize,
353         offset: u64,
354         prot: Protection,
355     ) -> Result<MemoryMapping> {
356         MemoryMapping::try_mmap(
357             Some(addr),
358             size,
359             prot.into(),
360             libc::MAP_SHARED | libc::MAP_NORESERVE,
361             Some((fd, offset)),
362         )
363     }
364 
365     /// Helper wrapper around libc::mmap that does some basic validation, and calls
366     /// madvise with MADV_DONTDUMP on the created mmap
try_mmap( addr: Option<*mut u8>, size: usize, prot: c_int, flags: c_int, fd: Option<(&dyn AsRawFd, u64)>, ) -> Result<MemoryMapping>367     unsafe fn try_mmap(
368         addr: Option<*mut u8>,
369         size: usize,
370         prot: c_int,
371         flags: c_int,
372         fd: Option<(&dyn AsRawFd, u64)>,
373     ) -> Result<MemoryMapping> {
374         let mut flags = flags;
375         // If addr is provided, set the FIXED flag, and validate addr alignment
376         let addr = match addr {
377             Some(addr) => {
378                 if (addr as usize) % pagesize() != 0 {
379                     return Err(Error::NotPageAligned);
380                 }
381                 flags |= libc::MAP_FIXED;
382                 addr as *mut libc::c_void
383             }
384             None => null_mut(),
385         };
386         // If fd is provided, validate fd offset is within bounds
387         let (fd, offset) = match fd {
388             Some((fd, offset)) => {
389                 if offset > libc::off_t::max_value() as u64 {
390                     return Err(Error::InvalidOffset);
391                 }
392                 (fd.as_raw_fd(), offset as libc::off_t)
393             }
394             None => (-1, 0),
395         };
396         let addr = libc::mmap(addr, size, prot, flags, fd, offset);
397         if addr == libc::MAP_FAILED {
398             return Err(Error::SystemCallFailed(errno::Error::last()));
399         }
400         // This is safe because we call madvise with a valid address and size, and we check the
401         // return value. We only warn about an error because failure here is not fatal to the mmap.
402         if libc::madvise(addr, size, libc::MADV_DONTDUMP) == -1 {
403             warn!(
404                 "failed madvise(MADV_DONTDUMP) on mmap: {}",
405                 errno::Error::last()
406             );
407         }
408         Ok(MemoryMapping {
409             addr: addr as *mut u8,
410             size,
411         })
412     }
413 
414     /// Madvise the kernel to use Huge Pages for this mapping.
use_hugepages(&self) -> Result<()>415     pub fn use_hugepages(&self) -> Result<()> {
416         const SZ_2M: usize = 2 * 1024 * 1024;
417 
418         // THP uses 2M pages, so use THP only on mappings that are at least
419         // 2M in size.
420         if self.size() < SZ_2M {
421             return Ok(());
422         }
423 
424         // This is safe because we call madvise with a valid address and size, and we check the
425         // return value.
426         let ret = unsafe {
427             libc::madvise(
428                 self.as_ptr() as *mut libc::c_void,
429                 self.size(),
430                 libc::MADV_HUGEPAGE,
431             )
432         };
433         if ret == -1 {
434             Err(Error::SystemCallFailed(errno::Error::last()))
435         } else {
436             Ok(())
437         }
438     }
439 
440     /// Calls msync with MS_SYNC on the mapping.
msync(&self) -> Result<()>441     pub fn msync(&self) -> Result<()> {
442         // This is safe since we use the exact address and length of a known
443         // good memory mapping.
444         let ret = unsafe {
445             libc::msync(
446                 self.as_ptr() as *mut libc::c_void,
447                 self.size(),
448                 libc::MS_SYNC,
449             )
450         };
451         if ret == -1 {
452             return Err(Error::SystemCallFailed(errno::Error::last()));
453         }
454         Ok(())
455     }
456 
457     /// Writes a slice to the memory region at the specified offset.
458     /// Returns the number of bytes written.  The number of bytes written can
459     /// be less than the length of the slice if there isn't enough room in the
460     /// memory region.
461     ///
462     /// # Examples
463     /// * Write a slice at offset 256.
464     ///
465     /// ```
466     /// #   use sys_util::MemoryMapping;
467     /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
468     ///     let res = mem_map.write_slice(&[1,2,3,4,5], 256);
469     ///     assert!(res.is_ok());
470     ///     assert_eq!(res.unwrap(), 5);
471     /// ```
write_slice(&self, buf: &[u8], offset: usize) -> Result<usize>472     pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> {
473         match self.size.checked_sub(offset) {
474             Some(size_past_offset) => {
475                 let bytes_copied = min(size_past_offset, buf.len());
476                 // The bytes_copied equation above ensures we don't copy bytes out of range of
477                 // either buf or this slice. We also know that the buffers do not overlap because
478                 // slices can never occupy the same memory as a volatile slice.
479                 unsafe {
480                     copy_nonoverlapping(buf.as_ptr(), self.as_ptr().add(offset), bytes_copied);
481                 }
482                 Ok(bytes_copied)
483             }
484             None => Err(Error::InvalidAddress),
485         }
486     }
487 
488     /// Reads to a slice from the memory region at the specified offset.
489     /// Returns the number of bytes read.  The number of bytes read can
490     /// be less than the length of the slice if there isn't enough room in the
491     /// memory region.
492     ///
493     /// # Examples
494     /// * Read a slice of size 16 at offset 256.
495     ///
496     /// ```
497     /// #   use sys_util::MemoryMapping;
498     /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
499     ///     let buf = &mut [0u8; 16];
500     ///     let res = mem_map.read_slice(buf, 256);
501     ///     assert!(res.is_ok());
502     ///     assert_eq!(res.unwrap(), 16);
503     /// ```
read_slice(&self, buf: &mut [u8], offset: usize) -> Result<usize>504     pub fn read_slice(&self, buf: &mut [u8], offset: usize) -> Result<usize> {
505         match self.size.checked_sub(offset) {
506             Some(size_past_offset) => {
507                 let bytes_copied = min(size_past_offset, buf.len());
508                 // The bytes_copied equation above ensures we don't copy bytes out of range of
509                 // either buf or this slice. We also know that the buffers do not overlap because
510                 // slices can never occupy the same memory as a volatile slice.
511                 unsafe {
512                     copy_nonoverlapping(
513                         self.as_ptr().add(offset) as *const u8,
514                         buf.as_mut_ptr(),
515                         bytes_copied,
516                     );
517                 }
518                 Ok(bytes_copied)
519             }
520             None => Err(Error::InvalidAddress),
521         }
522     }
523 
524     /// Writes an object to the memory region at the specified offset.
525     /// Returns Ok(()) if the object fits, or Err if it extends past the end.
526     ///
527     /// # Examples
528     /// * Write a u64 at offset 16.
529     ///
530     /// ```
531     /// #   use sys_util::MemoryMapping;
532     /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
533     ///     let res = mem_map.write_obj(55u64, 16);
534     ///     assert!(res.is_ok());
535     /// ```
write_obj<T: DataInit>(&self, val: T, offset: usize) -> Result<()>536     pub fn write_obj<T: DataInit>(&self, val: T, offset: usize) -> Result<()> {
537         self.range_end(offset, size_of::<T>())?;
538         // This is safe because we checked the bounds above.
539         unsafe {
540             write_unaligned(self.as_ptr().add(offset) as *mut T, val);
541         }
542         Ok(())
543     }
544 
545     /// Reads on object from the memory region at the given offset.
546     /// Reading from a volatile area isn't strictly safe as it could change
547     /// mid-read.  However, as long as the type T is plain old data and can
548     /// handle random initialization, everything will be OK.
549     ///
550     /// # Examples
551     /// * Read a u64 written to offset 32.
552     ///
553     /// ```
554     /// #   use sys_util::MemoryMapping;
555     /// #   let mut mem_map = MemoryMapping::new(1024).unwrap();
556     ///     let res = mem_map.write_obj(55u64, 32);
557     ///     assert!(res.is_ok());
558     ///     let num: u64 = mem_map.read_obj(32).unwrap();
559     ///     assert_eq!(55, num);
560     /// ```
read_obj<T: DataInit>(&self, offset: usize) -> Result<T>561     pub fn read_obj<T: DataInit>(&self, offset: usize) -> Result<T> {
562         self.range_end(offset, size_of::<T>())?;
563         // This is safe because by definition Copy types can have their bits set arbitrarily and
564         // still be valid.
565         unsafe {
566             Ok(read_unaligned(
567                 self.as_ptr().add(offset) as *const u8 as *const T
568             ))
569         }
570     }
571 
572     /// Reads data from a file descriptor and writes it to guest memory.
573     ///
574     /// # Arguments
575     /// * `mem_offset` - Begin writing memory at this offset.
576     /// * `src` - Read from `src` to memory.
577     /// * `count` - Read `count` bytes from `src` to memory.
578     ///
579     /// # Examples
580     ///
581     /// * Read bytes from /dev/urandom
582     ///
583     /// ```
584     /// # use sys_util::MemoryMapping;
585     /// # use std::fs::File;
586     /// # use std::path::Path;
587     /// # fn test_read_random() -> Result<u32, ()> {
588     /// #     let mut mem_map = MemoryMapping::new(1024).unwrap();
589     ///       let mut file = File::open(Path::new("/dev/urandom")).map_err(|_| ())?;
590     ///       mem_map.read_to_memory(32, &mut file, 128).map_err(|_| ())?;
591     ///       let rand_val: u32 =  mem_map.read_obj(40).map_err(|_| ())?;
592     /// #     Ok(rand_val)
593     /// # }
594     /// ```
read_to_memory( &self, mut mem_offset: usize, src: &dyn AsRawFd, mut count: usize, ) -> Result<()>595     pub fn read_to_memory(
596         &self,
597         mut mem_offset: usize,
598         src: &dyn AsRawFd,
599         mut count: usize,
600     ) -> Result<()> {
601         self.range_end(mem_offset, count)
602             .map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
603         while count > 0 {
604             // The check above ensures that no memory outside this slice will get accessed by this
605             // read call.
606             match unsafe {
607                 read(
608                     src.as_raw_fd(),
609                     self.as_ptr().add(mem_offset) as *mut c_void,
610                     count,
611                 )
612             } {
613                 0 => {
614                     return Err(Error::ReadToMemory(io::Error::from(
615                         io::ErrorKind::UnexpectedEof,
616                     )))
617                 }
618                 r if r < 0 => return Err(Error::ReadToMemory(io::Error::last_os_error())),
619                 ret => {
620                     let bytes_read = ret as usize;
621                     match count.checked_sub(bytes_read) {
622                         Some(count_remaining) => count = count_remaining,
623                         None => break,
624                     }
625                     mem_offset += ret as usize;
626                 }
627             }
628         }
629         Ok(())
630     }
631 
632     /// Writes data from memory to a file descriptor.
633     ///
634     /// # Arguments
635     /// * `mem_offset` - Begin reading memory from this offset.
636     /// * `dst` - Write from memory to `dst`.
637     /// * `count` - Read `count` bytes from memory to `src`.
638     ///
639     /// # Examples
640     ///
641     /// * Write 128 bytes to /dev/null
642     ///
643     /// ```
644     /// # use sys_util::MemoryMapping;
645     /// # use std::fs::File;
646     /// # use std::path::Path;
647     /// # fn test_write_null() -> Result<(), ()> {
648     /// #     let mut mem_map = MemoryMapping::new(1024).unwrap();
649     ///       let mut file = File::open(Path::new("/dev/null")).map_err(|_| ())?;
650     ///       mem_map.write_from_memory(32, &mut file, 128).map_err(|_| ())?;
651     /// #     Ok(())
652     /// # }
653     /// ```
write_from_memory( &self, mut mem_offset: usize, dst: &dyn AsRawFd, mut count: usize, ) -> Result<()>654     pub fn write_from_memory(
655         &self,
656         mut mem_offset: usize,
657         dst: &dyn AsRawFd,
658         mut count: usize,
659     ) -> Result<()> {
660         self.range_end(mem_offset, count)
661             .map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
662         while count > 0 {
663             // The check above ensures that no memory outside this slice will get accessed by this
664             // write call.
665             match unsafe {
666                 write(
667                     dst.as_raw_fd(),
668                     self.as_ptr().add(mem_offset) as *const c_void,
669                     count,
670                 )
671             } {
672                 0 => {
673                     return Err(Error::WriteFromMemory(io::Error::from(
674                         io::ErrorKind::WriteZero,
675                     )))
676                 }
677                 ret if ret < 0 => return Err(Error::WriteFromMemory(io::Error::last_os_error())),
678                 ret => {
679                     let bytes_written = ret as usize;
680                     match count.checked_sub(bytes_written) {
681                         Some(count_remaining) => count = count_remaining,
682                         None => break,
683                     }
684                     mem_offset += ret as usize;
685                 }
686             }
687         }
688         Ok(())
689     }
690 
691     /// Uses madvise to tell the kernel to remove the specified range.  Subsequent reads
692     /// to the pages in the range will return zero bytes.
remove_range(&self, mem_offset: usize, count: usize) -> Result<()>693     pub fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()> {
694         self.range_end(mem_offset, count)
695             .map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
696         let ret = unsafe {
697             // madvising away the region is the same as the guest changing it.
698             // Next time it is read, it may return zero pages.
699             libc::madvise(
700                 (self.addr as usize + mem_offset) as *mut _,
701                 count,
702                 libc::MADV_REMOVE,
703             )
704         };
705         if ret < 0 {
706             Err(Error::InvalidRange(mem_offset, count, self.size()))
707         } else {
708             Ok(())
709         }
710     }
711 
712     // Check that offset+count is valid and return the sum.
range_end(&self, offset: usize, count: usize) -> Result<usize>713     fn range_end(&self, offset: usize, count: usize) -> Result<usize> {
714         let mem_end = offset.checked_add(count).ok_or(Error::InvalidAddress)?;
715         if mem_end > self.size() {
716             return Err(Error::InvalidAddress);
717         }
718         Ok(mem_end)
719     }
720 }
721 
722 // Safe because the pointer and size point to a memory range owned by this MemoryMapping that won't
723 // be unmapped until it's Dropped.
724 unsafe impl MappedRegion for MemoryMapping {
as_ptr(&self) -> *mut u8725     fn as_ptr(&self) -> *mut u8 {
726         self.addr
727     }
728 
size(&self) -> usize729     fn size(&self) -> usize {
730         self.size
731     }
732 }
733 
734 impl VolatileMemory for MemoryMapping {
get_slice(&self, offset: usize, count: usize) -> VolatileMemoryResult<VolatileSlice>735     fn get_slice(&self, offset: usize, count: usize) -> VolatileMemoryResult<VolatileSlice> {
736         let mem_end = calc_offset(offset, count)?;
737         if mem_end > self.size {
738             return Err(VolatileMemoryError::OutOfBounds { addr: mem_end });
739         }
740 
741         let new_addr =
742             (self.as_ptr() as usize)
743                 .checked_add(offset)
744                 .ok_or(VolatileMemoryError::Overflow {
745                     base: self.as_ptr() as usize,
746                     offset,
747                 })?;
748 
749         // Safe because we checked that offset + count was within our range and we only ever hand
750         // out volatile accessors.
751         Ok(unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) })
752     }
753 }
754 
755 impl Drop for MemoryMapping {
drop(&mut self)756     fn drop(&mut self) {
757         // This is safe because we mmap the area at addr ourselves, and nobody
758         // else is holding a reference to it.
759         unsafe {
760             libc::munmap(self.addr as *mut libc::c_void, self.size);
761         }
762     }
763 }
764 
765 /// Tracks Fixed Memory Maps within an anonymous memory-mapped fixed-sized arena
766 /// in the current process.
767 pub struct MemoryMappingArena {
768     addr: *mut u8,
769     size: usize,
770 }
771 
772 // Send and Sync aren't automatically inherited for the raw address pointer.
773 // Accessing that pointer is only done through the stateless interface which
774 // allows the object to be shared by multiple threads without a decrease in
775 // safety.
776 unsafe impl Send for MemoryMappingArena {}
777 unsafe impl Sync for MemoryMappingArena {}
778 
779 impl MemoryMappingArena {
780     /// Creates an mmap arena of `size` bytes.
781     ///
782     /// # Arguments
783     /// * `size` - Size of memory region in bytes.
new(size: usize) -> Result<MemoryMappingArena>784     pub fn new(size: usize) -> Result<MemoryMappingArena> {
785         // Reserve the arena's memory using an anonymous read-only mmap.
786         MemoryMapping::new_protection(size, Protection::none().set_read()).map(From::from)
787     }
788 
789     /// Anonymously maps `size` bytes at `offset` bytes from the start of the arena.
790     /// `offset` must be page aligned.
791     ///
792     /// # Arguments
793     /// * `offset` - Page aligned offset into the arena in bytes.
794     /// * `size` - Size of memory region in bytes.
795     /// * `fd` - File descriptor to mmap from.
add_anon(&mut self, offset: usize, size: usize) -> Result<()>796     pub fn add_anon(&mut self, offset: usize, size: usize) -> Result<()> {
797         self.try_add(offset, size, Protection::read_write(), None)
798     }
799 
800     /// Maps `size` bytes from the start of the given `fd` at `offset` bytes from
801     /// the start of the arena. `offset` must be page aligned.
802     ///
803     /// # Arguments
804     /// * `offset` - Page aligned offset into the arena in bytes.
805     /// * `size` - Size of memory region in bytes.
806     /// * `fd` - File descriptor to mmap from.
add_fd(&mut self, offset: usize, size: usize, fd: &dyn AsRawFd) -> Result<()>807     pub fn add_fd(&mut self, offset: usize, size: usize, fd: &dyn AsRawFd) -> Result<()> {
808         self.add_fd_offset(offset, size, fd, 0)
809     }
810 
811     /// Maps `size` bytes starting at `fs_offset` bytes from within the given `fd`
812     /// at `offset` bytes from the start of the arena. `offset` must be page aligned.
813     ///
814     /// # Arguments
815     /// * `offset` - Page aligned offset into the arena in bytes.
816     /// * `size` - Size of memory region in bytes.
817     /// * `fd` - File descriptor to mmap from.
818     /// * `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: u64, ) -> Result<()>819     pub fn add_fd_offset(
820         &mut self,
821         offset: usize,
822         size: usize,
823         fd: &dyn AsRawFd,
824         fd_offset: u64,
825     ) -> Result<()> {
826         self.add_fd_offset_protection(offset, size, fd, fd_offset, Protection::read_write())
827     }
828 
829     /// Maps `size` bytes starting at `fs_offset` bytes from within the given `fd`
830     /// at `offset` bytes from the start of the arena with `prot` protections.
831     /// `offset` must be page aligned.
832     ///
833     /// # Arguments
834     /// * `offset` - Page aligned offset into the arena in bytes.
835     /// * `size` - Size of memory region in bytes.
836     /// * `fd` - File descriptor to mmap from.
837     /// * `fd_offset` - Offset in bytes from the beginning of `fd` to start the mmap.
838     /// * `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: u64, prot: Protection, ) -> Result<()>839     pub fn add_fd_offset_protection(
840         &mut self,
841         offset: usize,
842         size: usize,
843         fd: &dyn AsRawFd,
844         fd_offset: u64,
845         prot: Protection,
846     ) -> Result<()> {
847         self.try_add(offset, size, prot, Some((fd, fd_offset)))
848     }
849 
850     /// Helper method that calls appropriate MemoryMapping constructor and adds
851     /// the resulting map into the arena.
try_add( &mut self, offset: usize, size: usize, prot: Protection, fd: Option<(&dyn AsRawFd, u64)>, ) -> Result<()>852     fn try_add(
853         &mut self,
854         offset: usize,
855         size: usize,
856         prot: Protection,
857         fd: Option<(&dyn AsRawFd, u64)>,
858     ) -> Result<()> {
859         // Ensure offset is page-aligned
860         if offset % pagesize() != 0 {
861             return Err(Error::NotPageAligned);
862         }
863         validate_includes_range(self.size(), offset, size)?;
864 
865         // This is safe since the range has been validated.
866         let mmap = unsafe {
867             match fd {
868                 Some((fd, fd_offset)) => MemoryMapping::from_fd_offset_protection_fixed(
869                     self.addr.add(offset),
870                     fd,
871                     size,
872                     fd_offset,
873                     prot,
874                 )?,
875                 None => MemoryMapping::new_protection_fixed(self.addr.add(offset), size, prot)?,
876             }
877         };
878 
879         // This mapping will get automatically removed when we drop the whole arena.
880         std::mem::forget(mmap);
881         Ok(())
882     }
883 
884     /// Removes `size` bytes at `offset` bytes from the start of the arena. `offset` must be page
885     /// aligned.
886     ///
887     /// # Arguments
888     /// * `offset` - Page aligned offset into the arena in bytes.
889     /// * `size` - Size of memory region in bytes.
remove(&mut self, offset: usize, size: usize) -> Result<()>890     pub fn remove(&mut self, offset: usize, size: usize) -> Result<()> {
891         self.try_add(offset, size, Protection::read(), None)
892     }
893 }
894 
895 // Safe because the pointer and size point to a memory range owned by this MemoryMappingArena that
896 // won't be unmapped until it's Dropped.
897 unsafe impl MappedRegion for MemoryMappingArena {
as_ptr(&self) -> *mut u8898     fn as_ptr(&self) -> *mut u8 {
899         self.addr
900     }
901 
size(&self) -> usize902     fn size(&self) -> usize {
903         self.size
904     }
905 
add_fd_mapping( &mut self, offset: usize, size: usize, fd: &dyn AsRawFd, fd_offset: u64, prot: Protection, ) -> Result<()>906     fn add_fd_mapping(
907         &mut self,
908         offset: usize,
909         size: usize,
910         fd: &dyn AsRawFd,
911         fd_offset: u64,
912         prot: Protection,
913     ) -> Result<()> {
914         self.add_fd_offset_protection(offset, size, fd, fd_offset, prot)
915     }
916 
remove_mapping(&mut self, offset: usize, size: usize) -> Result<()>917     fn remove_mapping(&mut self, offset: usize, size: usize) -> Result<()> {
918         self.remove(offset, size)
919     }
920 }
921 
922 impl From<MemoryMapping> for MemoryMappingArena {
from(mmap: MemoryMapping) -> Self923     fn from(mmap: MemoryMapping) -> Self {
924         let addr = mmap.as_ptr();
925         let size = mmap.size();
926 
927         // Forget the original mapping because the `MemoryMappingArena` will take care of calling
928         // `munmap` when it is dropped.
929         std::mem::forget(mmap);
930         MemoryMappingArena { addr, size }
931     }
932 }
933 
934 impl Drop for MemoryMappingArena {
drop(&mut self)935     fn drop(&mut self) {
936         // This is safe because we own this memory range, and nobody else is holding a reference to
937         // it.
938         unsafe {
939             libc::munmap(self.addr as *mut libc::c_void, self.size);
940         }
941     }
942 }
943 
944 #[cfg(test)]
945 mod tests {
946     use super::*;
947     use crate::Descriptor;
948     use data_model::{VolatileMemory, VolatileMemoryError};
949     use tempfile::tempfile;
950 
951     #[test]
basic_map()952     fn basic_map() {
953         let m = MemoryMapping::new(1024).unwrap();
954         assert_eq!(1024, m.size());
955     }
956 
957     #[test]
map_invalid_size()958     fn map_invalid_size() {
959         let res = MemoryMapping::new(0).unwrap_err();
960         if let Error::SystemCallFailed(e) = res {
961             assert_eq!(e.errno(), libc::EINVAL);
962         } else {
963             panic!("unexpected error: {}", res);
964         }
965     }
966 
967     #[test]
map_invalid_fd()968     fn map_invalid_fd() {
969         let fd = Descriptor(-1);
970         let res = MemoryMapping::from_fd(&fd, 1024).unwrap_err();
971         if let Error::SystemCallFailed(e) = res {
972             assert_eq!(e.errno(), libc::EBADF);
973         } else {
974             panic!("unexpected error: {}", res);
975         }
976     }
977 
978     #[test]
test_write_past_end()979     fn test_write_past_end() {
980         let m = MemoryMapping::new(5).unwrap();
981         let res = m.write_slice(&[1, 2, 3, 4, 5, 6], 0);
982         assert!(res.is_ok());
983         assert_eq!(res.unwrap(), 5);
984     }
985 
986     #[test]
slice_size()987     fn slice_size() {
988         let m = MemoryMapping::new(5).unwrap();
989         let s = m.get_slice(2, 3).unwrap();
990         assert_eq!(s.size(), 3);
991     }
992 
993     #[test]
slice_addr()994     fn slice_addr() {
995         let m = MemoryMapping::new(5).unwrap();
996         let s = m.get_slice(2, 3).unwrap();
997         assert_eq!(s.as_ptr(), unsafe { m.as_ptr().offset(2) });
998     }
999 
1000     #[test]
slice_store()1001     fn slice_store() {
1002         let m = MemoryMapping::new(5).unwrap();
1003         let r = m.get_ref(2).unwrap();
1004         r.store(9u16);
1005         assert_eq!(m.read_obj::<u16>(2).unwrap(), 9);
1006     }
1007 
1008     #[test]
slice_overflow_error()1009     fn slice_overflow_error() {
1010         let m = MemoryMapping::new(5).unwrap();
1011         let res = m.get_slice(std::usize::MAX, 3).unwrap_err();
1012         assert_eq!(
1013             res,
1014             VolatileMemoryError::Overflow {
1015                 base: std::usize::MAX,
1016                 offset: 3,
1017             }
1018         );
1019     }
1020     #[test]
slice_oob_error()1021     fn slice_oob_error() {
1022         let m = MemoryMapping::new(5).unwrap();
1023         let res = m.get_slice(3, 3).unwrap_err();
1024         assert_eq!(res, VolatileMemoryError::OutOfBounds { addr: 6 });
1025     }
1026 
1027     #[test]
from_fd_offset_invalid()1028     fn from_fd_offset_invalid() {
1029         let fd = tempfile().unwrap();
1030         let res = MemoryMapping::from_fd_offset(&fd, 4096, (libc::off_t::max_value() as u64) + 1)
1031             .unwrap_err();
1032         match res {
1033             Error::InvalidOffset => {}
1034             e => panic!("unexpected error: {}", e),
1035         }
1036     }
1037 
1038     #[test]
arena_new()1039     fn arena_new() {
1040         let m = MemoryMappingArena::new(0x40000).unwrap();
1041         assert_eq!(m.size(), 0x40000);
1042     }
1043 
1044     #[test]
arena_add()1045     fn arena_add() {
1046         let mut m = MemoryMappingArena::new(0x40000).unwrap();
1047         assert!(m.add_anon(0, pagesize() * 4).is_ok());
1048     }
1049 
1050     #[test]
arena_remove()1051     fn arena_remove() {
1052         let mut m = MemoryMappingArena::new(0x40000).unwrap();
1053         assert!(m.add_anon(0, pagesize() * 4).is_ok());
1054         assert!(m.remove(0, pagesize()).is_ok());
1055         assert!(m.remove(0, pagesize() * 2).is_ok());
1056     }
1057 
1058     #[test]
arena_add_alignment_error()1059     fn arena_add_alignment_error() {
1060         let mut m = MemoryMappingArena::new(pagesize() * 2).unwrap();
1061         assert!(m.add_anon(0, 0x100).is_ok());
1062         let res = m.add_anon(pagesize() + 1, 0x100).unwrap_err();
1063         match res {
1064             Error::NotPageAligned => {}
1065             e => panic!("unexpected error: {}", e),
1066         }
1067     }
1068 
1069     #[test]
arena_add_oob_error()1070     fn arena_add_oob_error() {
1071         let mut m = MemoryMappingArena::new(pagesize()).unwrap();
1072         let res = m.add_anon(0, pagesize() + 1).unwrap_err();
1073         match res {
1074             Error::InvalidAddress => {}
1075             e => panic!("unexpected error: {}", e),
1076         }
1077     }
1078 
1079     #[test]
arena_add_overlapping()1080     fn arena_add_overlapping() {
1081         let ps = pagesize();
1082         let mut m =
1083             MemoryMappingArena::new(12 * ps).expect("failed to create `MemoryMappingArena`");
1084         m.add_anon(ps * 4, ps * 4)
1085             .expect("failed to add sub-mapping");
1086 
1087         // Overlap in the front.
1088         m.add_anon(ps * 2, ps * 3)
1089             .expect("failed to add front overlapping sub-mapping");
1090 
1091         // Overlap in the back.
1092         m.add_anon(ps * 7, ps * 3)
1093             .expect("failed to add back overlapping sub-mapping");
1094 
1095         // Overlap the back of the first mapping, all of the middle mapping, and the front of the
1096         // last mapping.
1097         m.add_anon(ps * 3, ps * 6)
1098             .expect("failed to add mapping that overlaps several mappings");
1099     }
1100 
1101     #[test]
arena_remove_overlapping()1102     fn arena_remove_overlapping() {
1103         let ps = pagesize();
1104         let mut m =
1105             MemoryMappingArena::new(12 * ps).expect("failed to create `MemoryMappingArena`");
1106         m.add_anon(ps * 4, ps * 4)
1107             .expect("failed to add sub-mapping");
1108         m.add_anon(ps * 2, ps * 2)
1109             .expect("failed to add front overlapping sub-mapping");
1110         m.add_anon(ps * 8, ps * 2)
1111             .expect("failed to add back overlapping sub-mapping");
1112 
1113         // Remove the back of the first mapping and the front of the second.
1114         m.remove(ps * 3, ps * 2)
1115             .expect("failed to remove front overlapping mapping");
1116 
1117         // Remove the back of the second mapping and the front of the third.
1118         m.remove(ps * 7, ps * 2)
1119             .expect("failed to remove back overlapping mapping");
1120 
1121         // Remove a mapping that completely overlaps the middle mapping.
1122         m.remove(ps * 5, ps * 2)
1123             .expect("failed to remove fully overlapping mapping");
1124     }
1125 
1126     #[test]
arena_remove_unaligned()1127     fn arena_remove_unaligned() {
1128         let ps = pagesize();
1129         let mut m =
1130             MemoryMappingArena::new(12 * ps).expect("failed to create `MemoryMappingArena`");
1131 
1132         m.add_anon(0, ps).expect("failed to add mapping");
1133         m.remove(0, ps - 1)
1134             .expect("failed to remove unaligned mapping");
1135     }
1136 
1137     #[test]
arena_msync()1138     fn arena_msync() {
1139         let size = 0x40000;
1140         let m = MemoryMappingArena::new(size).unwrap();
1141         let ps = pagesize();
1142         MappedRegion::msync(&m, 0, ps).unwrap();
1143         MappedRegion::msync(&m, 0, size).unwrap();
1144         MappedRegion::msync(&m, ps, size - ps).unwrap();
1145         let res = MappedRegion::msync(&m, ps, size).unwrap_err();
1146         match res {
1147             Error::InvalidAddress => {}
1148             e => panic!("unexpected error: {}", e),
1149         }
1150     }
1151 }
1152