[libos] Add untrusted circular buffer
This commit is contained in:
		
							parent
							
								
									f3b1acf3ce
								
							
						
					
					
						commit
						f91cd60786
					
				| @ -2,9 +2,11 @@ | ||||
| mod alloc; | ||||
| mod slice_alloc; | ||||
| mod slice_ext; | ||||
| mod untrusted_circular_buf; | ||||
| 
 | ||||
| use super::*; | ||||
| 
 | ||||
| pub use self::alloc::UNTRUSTED_ALLOC; | ||||
| pub use self::slice_alloc::{UntrustedSlice, UntrustedSliceAlloc, UntrustedSliceAllocGuard}; | ||||
| pub use self::slice_ext::{SliceAsMutPtrAndLen, SliceAsPtrAndLen}; | ||||
| pub use self::untrusted_circular_buf::UntrustedCircularBuf; | ||||
|  | ||||
							
								
								
									
										208
									
								
								src/libos/src/untrusted/untrusted_circular_buf.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										208
									
								
								src/libos/src/untrusted/untrusted_circular_buf.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,208 @@ | ||||
| use std::mem::{self}; | ||||
| 
 | ||||
| use sgx_untrusted_alloc::UntrustedBox; | ||||
| 
 | ||||
| /// A circular buffer in untrusted memory.
 | ||||
| pub struct UntrustedCircularBuf { | ||||
|     // The underlying storage of the buffer.
 | ||||
|     //
 | ||||
|     // Note that to differentiate between the state of being full and empty,
 | ||||
|     // the actual capacity has to be `buf.len() - 1`
 | ||||
|     buf: UntrustedBox<[u8]>, | ||||
|     // The head of the buf, manipulated by consumer methods.
 | ||||
|     //
 | ||||
|     // Invariant: 0 <= head < len.
 | ||||
|     head: usize, | ||||
|     // The tail of the buf, manipulated by producer methods.
 | ||||
|     //
 | ||||
|     // Invariant: 0 <= tail < len.
 | ||||
|     tail: usize, // producer
 | ||||
| } | ||||
| 
 | ||||
| impl UntrustedCircularBuf { | ||||
|     /// Construct a circular buffer.
 | ||||
|     pub fn with_capacity(capacity: usize) -> Self { | ||||
|         debug_assert!(capacity > 0); | ||||
|         Self { | ||||
|             buf: UntrustedBox::new_uninit_slice(capacity), | ||||
|             head: 0, | ||||
|             tail: 0, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Produce some bytes.
 | ||||
|     pub fn produce(&mut self, buf: &[u8]) -> usize { | ||||
|         self.with_producer_view(|part0, part1| { | ||||
|             if buf.len() <= part0.len() { | ||||
|                 part0[..buf.len()].copy_from_slice(buf); | ||||
|                 return buf.len(); | ||||
|             } | ||||
| 
 | ||||
|             part0.copy_from_slice(&buf[..part0.len()]); | ||||
| 
 | ||||
|             let buf = &buf[part0.len()..]; | ||||
|             if buf.len() <= part1.len() { | ||||
|                 part1[..buf.len()].copy_from_slice(buf); | ||||
|                 return part0.len() + buf.len(); | ||||
|             } else { | ||||
|                 part1.copy_from_slice(&buf[..part1.len()]); | ||||
|                 return part0.len() + part1.len(); | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn produce_without_copy(&mut self, len: usize) -> usize { | ||||
|         self.with_producer_view(|part0, part1| { | ||||
|             // println!("part0: {}, part1: {}, produce len: {}", part0.len(), part1.len(), len);
 | ||||
|             len.min(part0.len() + part1.len()) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn producible(&self) -> usize { | ||||
|         self.capacity() - self.consumable() | ||||
|     } | ||||
| 
 | ||||
|     pub fn with_producer_view(&mut self, f: impl FnOnce(&mut [u8], &mut [u8]) -> usize) -> usize { | ||||
|         let head = self.head; | ||||
|         let tail = self.tail; | ||||
|         let len = self.buf.len(); | ||||
| 
 | ||||
|         let (range0, range1) = if tail >= head { | ||||
|             if head > 0 { | ||||
|                 (tail..len, 0..(head - 1)) | ||||
|             } else if tail < len - 1 { | ||||
|                 (tail..(len - 1), 0..0) | ||||
|             } else { | ||||
|                 (0..0, 0..0) | ||||
|             } | ||||
|         } else if tail < head - 1 { | ||||
|             (tail..(head - 1), 0..0) | ||||
|         } else { | ||||
|             (0..0, 0..0) | ||||
|         }; | ||||
|         // To reason about the above two resulting ranges, here is two figures that
 | ||||
|         // illustrate two typical settings.
 | ||||
|         //
 | ||||
|         // Setting 1:
 | ||||
|         //
 | ||||
|         // indexes:     0 1 2 3     ...     L-1
 | ||||
|         // bytes:      [ | |*|*|*|*|*| | | | ]
 | ||||
|         //                 ^         ^
 | ||||
|         // cursors:        head      tail
 | ||||
|         //
 | ||||
|         // Setting 2:
 | ||||
|         //
 | ||||
|         // indexes:     0 1 2 3     ...     L-1
 | ||||
|         // bytes:      [*|*|*|*| | | |*|*|*|*]
 | ||||
|         //                     ^     ^
 | ||||
|         // cursors:            tail  head
 | ||||
|         //
 | ||||
|         // where L = self.len and "*" indicates a stored byte.
 | ||||
| 
 | ||||
|         // Safety. It is ok to acquire two mutable subslices from the buf since the two
 | ||||
|         // subslices are guaranteed to be exclusive to each other.
 | ||||
|         let (part0, part1) = unsafe { | ||||
|             #![allow(mutable_transmutes)] | ||||
|             let part0 = mem::transmute::<&[u8], &mut [u8]>(&self.buf[range0]); | ||||
|             let part1 = mem::transmute::<&[u8], &mut [u8]>(&self.buf[range1]); | ||||
|             (part0, part1) | ||||
|         }; | ||||
| 
 | ||||
|         let bytes_produced = f(part0, part1); | ||||
|         assert!(bytes_produced <= self.producible()); | ||||
| 
 | ||||
|         self.tail = (tail + bytes_produced) % len; | ||||
|         bytes_produced | ||||
|     } | ||||
| 
 | ||||
|     pub fn consume(&mut self, buf: &mut [u8]) -> usize { | ||||
|         self.with_consumer_view(|part0, part1| { | ||||
|             if buf.len() <= part0.len() { | ||||
|                 buf.copy_from_slice(&part0[..buf.len()]); | ||||
|                 return buf.len(); | ||||
|             } | ||||
| 
 | ||||
|             buf[..part0.len()].copy_from_slice(part0); | ||||
| 
 | ||||
|             let buf = &mut buf[part0.len()..]; | ||||
|             if buf.len() <= part1.len() { | ||||
|                 buf.copy_from_slice(&part1[..buf.len()]); | ||||
|                 return part0.len() + buf.len(); | ||||
|             } else { | ||||
|                 buf[..part1.len()].copy_from_slice(part1); | ||||
|                 return part0.len() + part1.len(); | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn consume_without_copy(&mut self, len: usize) -> usize { | ||||
|         self.with_consumer_view(|part0, part1| len.min(part0.len() + part1.len())) | ||||
|     } | ||||
| 
 | ||||
|     pub fn with_consumer_view(&mut self, f: impl FnOnce(&[u8], &[u8]) -> usize) -> usize { | ||||
|         let head = self.head; | ||||
|         let tail = self.tail; | ||||
|         let len = self.buf.len(); | ||||
| 
 | ||||
|         let (range0, range1) = if head <= tail { | ||||
|             (head..tail, 0..0) | ||||
|         } else { | ||||
|             (head..len, 0..tail) | ||||
|         }; | ||||
| 
 | ||||
|         let part0 = &self.buf[range0]; | ||||
|         let part1 = &self.buf[range1]; | ||||
| 
 | ||||
|         let bytes_consumed = f(part0, part1); | ||||
|         assert!(bytes_consumed <= self.consumable()); | ||||
| 
 | ||||
|         self.head = (head + bytes_consumed) % len; | ||||
|         bytes_consumed | ||||
|     } | ||||
| 
 | ||||
|     pub fn consumable(&self) -> usize { | ||||
|         let head = self.head; | ||||
|         let tail = self.tail; | ||||
|         let len = self.buf.len(); | ||||
| 
 | ||||
|         if head <= tail { | ||||
|             tail - head | ||||
|         } else { | ||||
|             (len - head) + tail | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn capacity(&self) -> usize { | ||||
|         self.buf.len() - 1 | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_full(&self) -> bool { | ||||
|         self.producible() == 0 | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         self.consumable() == 0 | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a slice that contains the entire buffer.
 | ||||
|     #[allow(dead_code)] | ||||
|     pub fn as_slice(&self) -> &[u8] { | ||||
|         &*self.buf | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a mutable slice that contains the entire buffer.
 | ||||
|     #[allow(dead_code)] | ||||
|     pub fn as_mut_slice(&mut self) -> &mut [u8] { | ||||
|         &mut *self.buf | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::fmt::Debug for UntrustedCircularBuf { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("UntrustedCircularBuf") | ||||
|             .field("capacity", &self.capacity()) | ||||
|             .field("producible", &self.producible()) | ||||
|             .field("consumable", &self.consumable()) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| @ -2,6 +2,7 @@ use super::*; | ||||
| use std::ffi::{CStr, CString}; | ||||
| use std::mem::size_of; | ||||
| use std::ptr; | ||||
| use std::slice; | ||||
| use vm::VMRange; | ||||
| 
 | ||||
| /// Memory utilities that deals with primitive types passed from user process
 | ||||
| @ -42,6 +43,26 @@ pub mod from_user { | ||||
|         check_array(user_buf, count) | ||||
|     } | ||||
| 
 | ||||
|     pub fn make_slice<'a, T>(user_buf: *const T, count: usize) -> Result<&'a [T]> { | ||||
|         check_array(user_buf, count)?; | ||||
|         Ok(unsafe { slice::from_raw_parts(user_buf, count) }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn make_mut_slice<'a, T>(user_buf: *mut T, count: usize) -> Result<&'a mut [T]> { | ||||
|         check_mut_array(user_buf, count)?; | ||||
|         Ok(unsafe { slice::from_raw_parts_mut(user_buf, count) }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn make_ref<'a, T>(user_ptr: *const T) -> Result<&'a T> { | ||||
|         check_ptr(user_ptr)?; | ||||
|         Ok(unsafe { &*user_ptr }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn make_mut_ref<'a, T>(user_ptr: *mut T) -> Result<&'a mut T> { | ||||
|         check_mut_ptr(user_ptr)?; | ||||
|         Ok(unsafe { &mut *user_ptr }) | ||||
|     } | ||||
| 
 | ||||
|     /// Clone a C-string from the user process safely
 | ||||
|     pub fn clone_cstring_safely(out_ptr: *const c_char) -> Result<CString> { | ||||
|         if out_ptr.is_null() { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user