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 use std::ffi::{CStr, CString};
6 use std::fs::{read_link, File};
7 use std::io::{self, Read, Seek, SeekFrom, Write};
8 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
9 
10 use libc::{
11     self, c_char, c_int, c_long, c_uint, close, fcntl, ftruncate64, off64_t, syscall,
12     SYS_memfd_create, EINVAL, F_ADD_SEALS, F_GET_SEALS, F_SEAL_GROW, F_SEAL_SEAL, F_SEAL_SHRINK,
13     F_SEAL_WRITE, MFD_ALLOW_SEALING,
14 };
15 use serde::{Deserialize, Serialize};
16 
17 use crate::{errno, errno_result, Result};
18 
19 /// A shared memory file descriptor and its size.
20 #[derive(Serialize, Deserialize)]
21 pub struct SharedMemory {
22     #[serde(with = "crate::with_as_descriptor")]
23     fd: File,
24     size: u64,
25 }
26 
27 // from <sys/memfd.h>
28 const MFD_CLOEXEC: c_uint = 0x0001;
29 
memfd_create(name: *const c_char, flags: c_uint) -> c_int30 unsafe fn memfd_create(name: *const c_char, flags: c_uint) -> c_int {
31     syscall(SYS_memfd_create as c_long, name, flags) as c_int
32 }
33 
34 /// A set of memfd seals.
35 ///
36 /// An enumeration of each bit can be found at `fcntl(2)`.
37 #[derive(Copy, Clone, Default)]
38 pub struct MemfdSeals(i32);
39 
40 impl MemfdSeals {
41     /// Returns an empty set of memfd seals.
42     #[inline]
new() -> MemfdSeals43     pub fn new() -> MemfdSeals {
44         MemfdSeals(0)
45     }
46 
47     /// Gets the raw bitmask of seals enumerated in `fcntl(2)`.
48     #[inline]
bitmask(self) -> i3249     pub fn bitmask(self) -> i32 {
50         self.0
51     }
52 
53     /// True of the grow seal bit is present.
54     #[inline]
grow_seal(self) -> bool55     pub fn grow_seal(self) -> bool {
56         self.0 & F_SEAL_GROW != 0
57     }
58 
59     /// Sets the grow seal bit.
60     #[inline]
set_grow_seal(&mut self)61     pub fn set_grow_seal(&mut self) {
62         self.0 |= F_SEAL_GROW;
63     }
64 
65     /// True of the shrink seal bit is present.
66     #[inline]
shrink_seal(self) -> bool67     pub fn shrink_seal(self) -> bool {
68         self.0 & F_SEAL_SHRINK != 0
69     }
70 
71     /// Sets the shrink seal bit.
72     #[inline]
set_shrink_seal(&mut self)73     pub fn set_shrink_seal(&mut self) {
74         self.0 |= F_SEAL_SHRINK;
75     }
76 
77     /// True of the write seal bit is present.
78     #[inline]
write_seal(self) -> bool79     pub fn write_seal(self) -> bool {
80         self.0 & F_SEAL_WRITE != 0
81     }
82 
83     /// Sets the write seal bit.
84     #[inline]
set_write_seal(&mut self)85     pub fn set_write_seal(&mut self) {
86         self.0 |= F_SEAL_WRITE;
87     }
88 
89     /// True of the seal seal bit is present.
90     #[inline]
seal_seal(self) -> bool91     pub fn seal_seal(self) -> bool {
92         self.0 & F_SEAL_SEAL != 0
93     }
94 
95     /// Sets the seal seal bit.
96     #[inline]
set_seal_seal(&mut self)97     pub fn set_seal_seal(&mut self) {
98         self.0 |= F_SEAL_SEAL;
99     }
100 }
101 
102 impl SharedMemory {
103     /// Convenience function for `SharedMemory::new` that is always named and accepts a wide variety
104     /// of string-like types.
105     ///
106     /// Note that the given name may not have NUL characters anywhere in it, or this will return an
107     /// error.
named<T: Into<Vec<u8>>>(name: T) -> Result<SharedMemory>108     pub fn named<T: Into<Vec<u8>>>(name: T) -> Result<SharedMemory> {
109         Self::new(Some(
110             &CString::new(name).map_err(|_| errno::Error::new(EINVAL))?,
111         ))
112     }
113 
114     /// Convenience function for `SharedMemory::new` that has an arbitrary and unspecified name.
anon() -> Result<SharedMemory>115     pub fn anon() -> Result<SharedMemory> {
116         Self::new(None)
117     }
118 
119     /// Creates a new shared memory file descriptor with zero size.
120     ///
121     /// If a name is given, it will appear in `/proc/self/fd/<shm fd>` for the purposes of
122     /// debugging. The name does not need to be unique.
123     ///
124     /// The file descriptor is opened with the close on exec flag and allows memfd sealing.
new(name: Option<&CStr>) -> Result<SharedMemory>125     pub fn new(name: Option<&CStr>) -> Result<SharedMemory> {
126         let shm_name = name
127             .map(|n| n.as_ptr())
128             .unwrap_or(b"/crosvm_shm\0".as_ptr() as *const c_char);
129         // The following are safe because we give a valid C string and check the
130         // results of the memfd_create call.
131         let fd = unsafe { memfd_create(shm_name, MFD_CLOEXEC | MFD_ALLOW_SEALING) };
132         if fd < 0 {
133             return errno_result();
134         }
135 
136         let file = unsafe { File::from_raw_fd(fd) };
137 
138         Ok(SharedMemory { fd: file, size: 0 })
139     }
140 
141     /// Constructs a `SharedMemory` instance from a `File` that represents shared memory.
142     ///
143     /// The size of the resulting shared memory will be determined using `File::seek`. If the given
144     /// file's size can not be determined this way, this will return an error.
from_file(mut file: File) -> Result<SharedMemory>145     pub fn from_file(mut file: File) -> Result<SharedMemory> {
146         let file_size = file.seek(SeekFrom::End(0))?;
147         Ok(SharedMemory {
148             fd: file,
149             size: file_size as u64,
150         })
151     }
152 
153     /// Gets the memfd seals that have already been added to this.
154     ///
155     /// This may fail if this instance was not constructed from a memfd.
get_seals(&self) -> Result<MemfdSeals>156     pub fn get_seals(&self) -> Result<MemfdSeals> {
157         let ret = unsafe { fcntl(self.fd.as_raw_fd(), F_GET_SEALS) };
158         if ret < 0 {
159             return errno_result();
160         }
161         Ok(MemfdSeals(ret))
162     }
163 
164     /// Adds the given set of memfd seals.
165     ///
166     /// This may fail if this instance was not constructed from a memfd with sealing allowed or if
167     /// the seal seal (`F_SEAL_SEAL`) bit was already added.
add_seals(&mut self, seals: MemfdSeals) -> Result<()>168     pub fn add_seals(&mut self, seals: MemfdSeals) -> Result<()> {
169         let ret = unsafe { fcntl(self.fd.as_raw_fd(), F_ADD_SEALS, seals) };
170         if ret < 0 {
171             return errno_result();
172         }
173         Ok(())
174     }
175 
176     /// Gets the size in bytes of the shared memory.
177     ///
178     /// The size returned here does not reflect changes by other interfaces or users of the shared
179     /// memory file descriptor..
size(&self) -> u64180     pub fn size(&self) -> u64 {
181         self.size
182     }
183 
184     /// Sets the size in bytes of the shared memory.
185     ///
186     /// Note that if some process has already mapped this shared memory and the new size is smaller,
187     /// that process may get signaled with SIGBUS if they access any page past the new size.
set_size(&mut self, size: u64) -> Result<()>188     pub fn set_size(&mut self, size: u64) -> Result<()> {
189         let ret = unsafe { ftruncate64(self.fd.as_raw_fd(), size as off64_t) };
190         if ret < 0 {
191             return errno_result();
192         }
193         self.size = size;
194         Ok(())
195     }
196 
197     /// Reads the name from the underlying file as a `String`.
198     ///
199     /// If the underlying file was not created with `SharedMemory::new` or with `memfd_create`, the
200     /// results are undefined. Because this returns a `String`, the name's bytes are interpreted as
201     /// utf-8.
read_name(&self) -> Result<String>202     pub fn read_name(&self) -> Result<String> {
203         let fd_path = format!("/proc/self/fd/{}", self.as_raw_fd());
204         let link_name = read_link(fd_path)?;
205         link_name
206             .to_str()
207             .map(|s| {
208                 s.trim_start_matches("/memfd:")
209                     .trim_end_matches(" (deleted)")
210                     .to_owned()
211             })
212             .ok_or_else(|| errno::Error::new(EINVAL))
213     }
214 }
215 
216 impl Read for SharedMemory {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>217     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
218         self.fd.read(buf)
219     }
220 }
221 
222 impl Read for &SharedMemory {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>223     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
224         (&self.fd).read(buf)
225     }
226 }
227 
228 impl Write for SharedMemory {
write(&mut self, buf: &[u8]) -> io::Result<usize>229     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
230         self.fd.write(buf)
231     }
232 
flush(&mut self) -> io::Result<()>233     fn flush(&mut self) -> io::Result<()> {
234         self.fd.flush()
235     }
236 }
237 
238 impl Write for &SharedMemory {
write(&mut self, buf: &[u8]) -> io::Result<usize>239     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
240         (&self.fd).write(buf)
241     }
242 
flush(&mut self) -> io::Result<()>243     fn flush(&mut self) -> io::Result<()> {
244         (&self.fd).flush()
245     }
246 }
247 
248 impl Seek for SharedMemory {
seek(&mut self, pos: SeekFrom) -> io::Result<u64>249     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
250         self.fd.seek(pos)
251     }
252 }
253 
254 impl Seek for &SharedMemory {
seek(&mut self, pos: SeekFrom) -> io::Result<u64>255     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
256         (&self.fd).seek(pos)
257     }
258 }
259 
260 impl AsRawFd for SharedMemory {
as_raw_fd(&self) -> RawFd261     fn as_raw_fd(&self) -> RawFd {
262         self.fd.as_raw_fd()
263     }
264 }
265 
266 impl AsRawFd for &SharedMemory {
as_raw_fd(&self) -> RawFd267     fn as_raw_fd(&self) -> RawFd {
268         self.fd.as_raw_fd()
269     }
270 }
271 
272 impl IntoRawFd for SharedMemory {
into_raw_fd(self) -> RawFd273     fn into_raw_fd(self) -> RawFd {
274         self.fd.into_raw_fd()
275     }
276 }
277 
278 impl From<SharedMemory> for File {
from(s: SharedMemory) -> File279     fn from(s: SharedMemory) -> File {
280         s.fd
281     }
282 }
283 
284 /// Checks if the kernel we are running on has memfd_create. It was introduced in 3.17.
285 /// Only to be used from tests to prevent running on ancient kernels that won't
286 /// support the functionality anyways.
kernel_has_memfd() -> bool287 pub fn kernel_has_memfd() -> bool {
288     unsafe {
289         let fd = memfd_create(b"/test_memfd_create\0".as_ptr() as *const c_char, 0);
290         if fd < 0 {
291             if errno::Error::last().errno() == libc::ENOSYS {
292                 return false;
293             }
294             return true;
295         }
296         close(fd);
297     }
298     true
299 }
300 
301 #[cfg(test)]
302 mod tests {
303     use super::*;
304 
305     use std::ffi::CString;
306 
307     use data_model::VolatileMemory;
308 
309     use crate::MemoryMapping;
310 
311     #[test]
named()312     fn named() {
313         if !kernel_has_memfd() {
314             return;
315         }
316         const TEST_NAME: &str = "Name McCool Person";
317         let shm = SharedMemory::named(TEST_NAME).expect("failed to create shared memory");
318         assert_eq!(shm.read_name(), Ok(TEST_NAME.to_owned()));
319     }
320 
321     #[test]
anon()322     fn anon() {
323         if !kernel_has_memfd() {
324             return;
325         }
326         SharedMemory::anon().expect("failed to create shared memory");
327     }
328 
329     #[test]
new()330     fn new() {
331         if !kernel_has_memfd() {
332             return;
333         }
334         let shm = SharedMemory::anon().expect("failed to create shared memory");
335         assert_eq!(shm.size(), 0);
336     }
337 
338     #[test]
new_sized()339     fn new_sized() {
340         if !kernel_has_memfd() {
341             return;
342         }
343         let mut shm = SharedMemory::anon().expect("failed to create shared memory");
344         shm.set_size(1024)
345             .expect("failed to set shared memory size");
346         assert_eq!(shm.size(), 1024);
347     }
348 
349     #[test]
new_huge()350     fn new_huge() {
351         if !kernel_has_memfd() {
352             return;
353         }
354         let mut shm = SharedMemory::anon().expect("failed to create shared memory");
355         shm.set_size(0x7fff_ffff_ffff_ffff)
356             .expect("failed to set shared memory size");
357         assert_eq!(shm.size(), 0x7fff_ffff_ffff_ffff);
358     }
359 
360     #[test]
new_too_huge()361     fn new_too_huge() {
362         if !kernel_has_memfd() {
363             return;
364         }
365         let mut shm = SharedMemory::anon().expect("failed to create shared memory");
366         shm.set_size(0x8000_0000_0000_0000).unwrap_err();
367         assert_eq!(shm.size(), 0);
368     }
369 
370     #[test]
new_named()371     fn new_named() {
372         if !kernel_has_memfd() {
373             return;
374         }
375         let name = "very unique name";
376         let cname = CString::new(name).unwrap();
377         let shm = SharedMemory::new(Some(&cname)).expect("failed to create shared memory");
378         assert_eq!(shm.read_name(), Ok(name.to_owned()));
379     }
380 
381     #[test]
new_sealed()382     fn new_sealed() {
383         if !kernel_has_memfd() {
384             return;
385         }
386         let mut shm = SharedMemory::anon().expect("failed to create shared memory");
387         let mut seals = shm.get_seals().expect("failed to get seals");
388         assert_eq!(seals.bitmask(), 0);
389         seals.set_seal_seal();
390         shm.add_seals(seals).expect("failed to add seals");
391         seals = shm.get_seals().expect("failed to get seals");
392         assert!(seals.seal_seal());
393         // Adding more seals should be rejected by the kernel.
394         shm.add_seals(seals).unwrap_err();
395     }
396 
397     #[test]
mmap_page()398     fn mmap_page() {
399         if !kernel_has_memfd() {
400             return;
401         }
402         let mut shm = SharedMemory::anon().expect("failed to create shared memory");
403         shm.set_size(4096)
404             .expect("failed to set shared memory size");
405 
406         let mmap1 =
407             MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
408         let mmap2 =
409             MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
410 
411         assert_ne!(
412             mmap1.get_slice(0, 1).unwrap().as_ptr(),
413             mmap2.get_slice(0, 1).unwrap().as_ptr()
414         );
415 
416         mmap1
417             .get_slice(0, 4096)
418             .expect("failed to get mmap slice")
419             .write_bytes(0x45);
420 
421         for i in 0..4096 {
422             assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0x45u8);
423         }
424     }
425 
426     #[test]
mmap_page_offset()427     fn mmap_page_offset() {
428         if !kernel_has_memfd() {
429             return;
430         }
431         let mut shm = SharedMemory::anon().expect("failed to create shared memory");
432         shm.set_size(8092)
433             .expect("failed to set shared memory size");
434 
435         let mmap1 = MemoryMapping::from_fd_offset(&shm, shm.size() as usize, 4096)
436             .expect("failed to map shared memory");
437         let mmap2 =
438             MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
439 
440         mmap1
441             .get_slice(0, 4096)
442             .expect("failed to get mmap slice")
443             .write_bytes(0x45);
444 
445         for i in 0..4096 {
446             assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0);
447         }
448         for i in 4096..8092 {
449             assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0x45u8);
450         }
451     }
452 }
453