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