secgate/util/
buffer.rs

1use twizzler_abi::object::{MAX_SIZE, NULLPAGE_SIZE};
2use twizzler_rt_abi::object::ObjectHandle;
3
4/// A simple buffer to use for transferring bytes between compartments, using shared memory via
5/// objects underneath.
6pub struct SimpleBuffer {
7    handle: ObjectHandle,
8}
9
10impl core::fmt::Debug for SimpleBuffer {
11    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12        f.debug_struct("SimpleBuffer")
13            .field("id", &self.handle.id())
14            .finish_non_exhaustive()
15    }
16}
17
18impl SimpleBuffer {
19    fn ptr_to_base(&self) -> *const u8 {
20        unsafe { self.handle.start().add(NULLPAGE_SIZE) }
21    }
22
23    fn mut_ptr_to_base(&mut self) -> *mut u8 {
24        unsafe { self.handle.start().add(NULLPAGE_SIZE) }
25    }
26
27    /// Build a new SimpleBuffer from an object handle.
28    pub fn new(handle: ObjectHandle) -> Self {
29        Self { handle }
30    }
31
32    /// Returns the maximum length of a read or write.
33    pub fn max_len(&self) -> usize {
34        MAX_SIZE - NULLPAGE_SIZE * 2
35    }
36
37    /// Get the underlying object handle.
38    pub fn handle(&self) -> &ObjectHandle {
39        &self.handle
40    }
41
42    pub fn into_handle(self) -> ObjectHandle {
43        self.handle
44    }
45
46    /// Read bytes from the SimpleBuffer into `buffer`, up to the size of the supplied buffer. The
47    /// actual number of bytes copied is returned.
48    pub fn read(&self, buffer: &mut [u8]) -> usize {
49        let base_raw = self.ptr_to_base();
50        // Note that our len is not bounded by a previous write. But Twizzler objects are
51        // 0-initialized by default, so all bytes are initialized to 0u8. If any other data _was_
52        // written to the object, that still can be read as bytes.
53        let len = core::cmp::min(buffer.len(), self.max_len());
54        // Safety: technically, we cannot statically assert that no one else is writing this memory.
55        // However, since we are reading bytes directly, we will assert that this is safe,
56        // up to seeing torn writes. That is still UB, but it's the best we can do before
57        // having to introduce synchronization overhead, but since this is intended to be
58        // used by secure gates, that synchronization will have occurred via the secure gate call.
59        // While we cannot stop another compartment from violating this assumption, we are still
60        // reading bytes from object memory and not interpreting them. If a consumer of this
61        // interface chooses to cast those bytes into another type, or process them
62        // as UTF-8, or something, it is up to them to uphold safety guarantees (e.g. we cannot
63        // assume it is valid UTF-8).
64        let base = unsafe { core::slice::from_raw_parts(base_raw, len) };
65        (&mut buffer[0..len]).copy_from_slice(base);
66        len
67    }
68
69    pub fn read_offset(&self, buffer: &mut [u8], offset: usize) -> usize {
70        let base_raw = self.ptr_to_base();
71        if offset >= self.max_len() {
72            return 0;
73        }
74        let len = core::cmp::min(buffer.len(), self.max_len() - offset);
75        let base = unsafe { core::slice::from_raw_parts(base_raw.add(offset), len) };
76        (&mut buffer[0..len]).copy_from_slice(base);
77        len
78    }
79
80    /// Write bytes from `buffer` into the SimpleBuffer, up to the size of the supplied buffer. The
81    /// actual number of bytes copied is returned.
82    pub fn write(&mut self, buffer: &[u8]) -> usize {
83        let base_raw = self.mut_ptr_to_base();
84        let len = core::cmp::min(buffer.len(), self.max_len());
85        // Safety: See read function.
86        let base = unsafe { core::slice::from_raw_parts_mut(base_raw, len) };
87        base.copy_from_slice(&buffer[0..len]);
88        len
89    }
90
91    /// Write bytes from `buffer` into the SimpleBuffer at provided offset, up to the size of the
92    /// supplied buffer, minus the offset. The actual number of bytes copied is returned.
93    pub fn write_offset(&mut self, buffer: &[u8], offset: usize) -> usize {
94        let base_raw = self.mut_ptr_to_base();
95        if offset >= self.max_len() {
96            return 0;
97        }
98        let len = core::cmp::min(buffer.len(), self.max_len() - offset);
99        // Safety: See read function.
100        let base = unsafe { core::slice::from_raw_parts_mut(base_raw.add(offset), len) };
101        base.copy_from_slice(&buffer[0..len]);
102        len
103    }
104}
105
106#[cfg(test)]
107mod test {
108    use twizzler_abi::{
109        object::Protections,
110        syscall::{sys_object_create, BackingType, LifetimeType, ObjectCreate, ObjectCreateFlags},
111    };
112    use twizzler_rt_abi::object::{MapFlags, ObjectHandle};
113
114    use super::*;
115
116    fn new_handle() -> ObjectHandle {
117        let id = sys_object_create(
118            ObjectCreate::new(
119                BackingType::Normal,
120                LifetimeType::Volatile,
121                None,
122                ObjectCreateFlags::empty(),
123                Protections::all(),
124            ),
125            &[],
126            &[],
127        )
128        .unwrap();
129
130        twizzler_rt_abi::object::twz_rt_map_object(id, MapFlags::READ | MapFlags::WRITE).unwrap()
131    }
132
133    #[test]
134    fn transfer() {
135        let obj = new_handle();
136        let mut sb = SimpleBuffer::new(obj);
137
138        let data = b"simple buffer test!";
139        let wlen = sb.write(data);
140        let mut buf = [0u8; 19];
141        assert_eq!(buf.len(), data.len());
142        assert_eq!(buf.len(), wlen);
143
144        let rlen = sb.read(&mut buf);
145        assert_eq!(rlen, wlen);
146        assert_eq!(&buf, data);
147    }
148}