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    /// Read bytes from the SimpleBuffer into `buffer`, up to the size of the supplied buffer. The
43    /// actual number of bytes copied is returned.
44    pub fn read(&self, buffer: &mut [u8]) -> usize {
45        let base_raw = self.ptr_to_base();
46        // Note that our len is not bounded by a previous write. But Twizzler objects are
47        // 0-initialized by default, so all bytes are initialized to 0u8. If any other data _was_
48        // written to the object, that still can be read as bytes.
49        let len = core::cmp::min(buffer.len(), self.max_len());
50        // Safety: technically, we cannot statically assert that no one else is writing this memory.
51        // However, since we are reading bytes directly, we will assert that this is safe,
52        // up to seeing torn writes. That is still UB, but it's the best we can do before
53        // having to introduce synchronization overhead, but since this is intended to be
54        // used by secure gates, that synchronization will have occurred via the secure gate call.
55        // While we cannot stop another compartment from violating this assumption, we are still
56        // reading bytes from object memory and not interpreting them. If a consumer of this
57        // interface chooses to cast those bytes into another type, or process them
58        // as UTF-8, or something, it is up to them to uphold safety guarantees (e.g. we cannot
59        // assume it is valid UTF-8).
60        let base = unsafe { core::slice::from_raw_parts(base_raw, len) };
61        (&mut buffer[0..len]).copy_from_slice(base);
62        len
63    }
64
65    pub fn read_offset(&self, buffer: &mut [u8], offset: usize) -> usize {
66        let base_raw = self.ptr_to_base();
67        if offset >= self.max_len() {
68            return 0;
69        }
70        let len = core::cmp::min(buffer.len(), self.max_len() - offset);
71        let base = unsafe { core::slice::from_raw_parts(base_raw.add(offset), len) };
72        (&mut buffer[0..len]).copy_from_slice(base);
73        len
74    }
75
76    /// Write bytes from `buffer` into the SimpleBuffer, up to the size of the supplied buffer. The
77    /// actual number of bytes copied is returned.
78    pub fn write(&mut self, buffer: &[u8]) -> usize {
79        let base_raw = self.mut_ptr_to_base();
80        let len = core::cmp::min(buffer.len(), self.max_len());
81        // Safety: See read function.
82        let base = unsafe { core::slice::from_raw_parts_mut(base_raw, len) };
83        base.copy_from_slice(&buffer[0..len]);
84        len
85    }
86
87    /// Write bytes from `buffer` into the SimpleBuffer at provided offset, up to the size of the
88    /// supplied buffer, minus the offset. The actual number of bytes copied is returned.
89    pub fn write_offset(&mut self, buffer: &[u8], offset: usize) -> usize {
90        let base_raw = self.mut_ptr_to_base();
91        if offset >= self.max_len() {
92            return 0;
93        }
94        let len = core::cmp::min(buffer.len(), self.max_len() - offset);
95        // Safety: See read function.
96        let base = unsafe { core::slice::from_raw_parts_mut(base_raw.add(offset), len) };
97        base.copy_from_slice(&buffer[0..len]);
98        len
99    }
100}
101
102#[cfg(test)]
103mod test {
104    use twizzler_abi::{
105        object::Protections,
106        syscall::{sys_object_create, BackingType, LifetimeType, ObjectCreate, ObjectCreateFlags},
107    };
108    use twizzler_rt_abi::object::{MapFlags, ObjectHandle};
109
110    use super::*;
111
112    fn new_handle() -> ObjectHandle {
113        let id = sys_object_create(
114            ObjectCreate::new(
115                BackingType::Normal,
116                LifetimeType::Volatile,
117                None,
118                ObjectCreateFlags::empty(),
119                Protections::all(),
120            ),
121            &[],
122            &[],
123        )
124        .unwrap();
125
126        twizzler_rt_abi::object::twz_rt_map_object(id, MapFlags::READ | MapFlags::WRITE).unwrap()
127    }
128
129    #[test]
130    fn transfer() {
131        let obj = new_handle();
132        let mut sb = SimpleBuffer::new(obj);
133
134        let data = b"simple buffer test!";
135        let wlen = sb.write(data);
136        let mut buf = [0u8; 19];
137        assert_eq!(buf.len(), data.len());
138        assert_eq!(buf.len(), wlen);
139
140        let rlen = sb.read(&mut buf);
141        assert_eq!(rlen, wlen);
142        assert_eq!(&buf, data);
143    }
144}