xref: /aosp_15_r20/external/crosvm/base/src/sys/linux/shm.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2017 The ChromiumOS Authors
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::Seek;
8 use std::io::SeekFrom;
9 
10 use libc::c_char;
11 use libc::c_int;
12 use libc::c_long;
13 use libc::c_uint;
14 use libc::close;
15 use libc::fcntl;
16 use libc::ftruncate64;
17 use libc::off64_t;
18 use libc::syscall;
19 use libc::SYS_memfd_create;
20 use libc::F_ADD_SEALS;
21 use libc::F_GET_SEALS;
22 use libc::F_SEAL_FUTURE_WRITE;
23 use libc::F_SEAL_GROW;
24 use libc::F_SEAL_SEAL;
25 use libc::F_SEAL_SHRINK;
26 use libc::F_SEAL_WRITE;
27 use libc::MFD_ALLOW_SEALING;
28 use once_cell::sync::Lazy;
29 
30 use crate::errno_result;
31 use crate::shm::PlatformSharedMemory;
32 use crate::trace;
33 use crate::AsRawDescriptor;
34 use crate::FromRawDescriptor;
35 use crate::Result;
36 use crate::SafeDescriptor;
37 use crate::SharedMemory;
38 
39 // from <sys/memfd.h>
40 const MFD_CLOEXEC: c_uint = 0x0001;
41 const MFD_NOEXEC_SEAL: c_uint = 0x0008;
42 
43 // SAFETY: It is caller's responsibility to ensure the args are valid and check the
44 // return value of the function.
memfd_create(name: *const c_char, flags: c_uint) -> c_int45 unsafe fn memfd_create(name: *const c_char, flags: c_uint) -> c_int {
46     syscall(SYS_memfd_create as c_long, name, flags) as c_int
47 }
48 
49 /// A set of memfd seals.
50 ///
51 /// An enumeration of each bit can be found at `fcntl(2)`.
52 #[derive(Copy, Clone, Default)]
53 pub struct MemfdSeals(i32);
54 
55 impl MemfdSeals {
56     /// Returns an empty set of memfd seals.
57     #[inline]
new() -> MemfdSeals58     pub fn new() -> MemfdSeals {
59         MemfdSeals(0)
60     }
61 
62     /// Gets the raw bitmask of seals enumerated in `fcntl(2)`.
63     #[inline]
bitmask(self) -> i3264     pub fn bitmask(self) -> i32 {
65         self.0
66     }
67 
68     /// True if the grow seal bit is present.
69     #[inline]
grow_seal(self) -> bool70     pub fn grow_seal(self) -> bool {
71         self.0 & F_SEAL_GROW != 0
72     }
73 
74     /// Sets the grow seal bit.
75     #[inline]
set_grow_seal(&mut self)76     pub fn set_grow_seal(&mut self) {
77         self.0 |= F_SEAL_GROW;
78     }
79 
80     /// True if the shrink seal bit is present.
81     #[inline]
shrink_seal(self) -> bool82     pub fn shrink_seal(self) -> bool {
83         self.0 & F_SEAL_SHRINK != 0
84     }
85 
86     /// Sets the shrink seal bit.
87     #[inline]
set_shrink_seal(&mut self)88     pub fn set_shrink_seal(&mut self) {
89         self.0 |= F_SEAL_SHRINK;
90     }
91 
92     /// True if the write seal bit is present.
93     #[inline]
write_seal(self) -> bool94     pub fn write_seal(self) -> bool {
95         self.0 & F_SEAL_WRITE != 0
96     }
97 
98     /// Sets the write seal bit.
99     #[inline]
set_write_seal(&mut self)100     pub fn set_write_seal(&mut self) {
101         self.0 |= F_SEAL_WRITE;
102     }
103 
104     /// True if the future write seal bit is present.
105     #[inline]
future_write_seal(self) -> bool106     pub fn future_write_seal(self) -> bool {
107         self.0 & F_SEAL_FUTURE_WRITE != 0
108     }
109 
110     /// Sets the future write seal bit.
111     #[inline]
set_future_write_seal(&mut self)112     pub fn set_future_write_seal(&mut self) {
113         self.0 |= F_SEAL_FUTURE_WRITE;
114     }
115 
116     /// True of the seal seal bit is present.
117     #[inline]
seal_seal(self) -> bool118     pub fn seal_seal(self) -> bool {
119         self.0 & F_SEAL_SEAL != 0
120     }
121 
122     /// Sets the seal seal bit.
123     #[inline]
set_seal_seal(&mut self)124     pub fn set_seal_seal(&mut self) {
125         self.0 |= F_SEAL_SEAL;
126     }
127 }
128 
129 static MFD_NOEXEC_SEAL_SUPPORTED: Lazy<bool> = Lazy::new(|| {
130     // SAFETY: We pass a valid zero-terminated C string and check the result.
131     let fd = unsafe {
132         // The memfd name used here does not need to be unique, since duplicates are allowed and
133         // will not cause failures.
134         memfd_create(
135             b"MFD_NOEXEC_SEAL_test\0".as_ptr() as *const c_char,
136             MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL,
137         )
138     };
139     if fd < 0 {
140         trace!("MFD_NOEXEC_SEAL is not supported");
141         false
142     } else {
143         trace!("MFD_NOEXEC_SEAL is supported");
144         // SAFETY: We know `fd` is a valid file descriptor owned by us.
145         unsafe {
146             close(fd);
147         }
148         true
149     }
150 });
151 
152 impl PlatformSharedMemory for SharedMemory {
153     /// Creates a new shared memory file descriptor with the specified `size` in bytes.
154     ///
155     /// `name` will appear in `/proc/self/fd/<shm fd>` for the purposes of debugging. The name does
156     /// not need to be unique.
157     ///
158     /// The file descriptor is opened with the close on exec flag and allows memfd sealing.
159     ///
160     /// If the `MFD_NOEXEC_SEAL` flag is supported, the resulting file will also be created with a
161     /// non-executable file mode (in other words, it cannot be passed to the `exec` family of system
162     /// calls).
new(debug_name: &CStr, size: u64) -> Result<SharedMemory>163     fn new(debug_name: &CStr, size: u64) -> Result<SharedMemory> {
164         let mut flags = MFD_CLOEXEC | MFD_ALLOW_SEALING;
165         if *MFD_NOEXEC_SEAL_SUPPORTED {
166             flags |= MFD_NOEXEC_SEAL;
167         }
168 
169         let shm_name = debug_name.as_ptr() as *const c_char;
170         // SAFETY:
171         // The following are safe because we give a valid C string and check the
172         // results of the memfd_create call.
173         let fd = unsafe { memfd_create(shm_name, flags) };
174         if fd < 0 {
175             return errno_result();
176         }
177         // SAFETY: Safe because fd is valid.
178         let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
179 
180         // Set the size of the memfd.
181         // SAFETY: Safe because we check the return value to ftruncate64 and all the args to the
182         // function are valid.
183         let ret = unsafe { ftruncate64(descriptor.as_raw_descriptor(), size as off64_t) };
184         if ret < 0 {
185             return errno_result();
186         }
187 
188         Ok(SharedMemory { descriptor, size })
189     }
190 
191     /// Creates a SharedMemory instance from a SafeDescriptor owning a reference to a
192     /// shared memory descriptor. Ownership of the underlying descriptor is transferred to the
193     /// new SharedMemory object.
from_safe_descriptor(descriptor: SafeDescriptor, size: u64) -> Result<SharedMemory>194     fn from_safe_descriptor(descriptor: SafeDescriptor, size: u64) -> Result<SharedMemory> {
195         Ok(SharedMemory { descriptor, size })
196     }
197 }
198 
199 pub trait SharedMemoryLinux {
200     /// Constructs a `SharedMemory` instance from a `File` that represents shared memory.
201     ///
202     /// The size of the resulting shared memory will be determined using `File::seek`. If the given
203     /// file's size can not be determined this way, this will return an error.
from_file(file: File) -> Result<SharedMemory>204     fn from_file(file: File) -> Result<SharedMemory>;
205 
206     /// Gets the memfd seals that have already been added to this.
207     ///
208     /// This may fail if this instance was not constructed from a memfd.
get_seals(&self) -> Result<MemfdSeals>209     fn get_seals(&self) -> Result<MemfdSeals>;
210 
211     /// Adds the given set of memfd seals.
212     ///
213     /// This may fail if this instance was not constructed from a memfd with sealing allowed or if
214     /// the seal seal (`F_SEAL_SEAL`) bit was already added.
add_seals(&mut self, seals: MemfdSeals) -> Result<()>215     fn add_seals(&mut self, seals: MemfdSeals) -> Result<()>;
216 }
217 
218 impl SharedMemoryLinux for SharedMemory {
from_file(mut file: File) -> Result<SharedMemory>219     fn from_file(mut file: File) -> Result<SharedMemory> {
220         let file_size = file.seek(SeekFrom::End(0))?;
221         Ok(SharedMemory {
222             descriptor: file.into(),
223             size: file_size,
224         })
225     }
226 
get_seals(&self) -> Result<MemfdSeals>227     fn get_seals(&self) -> Result<MemfdSeals> {
228         // SAFETY: Safe because we check the return value to fcntl and all the args to the
229         // function are valid.
230         let ret = unsafe { fcntl(self.descriptor.as_raw_descriptor(), F_GET_SEALS) };
231         if ret < 0 {
232             return errno_result();
233         }
234         Ok(MemfdSeals(ret))
235     }
236 
add_seals(&mut self, seals: MemfdSeals) -> Result<()>237     fn add_seals(&mut self, seals: MemfdSeals) -> Result<()> {
238         // SAFETY: Safe because we check the return value to fcntl and all the args to the
239         // function are valid.
240         let ret = unsafe { fcntl(self.descriptor.as_raw_descriptor(), F_ADD_SEALS, seals) };
241         if ret < 0 {
242             return errno_result();
243         }
244         Ok(())
245     }
246 }
247 
248 #[cfg(test)]
249 mod tests {
250     use std::fs::read_link;
251 
252     use libc::EINVAL;
253 
254     use crate::linux::SharedMemoryLinux;
255     use crate::pagesize;
256     use crate::AsRawDescriptor;
257     use crate::Error;
258     use crate::MemoryMappingBuilder;
259     use crate::Result;
260     use crate::SharedMemory;
261     use crate::VolatileMemory;
262 
263     /// Reads the name from the underlying file as a `String`.
264     ///
265     /// If the underlying file was not created with `SharedMemory::new` or with `memfd_create`, the
266     /// results are undefined. Because this returns a `String`, the name's bytes are interpreted as
267     /// utf-8.
read_name(shm: &SharedMemory) -> Result<String>268     fn read_name(shm: &SharedMemory) -> Result<String> {
269         let fd_path = format!("/proc/self/fd/{}", shm.as_raw_descriptor());
270         let link_name = read_link(fd_path)?;
271         link_name
272             .to_str()
273             .map(|s| {
274                 s.trim_start_matches("/memfd:")
275                     .trim_end_matches(" (deleted)")
276                     .to_owned()
277             })
278             .ok_or_else(|| Error::new(EINVAL))
279     }
280 
281     #[test]
new()282     fn new() {
283         const TEST_NAME: &str = "Name McCool Person";
284         let shm = SharedMemory::new(TEST_NAME, 0).expect("failed to create shared memory");
285         assert_eq!(read_name(&shm), Ok(TEST_NAME.to_owned()));
286     }
287 
288     #[test]
new_huge()289     fn new_huge() {
290         let shm = SharedMemory::new("test", 0x7fff_ffff_ffff_ffff)
291             .expect("failed to create shared memory");
292         assert_eq!(shm.size(), 0x7fff_ffff_ffff_ffff);
293     }
294 
295     #[test]
new_sealed()296     fn new_sealed() {
297         let mut shm = SharedMemory::new("test", 0).expect("failed to create shared memory");
298         let mut seals = shm.get_seals().expect("failed to get seals");
299         assert!(!seals.seal_seal());
300         seals.set_seal_seal();
301         shm.add_seals(seals).expect("failed to add seals");
302         seals = shm.get_seals().expect("failed to get seals");
303         assert!(seals.seal_seal());
304         // Adding more seals should be rejected by the kernel.
305         shm.add_seals(seals).unwrap_err();
306     }
307 
308     #[test]
mmap_page()309     fn mmap_page() {
310         let shm = SharedMemory::new("test", 4096).expect("failed to create shared memory");
311 
312         let mmap1 = MemoryMappingBuilder::new(shm.size() as usize)
313             .from_shared_memory(&shm)
314             .build()
315             .expect("failed to map shared memory");
316         let mmap2 = MemoryMappingBuilder::new(shm.size() as usize)
317             .from_shared_memory(&shm)
318             .build()
319             .expect("failed to map shared memory");
320 
321         assert_ne!(
322             mmap1.get_slice(0, 1).unwrap().as_ptr(),
323             mmap2.get_slice(0, 1).unwrap().as_ptr()
324         );
325 
326         mmap1
327             .get_slice(0, 4096)
328             .expect("failed to get mmap slice")
329             .write_bytes(0x45);
330 
331         for i in 0..4096 {
332             assert_eq!(mmap2.read_obj::<u8>(i).unwrap(), 0x45u8);
333         }
334     }
335 
336     #[test]
mmap_page_offset()337     fn mmap_page_offset() {
338         let shm = SharedMemory::new("test", 2 * pagesize() as u64)
339             .expect("failed to create shared memory");
340 
341         let mmap1 = MemoryMappingBuilder::new(shm.size() as usize)
342             .from_shared_memory(&shm)
343             .offset(pagesize() as u64)
344             .build()
345             .expect("failed to map shared memory");
346         let mmap2 = MemoryMappingBuilder::new(shm.size() as usize)
347             .from_shared_memory(&shm)
348             .build()
349             .expect("failed to map shared memory");
350 
351         mmap1
352             .get_slice(0, pagesize())
353             .expect("failed to get mmap slice")
354             .write_bytes(0x45);
355 
356         for i in 0..pagesize() {
357             assert_eq!(mmap2.read_obj::<u8>(i).unwrap(), 0);
358         }
359         for i in pagesize()..(2 * pagesize()) {
360             assert_eq!(mmap2.read_obj::<u8>(i).unwrap(), 0x45u8);
361         }
362     }
363 }
364