1 // Copyright 2023 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 //! Cross platform [`IoBuf`] and [`IoBufMut`] types wrapping `iovec`/`WSABUF`. 6 7 use std::fmt; 8 use std::fmt::Debug; 9 use std::marker::PhantomData; 10 use std::slice; 11 12 pub use crate::IoBuf; 13 14 pub(crate) trait PlatformIoBuf { new(ptr: *mut u8, len: usize) -> Self15 fn new(ptr: *mut u8, len: usize) -> Self; len(&self) -> usize16 fn len(&self) -> usize; ptr(&self) -> *mut u817 fn ptr(&self) -> *mut u8; set_len(&mut self, len: usize)18 fn set_len(&mut self, len: usize); set_ptr(&mut self, ptr: *mut u8)19 fn set_ptr(&mut self, ptr: *mut u8); 20 } 21 22 /// Cross-platform mutable buffer. 23 /// 24 /// This type is essentialy `std::io::IoSliceMut`, and guaranteed to be ABI-compatible with 25 /// `libc::iovec` on Linux/`WSABUF` on Windows; however, it does NOT automatically deref to `&mut 26 /// [u8]`, which is critical because it can point to guest memory. (Guest memory is implicitly 27 /// mutably borrowed by the guest, so another mutable borrow would violate Rust assumptions about 28 /// references.) 29 #[derive(Copy, Clone)] 30 #[repr(transparent)] 31 pub struct IoBufMut<'a> { 32 iobuf: IoBuf, 33 phantom: PhantomData<&'a mut [u8]>, 34 } 35 36 impl<'a> IoBufMut<'a> { new(buf: &mut [u8]) -> IoBufMut<'a>37 pub fn new(buf: &mut [u8]) -> IoBufMut<'a> { 38 // SAFETY: 39 // Safe because buf's memory is of the supplied length, and 40 // guaranteed to exist for the lifetime of the returned value. 41 unsafe { Self::from_raw_parts(buf.as_mut_ptr(), buf.len()) } 42 } 43 44 /// Creates a `IoBufMut` from a pointer and a length. 45 /// 46 /// # Safety 47 /// 48 /// In order to use this method safely, `addr` must be valid for reads and writes of `len` bytes 49 /// and should live for the entire duration of lifetime `'a`. from_raw_parts(addr: *mut u8, len: usize) -> IoBufMut<'a>50 pub unsafe fn from_raw_parts(addr: *mut u8, len: usize) -> IoBufMut<'a> { 51 IoBufMut { 52 iobuf: IoBuf::new(addr, len), 53 phantom: PhantomData, 54 } 55 } 56 57 /// Creates a `IoBufMut` from an IoBuf. 58 /// 59 /// # Safety 60 /// 61 /// In order to use this method safely, `iobuf` must be valid for reads and writes through its 62 /// length and should live for the entire duration of lifetime `'a`. from_iobuf(iobuf: IoBuf) -> IoBufMut<'a>63 pub unsafe fn from_iobuf(iobuf: IoBuf) -> IoBufMut<'a> { 64 IoBufMut { 65 iobuf, 66 phantom: PhantomData, 67 } 68 } 69 70 /// Advance the internal position of the buffer. 71 /// 72 /// Panics if `count > self.len()`. advance(&mut self, count: usize)73 pub fn advance(&mut self, count: usize) { 74 assert!(count <= self.len()); 75 76 self.iobuf.set_len(self.len() - count); 77 78 // SAFETY: 79 // Safe because we've checked that `count <= self.len()` so both the starting and resulting 80 // pointer are within the bounds of the allocation. 81 self.iobuf.set_ptr(unsafe { self.as_mut_ptr().add(count) }); 82 } 83 84 /// Shorten the length of the buffer. 85 /// 86 /// Has no effect if `len > self.len()`. truncate(&mut self, len: usize)87 pub fn truncate(&mut self, len: usize) { 88 if len < self.len() { 89 self.iobuf.set_len(len); 90 } 91 } 92 93 #[inline] len(&self) -> usize94 pub fn len(&self) -> usize { 95 self.iobuf.len() 96 } 97 98 #[inline] is_empty(&self) -> bool99 pub fn is_empty(&self) -> bool { 100 self.len() == 0 101 } 102 103 /// Gets a const pointer to this slice's memory. 104 #[inline] as_ptr(&self) -> *const u8105 pub fn as_ptr(&self) -> *const u8 { 106 self.iobuf.ptr() as *const u8 107 } 108 109 /// Gets a mutable pointer to this slice's memory. 110 #[inline] as_mut_ptr(&self) -> *mut u8111 pub fn as_mut_ptr(&self) -> *mut u8 { 112 self.iobuf.ptr() 113 } 114 115 /// Converts a slice of `IoBufMut`s into a slice of `IoBuf`s. 116 #[allow(clippy::wrong_self_convention)] 117 #[inline] as_iobufs<'slice>(iovs: &'slice [IoBufMut<'_>]) -> &'slice [IoBuf]118 pub fn as_iobufs<'slice>(iovs: &'slice [IoBufMut<'_>]) -> &'slice [IoBuf] { 119 // SAFETY: 120 // Safe because `IoBufMut` is ABI-compatible with `IoBuf`. 121 unsafe { slice::from_raw_parts(iovs.as_ptr() as *const IoBuf, iovs.len()) } 122 } 123 124 /// Converts a mutable slice of `IoBufMut`s into a mutable slice of `IoBuf`s. 125 #[inline] as_iobufs_mut<'slice>(iovs: &'slice mut [IoBufMut<'_>]) -> &'slice mut [IoBuf]126 pub fn as_iobufs_mut<'slice>(iovs: &'slice mut [IoBufMut<'_>]) -> &'slice mut [IoBuf] { 127 // SAFETY: 128 // Safe because `IoBufMut` is ABI-compatible with `IoBuf`. 129 unsafe { slice::from_raw_parts_mut(iovs.as_mut_ptr() as *mut IoBuf, iovs.len()) } 130 } 131 } 132 133 impl<'a> AsRef<IoBuf> for IoBufMut<'a> { as_ref(&self) -> &IoBuf134 fn as_ref(&self) -> &IoBuf { 135 &self.iobuf 136 } 137 } 138 139 impl<'a> AsMut<IoBuf> for IoBufMut<'a> { as_mut(&mut self) -> &mut IoBuf140 fn as_mut(&mut self) -> &mut IoBuf { 141 &mut self.iobuf 142 } 143 } 144 145 // SAFETY: 146 // It's safe to implement Send + Sync for this type for the same reason that `std::io::IoSliceMut` 147 // is Send + Sync. Internally, it contains a pointer and a length. The integer length is safely Send 148 // + Sync. There's nothing wrong with sending a pointer between threads and de-referencing the 149 // pointer requires an unsafe block anyway. See also https://github.com/rust-lang/rust/pull/70342. 150 unsafe impl<'a> Send for IoBufMut<'a> {} 151 // SAFETY: See comments for impl Send 152 unsafe impl<'a> Sync for IoBufMut<'a> {} 153 154 impl<'a> Debug for IoBufMut<'a> { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 156 f.debug_struct("IoBufMut") 157 .field("ptr", &self.iobuf.ptr()) 158 .field("len", &self.iobuf.len()) 159 .finish() 160 } 161 } 162