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