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