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