• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
6 use std::fs::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, F_ADD_SEALS,
12     F_GET_SEALS, F_SEAL_GROW, F_SEAL_SEAL, F_SEAL_SHRINK, F_SEAL_WRITE, MFD_ALLOW_SEALING,
13 };
14 use syscall_defines::linux::LinuxSyscall::SYS_memfd_create;
15 
16 use crate::{errno, errno_result, Result};
17 
18 /// A shared memory file descriptor and its size.
19 pub struct SharedMemory {
20     fd: File,
21     size: u64,
22 }
23 
24 // from <sys/memfd.h>
25 const MFD_CLOEXEC: c_uint = 0x0001;
26 
memfd_create(name: *const c_char, flags: c_uint) -> c_int27 unsafe fn memfd_create(name: *const c_char, flags: c_uint) -> c_int {
28     syscall(SYS_memfd_create as c_long, name, flags) as c_int
29 }
30 
31 /// A set of memfd seals.
32 ///
33 /// An enumeration of each bit can be found at `fcntl(2)`.
34 #[derive(Copy, Clone, Default)]
35 pub struct MemfdSeals(i32);
36 
37 impl MemfdSeals {
38     /// Returns an empty set of memfd seals.
39     #[inline]
new() -> MemfdSeals40     pub fn new() -> MemfdSeals {
41         MemfdSeals(0)
42     }
43 
44     /// Gets the raw bitmask of seals enumerated in `fcntl(2)`.
45     #[inline]
bitmask(self) -> i3246     pub fn bitmask(self) -> i32 {
47         self.0
48     }
49 
50     /// True of the grow seal bit is present.
51     #[inline]
grow_seal(self) -> bool52     pub fn grow_seal(self) -> bool {
53         self.0 & F_SEAL_GROW != 0
54     }
55 
56     /// Sets the grow seal bit.
57     #[inline]
set_grow_seal(&mut self)58     pub fn set_grow_seal(&mut self) {
59         self.0 |= F_SEAL_GROW;
60     }
61 
62     /// True of the shrink seal bit is present.
63     #[inline]
shrink_seal(self) -> bool64     pub fn shrink_seal(self) -> bool {
65         self.0 & F_SEAL_SHRINK != 0
66     }
67 
68     /// Sets the shrink seal bit.
69     #[inline]
set_shrink_seal(&mut self)70     pub fn set_shrink_seal(&mut self) {
71         self.0 |= F_SEAL_SHRINK;
72     }
73 
74     /// True of the write seal bit is present.
75     #[inline]
write_seal(self) -> bool76     pub fn write_seal(self) -> bool {
77         self.0 & F_SEAL_WRITE != 0
78     }
79 
80     /// Sets the write seal bit.
81     #[inline]
set_write_seal(&mut self)82     pub fn set_write_seal(&mut self) {
83         self.0 |= F_SEAL_WRITE;
84     }
85 
86     /// True of the seal seal bit is present.
87     #[inline]
seal_seal(self) -> bool88     pub fn seal_seal(self) -> bool {
89         self.0 & F_SEAL_SEAL != 0
90     }
91 
92     /// Sets the seal seal bit.
93     #[inline]
set_seal_seal(&mut self)94     pub fn set_seal_seal(&mut self) {
95         self.0 |= F_SEAL_SEAL;
96     }
97 }
98 
99 impl SharedMemory {
100     /// Creates a new shared memory file descriptor with zero size.
101     ///
102     /// If a name is given, it will appear in `/proc/self/fd/<shm fd>` for the purposes of
103     /// debugging. The name does not need to be unique.
104     ///
105     /// The file descriptor is opened with the close on exec flag and allows memfd sealing.
new(name: Option<&CStr>) -> Result<SharedMemory>106     pub fn new(name: Option<&CStr>) -> Result<SharedMemory> {
107         let shm_name = name
108             .map(|n| n.as_ptr())
109             .unwrap_or(b"/crosvm_shm\0".as_ptr() as *const c_char);
110         // The following are safe because we give a valid C string and check the
111         // results of the memfd_create call.
112         let fd = unsafe { memfd_create(shm_name, MFD_CLOEXEC | MFD_ALLOW_SEALING) };
113         if fd < 0 {
114             return errno_result();
115         }
116 
117         let file = unsafe { File::from_raw_fd(fd) };
118 
119         Ok(SharedMemory { fd: file, size: 0 })
120     }
121 
122     /// Constructs a `SharedMemory` instance from a file descriptor that represents shared memory.
123     ///
124     /// The size of the resulting shared memory will be determined using `File::seek`. If the given
125     /// file's size can not be determined this way, this will return an error.
from_raw_fd<T: IntoRawFd>(fd: T) -> Result<SharedMemory>126     pub fn from_raw_fd<T: IntoRawFd>(fd: T) -> Result<SharedMemory> {
127         // Safe because the IntoRawFd trait indicates fd has unique ownership.
128         let mut file = unsafe { File::from_raw_fd(fd.into_raw_fd()) };
129         let file_size = file.seek(SeekFrom::End(0))?;
130         Ok(SharedMemory {
131             fd: file,
132             size: file_size as u64,
133         })
134     }
135 
136     /// Gets the memfd seals that have already been added to this.
137     ///
138     /// This may fail if this instance was not constructed from a memfd.
get_seals(&self) -> Result<MemfdSeals>139     pub fn get_seals(&self) -> Result<MemfdSeals> {
140         let ret = unsafe { fcntl(self.fd.as_raw_fd(), F_GET_SEALS) };
141         if ret < 0 {
142             return errno_result();
143         }
144         Ok(MemfdSeals(ret))
145     }
146 
147     /// Adds the given set of memfd seals.
148     ///
149     /// This may fail if this instance was not constructed from a memfd with sealing allowed or if
150     /// the seal seal (`F_SEAL_SEAL`) bit was already added.
add_seals(&mut self, seals: MemfdSeals) -> Result<()>151     pub fn add_seals(&mut self, seals: MemfdSeals) -> Result<()> {
152         let ret = unsafe { fcntl(self.fd.as_raw_fd(), F_ADD_SEALS, seals) };
153         if ret < 0 {
154             return errno_result();
155         }
156         Ok(())
157     }
158 
159     /// Gets the size in bytes of the shared memory.
160     ///
161     /// The size returned here does not reflect changes by other interfaces or users of the shared
162     /// memory file descriptor..
size(&self) -> u64163     pub fn size(&self) -> u64 {
164         self.size
165     }
166 
167     /// Sets the size in bytes of the shared memory.
168     ///
169     /// Note that if some process has already mapped this shared memory and the new size is smaller,
170     /// that process may get signaled with SIGBUS if they access any page past the new size.
set_size(&mut self, size: u64) -> Result<()>171     pub fn set_size(&mut self, size: u64) -> Result<()> {
172         let ret = unsafe { ftruncate64(self.fd.as_raw_fd(), size as off64_t) };
173         if ret < 0 {
174             return errno_result();
175         }
176         self.size = size;
177         Ok(())
178     }
179 }
180 
181 impl Read for SharedMemory {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>182     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
183         self.fd.read(buf)
184     }
185 }
186 
187 impl Read for &SharedMemory {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>188     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
189         (&self.fd).read(buf)
190     }
191 }
192 
193 impl Write for SharedMemory {
write(&mut self, buf: &[u8]) -> io::Result<usize>194     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
195         self.fd.write(buf)
196     }
197 
flush(&mut self) -> io::Result<()>198     fn flush(&mut self) -> io::Result<()> {
199         self.fd.flush()
200     }
201 }
202 
203 impl Write for &SharedMemory {
write(&mut self, buf: &[u8]) -> io::Result<usize>204     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
205         (&self.fd).write(buf)
206     }
207 
flush(&mut self) -> io::Result<()>208     fn flush(&mut self) -> io::Result<()> {
209         (&self.fd).flush()
210     }
211 }
212 
213 impl Seek for SharedMemory {
seek(&mut self, pos: SeekFrom) -> io::Result<u64>214     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
215         self.fd.seek(pos)
216     }
217 }
218 
219 impl Seek for &SharedMemory {
seek(&mut self, pos: SeekFrom) -> io::Result<u64>220     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
221         (&self.fd).seek(pos)
222     }
223 }
224 
225 impl AsRawFd for SharedMemory {
as_raw_fd(&self) -> RawFd226     fn as_raw_fd(&self) -> RawFd {
227         self.fd.as_raw_fd()
228     }
229 }
230 
231 impl AsRawFd for &SharedMemory {
as_raw_fd(&self) -> RawFd232     fn as_raw_fd(&self) -> RawFd {
233         self.fd.as_raw_fd()
234     }
235 }
236 
237 impl Into<File> for SharedMemory {
into(self) -> File238     fn into(self) -> File {
239         self.fd
240     }
241 }
242 
243 /// Checks if the kernel we are running on has memfd_create. It was introduced in 3.17.
244 /// Only to be used from tests to prevent running on ancient kernels that won't
245 /// support the functionality anyways.
kernel_has_memfd() -> bool246 pub fn kernel_has_memfd() -> bool {
247     unsafe {
248         let fd = memfd_create(b"/test_memfd_create\0".as_ptr() as *const c_char, 0);
249         if fd < 0 {
250             if errno::Error::last().errno() == libc::ENOSYS {
251                 return false;
252             }
253             return true;
254         }
255         close(fd);
256     }
257     true
258 }
259 
260 #[cfg(test)]
261 mod tests {
262     use super::*;
263 
264     use std::ffi::CString;
265     use std::fs::read_link;
266 
267     use data_model::VolatileMemory;
268 
269     use crate::MemoryMapping;
270 
271     #[test]
new()272     fn new() {
273         if !kernel_has_memfd() {
274             return;
275         }
276         let shm = SharedMemory::new(None).expect("failed to create shared memory");
277         assert_eq!(shm.size(), 0);
278     }
279 
280     #[test]
new_sized()281     fn new_sized() {
282         if !kernel_has_memfd() {
283             return;
284         }
285         let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
286         shm.set_size(1024)
287             .expect("failed to set shared memory size");
288         assert_eq!(shm.size(), 1024);
289     }
290 
291     #[test]
new_huge()292     fn new_huge() {
293         if !kernel_has_memfd() {
294             return;
295         }
296         let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
297         shm.set_size(0x7fff_ffff_ffff_ffff)
298             .expect("failed to set shared memory size");
299         assert_eq!(shm.size(), 0x7fff_ffff_ffff_ffff);
300     }
301 
302     #[test]
new_too_huge()303     fn new_too_huge() {
304         if !kernel_has_memfd() {
305             return;
306         }
307         let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
308         shm.set_size(0x8000_0000_0000_0000).unwrap_err();
309         assert_eq!(shm.size(), 0);
310     }
311 
312     #[test]
new_named()313     fn new_named() {
314         if !kernel_has_memfd() {
315             return;
316         }
317         let name = "very unique name";
318         let cname = CString::new(name).unwrap();
319         let shm = SharedMemory::new(Some(&cname)).expect("failed to create shared memory");
320         let fd_path = format!("/proc/self/fd/{}", shm.as_raw_fd());
321         let link_name =
322             read_link(fd_path).expect("failed to read link of shared memory /proc/self/fd entry");
323         assert!(link_name.to_str().unwrap().contains(name));
324     }
325 
326     #[test]
new_sealed()327     fn new_sealed() {
328         if !kernel_has_memfd() {
329             return;
330         }
331         let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
332         let mut seals = shm.get_seals().expect("failed to get seals");
333         assert_eq!(seals.bitmask(), 0);
334         seals.set_seal_seal();
335         shm.add_seals(seals).expect("failed to add seals");
336         seals = shm.get_seals().expect("failed to get seals");
337         assert!(seals.seal_seal());
338         // Adding more seals should be rejected by the kernel.
339         shm.add_seals(seals).unwrap_err();
340     }
341 
342     #[test]
mmap_page()343     fn mmap_page() {
344         if !kernel_has_memfd() {
345             return;
346         }
347         let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
348         shm.set_size(4096)
349             .expect("failed to set shared memory size");
350 
351         let mmap1 =
352             MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
353         let mmap2 =
354             MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
355 
356         assert_ne!(
357             mmap1.get_slice(0, 1).unwrap().as_ptr(),
358             mmap2.get_slice(0, 1).unwrap().as_ptr()
359         );
360 
361         mmap1
362             .get_slice(0, 4096)
363             .expect("failed to get mmap slice")
364             .write_bytes(0x45);
365 
366         for i in 0..4096 {
367             assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0x45u8);
368         }
369     }
370 
371     #[test]
mmap_page_offset()372     fn mmap_page_offset() {
373         if !kernel_has_memfd() {
374             return;
375         }
376         let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
377         shm.set_size(8092)
378             .expect("failed to set shared memory size");
379 
380         let mmap1 = MemoryMapping::from_fd_offset(&shm, shm.size() as usize, 4096)
381             .expect("failed to map shared memory");
382         let mmap2 =
383             MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
384 
385         mmap1
386             .get_slice(0, 4096)
387             .expect("failed to get mmap slice")
388             .write_bytes(0x45);
389 
390         for i in 0..4096 {
391             assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0);
392         }
393         for i in 4096..8092 {
394             assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0x45u8);
395         }
396     }
397 }
398