mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 12:34:31 +00:00
clippy: enforce safety block comments
BUG=b:316174930 TEST=none Change-Id: I5c7811b2c548155aa003e4b71a54bbc16e2f2588 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/5120567 Commit-Queue: Vikram Auradkar <auradkar@google.com> Reviewed-by: Dennis Kempin <denniskempin@google.com>
This commit is contained in:
parent
90c2ff0432
commit
2768f223ee
241 changed files with 2668 additions and 578 deletions
|
@ -25,4 +25,5 @@ rustflags = [
|
||||||
"-Aclippy::unreadable_literal",
|
"-Aclippy::unreadable_literal",
|
||||||
"-Aclippy::useless_let_if_seq",
|
"-Aclippy::useless_let_if_seq",
|
||||||
"-Aclippy::useless_transmute",
|
"-Aclippy::useless_transmute",
|
||||||
|
"-Dclippy::undocumented_unsafe_blocks",
|
||||||
]
|
]
|
||||||
|
|
|
@ -258,6 +258,7 @@ impl AudioMemoryMapping {
|
||||||
warn!("Accessing unallocated region");
|
warn!("Accessing unallocated region");
|
||||||
return &mut self.zero_buffer;
|
return &mut self.zero_buffer;
|
||||||
}
|
}
|
||||||
|
// SAFETY:
|
||||||
// safe because the region returned is owned by self.memory_mapping
|
// safe because the region returned is owned by self.memory_mapping
|
||||||
unsafe { slice::from_raw_parts_mut(self.memory_mapping.as_ptr().add(offset), len) }
|
unsafe { slice::from_raw_parts_mut(self.memory_mapping.as_ptr().add(offset), len) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ use std::cmp::min;
|
||||||
/// let layout = Layout::from_size_align(size, mem::align_of::<Header>()).unwrap();
|
/// let layout = Layout::from_size_align(size, mem::align_of::<Header>()).unwrap();
|
||||||
/// let mut allocation = LayoutAllocation::zeroed(layout);
|
/// let mut allocation = LayoutAllocation::zeroed(layout);
|
||||||
///
|
///
|
||||||
|
/// // SAFETY:
|
||||||
/// // Safe to obtain an exclusive reference because there are no other
|
/// // Safe to obtain an exclusive reference because there are no other
|
||||||
/// // references to the allocation yet and all-zero is a valid bit pattern for
|
/// // references to the allocation yet and all-zero is a valid bit pattern for
|
||||||
/// // our header.
|
/// // our header.
|
||||||
|
@ -57,10 +58,9 @@ impl LayoutAllocation {
|
||||||
/// incompatible with its type, for example an uninitialized bool or enum.
|
/// incompatible with its type, for example an uninitialized bool or enum.
|
||||||
pub fn uninitialized(layout: Layout) -> Self {
|
pub fn uninitialized(layout: Layout) -> Self {
|
||||||
let ptr = if layout.size() > 0 {
|
let ptr = if layout.size() > 0 {
|
||||||
unsafe {
|
// SAFETY:
|
||||||
// Safe as long as we guarantee layout.size() > 0.
|
// Safe as long as we guarantee layout.size() > 0.
|
||||||
alloc(layout)
|
unsafe { alloc(layout) }
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
layout.align() as *mut u8
|
layout.align() as *mut u8
|
||||||
};
|
};
|
||||||
|
@ -77,10 +77,9 @@ impl LayoutAllocation {
|
||||||
/// one of the fields has type NonZeroUsize.
|
/// one of the fields has type NonZeroUsize.
|
||||||
pub fn zeroed(layout: Layout) -> Self {
|
pub fn zeroed(layout: Layout) -> Self {
|
||||||
let ptr = if layout.size() > 0 {
|
let ptr = if layout.size() > 0 {
|
||||||
unsafe {
|
// SAFETY:
|
||||||
// Safe as long as we guarantee layout.size() > 0.
|
// Safe as long as we guarantee layout.size() > 0.
|
||||||
alloc_zeroed(layout)
|
unsafe { alloc_zeroed(layout) }
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
layout.align() as *mut u8
|
layout.align() as *mut u8
|
||||||
};
|
};
|
||||||
|
@ -159,8 +158,9 @@ impl LayoutAllocation {
|
||||||
impl Drop for LayoutAllocation {
|
impl Drop for LayoutAllocation {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.layout.size() > 0 {
|
if self.layout.size() > 0 {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe as long as we guarantee layout.size() > 0.
|
||||||
unsafe {
|
unsafe {
|
||||||
// Safe as long as we guarantee layout.size() > 0.
|
|
||||||
dealloc(self.ptr, self.layout);
|
dealloc(self.ptr, self.layout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,6 +178,8 @@ mod tests {
|
||||||
fn test_as_slice_u32() {
|
fn test_as_slice_u32() {
|
||||||
let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
|
let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
|
||||||
let allocation = LayoutAllocation::zeroed(layout);
|
let allocation = LayoutAllocation::zeroed(layout);
|
||||||
|
// SAFETY:
|
||||||
|
// Slice less than the allocation size, which will return a slice of only the requested length.
|
||||||
let slice: &[u32] = unsafe { allocation.as_slice(15) };
|
let slice: &[u32] = unsafe { allocation.as_slice(15) };
|
||||||
assert_eq!(slice.len(), 15);
|
assert_eq!(slice.len(), 15);
|
||||||
assert_eq!(slice[0], 0);
|
assert_eq!(slice[0], 0);
|
||||||
|
@ -189,6 +191,7 @@ mod tests {
|
||||||
let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
|
let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
|
||||||
let allocation = LayoutAllocation::zeroed(layout);
|
let allocation = LayoutAllocation::zeroed(layout);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Slice less than the allocation size, which will return a slice of only the requested length.
|
// Slice less than the allocation size, which will return a slice of only the requested length.
|
||||||
let slice: &[u32] = unsafe { allocation.as_slice(5) };
|
let slice: &[u32] = unsafe { allocation.as_slice(5) };
|
||||||
assert_eq!(slice.len(), 5);
|
assert_eq!(slice.len(), 5);
|
||||||
|
@ -199,6 +202,7 @@ mod tests {
|
||||||
let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
|
let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
|
||||||
let allocation = LayoutAllocation::zeroed(layout);
|
let allocation = LayoutAllocation::zeroed(layout);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Slice more than the allocation size, which will clamp the returned slice len to the limit.
|
// Slice more than the allocation size, which will clamp the returned slice len to the limit.
|
||||||
let slice: &[u32] = unsafe { allocation.as_slice(100) };
|
let slice: &[u32] = unsafe { allocation.as_slice(100) };
|
||||||
assert_eq!(slice.len(), 15);
|
assert_eq!(slice.len(), 15);
|
||||||
|
@ -210,6 +214,7 @@ mod tests {
|
||||||
let layout = Layout::from_size_align(size_of::<u32>() * 15 + 2, align_of::<u32>()).unwrap();
|
let layout = Layout::from_size_align(size_of::<u32>() * 15 + 2, align_of::<u32>()).unwrap();
|
||||||
let allocation = LayoutAllocation::zeroed(layout);
|
let allocation = LayoutAllocation::zeroed(layout);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Slice as many u32s as possible, which should return a slice that only includes the full
|
// Slice as many u32s as possible, which should return a slice that only includes the full
|
||||||
// u32s, not the trailing 2 bytes.
|
// u32s, not the trailing 2 bytes.
|
||||||
let slice: &[u32] = unsafe { allocation.as_slice(100) };
|
let slice: &[u32] = unsafe { allocation.as_slice(100) };
|
||||||
|
|
|
@ -111,6 +111,7 @@ impl TryFrom<&dyn AsRawDescriptor> for SafeDescriptor {
|
||||||
/// TODO(b/191800567): this API has sharp edges on Windows. We should evaluate making some
|
/// TODO(b/191800567): this API has sharp edges on Windows. We should evaluate making some
|
||||||
/// adjustments to smooth those edges.
|
/// adjustments to smooth those edges.
|
||||||
fn try_from(rd: &dyn AsRawDescriptor) -> std::result::Result<Self, Self::Error> {
|
fn try_from(rd: &dyn AsRawDescriptor) -> std::result::Result<Self, Self::Error> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the underlying raw descriptor is guaranteed valid by rd's existence.
|
// Safe because the underlying raw descriptor is guaranteed valid by rd's existence.
|
||||||
//
|
//
|
||||||
// Note that we are cloning the underlying raw descriptor since we have no guarantee of
|
// Note that we are cloning the underlying raw descriptor since we have no guarantee of
|
||||||
|
@ -129,6 +130,7 @@ impl TryFrom<&dyn AsRawDescriptor> for SafeDescriptor {
|
||||||
|
|
||||||
impl From<File> for SafeDescriptor {
|
impl From<File> for SafeDescriptor {
|
||||||
fn from(f: File) -> SafeDescriptor {
|
fn from(f: File) -> SafeDescriptor {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we own the File at this point.
|
// Safe because we own the File at this point.
|
||||||
unsafe { SafeDescriptor::from_raw_descriptor(f.into_raw_descriptor()) }
|
unsafe { SafeDescriptor::from_raw_descriptor(f.into_raw_descriptor()) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -398,7 +398,9 @@ pub mod with_as_descriptor {
|
||||||
{
|
{
|
||||||
super::deserialize_descriptor(de)
|
super::deserialize_descriptor(de)
|
||||||
.map(IntoRawDescriptor::into_raw_descriptor)
|
.map(IntoRawDescriptor::into_raw_descriptor)
|
||||||
.map(|rd| unsafe { T::from_raw_descriptor(rd) })
|
.map(|rd|
|
||||||
|
// SAFETY: rd is expected to be valid for the duration of the call.
|
||||||
|
unsafe { T::from_raw_descriptor(rd) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,9 +464,9 @@ mod tests {
|
||||||
use super::super::SerializeDescriptors;
|
use super::super::SerializeDescriptors;
|
||||||
|
|
||||||
fn deserialize<T: DeserializeOwned>(json: &str, descriptors: &[RawDescriptor]) -> T {
|
fn deserialize<T: DeserializeOwned>(json: &str, descriptors: &[RawDescriptor]) -> T {
|
||||||
let safe_descriptors = descriptors
|
let safe_descriptors = descriptors.iter().map(|&v|
|
||||||
.iter()
|
// SAFETY: `descriptor` is expected to be valid.
|
||||||
.map(|&v| unsafe { SafeDescriptor::from_raw_descriptor(v) });
|
unsafe { SafeDescriptor::from_raw_descriptor(v) });
|
||||||
|
|
||||||
deserialize_with_descriptors(|| serde_json::from_str(json), safe_descriptors).unwrap()
|
deserialize_with_descriptors(|| serde_json::from_str(json), safe_descriptors).unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ pub struct IoBufMut<'a> {
|
||||||
|
|
||||||
impl<'a> IoBufMut<'a> {
|
impl<'a> IoBufMut<'a> {
|
||||||
pub fn new(buf: &mut [u8]) -> IoBufMut<'a> {
|
pub fn new(buf: &mut [u8]) -> IoBufMut<'a> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because buf's memory is of the supplied length, and
|
// Safe because buf's memory is of the supplied length, and
|
||||||
// guaranteed to exist for the lifetime of the returned value.
|
// guaranteed to exist for the lifetime of the returned value.
|
||||||
unsafe { Self::from_raw_parts(buf.as_mut_ptr(), buf.len()) }
|
unsafe { Self::from_raw_parts(buf.as_mut_ptr(), buf.len()) }
|
||||||
|
@ -74,6 +75,7 @@ impl<'a> IoBufMut<'a> {
|
||||||
|
|
||||||
self.iobuf.set_len(self.len() - count);
|
self.iobuf.set_len(self.len() - count);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we've checked that `count <= self.len()` so both the starting and resulting
|
// Safe because we've checked that `count <= self.len()` so both the starting and resulting
|
||||||
// pointer are within the bounds of the allocation.
|
// pointer are within the bounds of the allocation.
|
||||||
self.iobuf.set_ptr(unsafe { self.as_mut_ptr().add(count) });
|
self.iobuf.set_ptr(unsafe { self.as_mut_ptr().add(count) });
|
||||||
|
@ -114,6 +116,7 @@ impl<'a> IoBufMut<'a> {
|
||||||
#[allow(clippy::wrong_self_convention)]
|
#[allow(clippy::wrong_self_convention)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_iobufs<'slice>(iovs: &'slice [IoBufMut<'_>]) -> &'slice [IoBuf] {
|
pub fn as_iobufs<'slice>(iovs: &'slice [IoBufMut<'_>]) -> &'slice [IoBuf] {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `IoBufMut` is ABI-compatible with `IoBuf`.
|
// Safe because `IoBufMut` is ABI-compatible with `IoBuf`.
|
||||||
unsafe { slice::from_raw_parts(iovs.as_ptr() as *const IoBuf, iovs.len()) }
|
unsafe { slice::from_raw_parts(iovs.as_ptr() as *const IoBuf, iovs.len()) }
|
||||||
}
|
}
|
||||||
|
@ -121,6 +124,7 @@ impl<'a> IoBufMut<'a> {
|
||||||
/// Converts a mutable slice of `IoBufMut`s into a mutable slice of `IoBuf`s.
|
/// Converts a mutable slice of `IoBufMut`s into a mutable slice of `IoBuf`s.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_iobufs_mut<'slice>(iovs: &'slice mut [IoBufMut<'_>]) -> &'slice mut [IoBuf] {
|
pub fn as_iobufs_mut<'slice>(iovs: &'slice mut [IoBufMut<'_>]) -> &'slice mut [IoBuf] {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `IoBufMut` is ABI-compatible with `IoBuf`.
|
// Safe because `IoBufMut` is ABI-compatible with `IoBuf`.
|
||||||
unsafe { slice::from_raw_parts_mut(iovs.as_mut_ptr() as *mut IoBuf, iovs.len()) }
|
unsafe { slice::from_raw_parts_mut(iovs.as_mut_ptr() as *mut IoBuf, iovs.len()) }
|
||||||
}
|
}
|
||||||
|
@ -138,11 +142,13 @@ impl<'a> AsMut<IoBuf> for IoBufMut<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// It's safe to implement Send + Sync for this type for the same reason that `std::io::IoSliceMut`
|
// It's safe to implement Send + Sync for this type for the same reason that `std::io::IoSliceMut`
|
||||||
// is Send + Sync. Internally, it contains a pointer and a length. The integer length is safely Send
|
// is Send + Sync. Internally, it contains a pointer and a length. The integer length is safely Send
|
||||||
// + Sync. There's nothing wrong with sending a pointer between threads and de-referencing the
|
// + Sync. There's nothing wrong with sending a pointer between threads and de-referencing the
|
||||||
// pointer requires an unsafe block anyway. See also https://github.com/rust-lang/rust/pull/70342.
|
// pointer requires an unsafe block anyway. See also https://github.com/rust-lang/rust/pull/70342.
|
||||||
unsafe impl<'a> Send for IoBufMut<'a> {}
|
unsafe impl<'a> Send for IoBufMut<'a> {}
|
||||||
|
// SAFETY: See comments for impl Send
|
||||||
unsafe impl<'a> Sync for IoBufMut<'a> {}
|
unsafe impl<'a> Sync for IoBufMut<'a> {}
|
||||||
|
|
||||||
impl<'a> Debug for IoBufMut<'a> {
|
impl<'a> Debug for IoBufMut<'a> {
|
||||||
|
|
|
@ -135,6 +135,7 @@ impl MemoryMapping {
|
||||||
match self.mapping.size().checked_sub(offset) {
|
match self.mapping.size().checked_sub(offset) {
|
||||||
Some(size_past_offset) => {
|
Some(size_past_offset) => {
|
||||||
let bytes_copied = min(size_past_offset, buf.len());
|
let bytes_copied = min(size_past_offset, buf.len());
|
||||||
|
// SAFETY:
|
||||||
// The bytes_copied equation above ensures we don't copy bytes out of range of
|
// The bytes_copied equation above ensures we don't copy bytes out of range of
|
||||||
// either buf or this slice. We also know that the buffers do not overlap because
|
// either buf or this slice. We also know that the buffers do not overlap because
|
||||||
// slices can never occupy the same memory as a volatile slice.
|
// slices can never occupy the same memory as a volatile slice.
|
||||||
|
@ -151,6 +152,7 @@ impl MemoryMapping {
|
||||||
match self.size().checked_sub(offset) {
|
match self.size().checked_sub(offset) {
|
||||||
Some(size_past_offset) => {
|
Some(size_past_offset) => {
|
||||||
let bytes_copied = min(size_past_offset, buf.len());
|
let bytes_copied = min(size_past_offset, buf.len());
|
||||||
|
// SAFETY:
|
||||||
// The bytes_copied equation above ensures we don't copy bytes out of range of
|
// The bytes_copied equation above ensures we don't copy bytes out of range of
|
||||||
// either buf or this slice. We also know that the buffers do not overlap because
|
// either buf or this slice. We also know that the buffers do not overlap because
|
||||||
// slices can never occupy the same memory as a volatile slice.
|
// slices can never occupy the same memory as a volatile slice.
|
||||||
|
@ -182,6 +184,7 @@ impl MemoryMapping {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn write_obj<T: AsBytes>(&self, val: T, offset: usize) -> Result<()> {
|
pub fn write_obj<T: AsBytes>(&self, val: T, offset: usize) -> Result<()> {
|
||||||
self.mapping.range_end(offset, size_of::<T>())?;
|
self.mapping.range_end(offset, size_of::<T>())?;
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we checked the bounds above.
|
// This is safe because we checked the bounds above.
|
||||||
unsafe {
|
unsafe {
|
||||||
write_unaligned(self.as_ptr().add(offset) as *mut T, val);
|
write_unaligned(self.as_ptr().add(offset) as *mut T, val);
|
||||||
|
@ -210,6 +213,7 @@ impl MemoryMapping {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn read_obj<T: FromBytes>(&self, offset: usize) -> Result<T> {
|
pub fn read_obj<T: FromBytes>(&self, offset: usize) -> Result<T> {
|
||||||
self.mapping.range_end(offset, size_of::<T>())?;
|
self.mapping.range_end(offset, size_of::<T>())?;
|
||||||
|
// SAFETY:
|
||||||
// This is safe because by definition Copy types can have their bits set arbitrarily and
|
// This is safe because by definition Copy types can have their bits set arbitrarily and
|
||||||
// still be valid.
|
// still be valid.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -242,6 +246,7 @@ impl MemoryMapping {
|
||||||
// Make sure writes to memory have been committed before performing I/O that could
|
// Make sure writes to memory have been committed before performing I/O that could
|
||||||
// potentially depend on them.
|
// potentially depend on them.
|
||||||
fence(Ordering::SeqCst);
|
fence(Ordering::SeqCst);
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we checked the bounds above.
|
// This is safe because we checked the bounds above.
|
||||||
unsafe {
|
unsafe {
|
||||||
write_volatile(self.as_ptr().add(offset) as *mut T, val);
|
write_volatile(self.as_ptr().add(offset) as *mut T, val);
|
||||||
|
@ -273,6 +278,7 @@ impl MemoryMapping {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn read_obj_volatile<T: FromBytes>(&self, offset: usize) -> Result<T> {
|
pub fn read_obj_volatile<T: FromBytes>(&self, offset: usize) -> Result<T> {
|
||||||
self.mapping.range_end(offset, size_of::<T>())?;
|
self.mapping.range_end(offset, size_of::<T>())?;
|
||||||
|
// SAFETY:
|
||||||
// This is safe because by definition Copy types can have their bits set arbitrarily and
|
// This is safe because by definition Copy types can have their bits set arbitrarily and
|
||||||
// still be valid.
|
// still be valid.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -410,6 +416,7 @@ impl VolatileMemory for MemoryMapping {
|
||||||
offset,
|
offset,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we checked that offset + count was within our range and we only ever hand
|
// Safe because we checked that offset + count was within our range and we only ever hand
|
||||||
// out volatile accessors.
|
// out volatile accessors.
|
||||||
Ok(unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) })
|
Ok(unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) })
|
||||||
|
@ -422,6 +429,7 @@ impl VolatileMemory for MemoryMapping {
|
||||||
/// Safe when implementers guarantee `ptr`..`ptr+size` is an mmaped region owned by this object that
|
/// Safe when implementers guarantee `ptr`..`ptr+size` is an mmaped region owned by this object that
|
||||||
/// can't be unmapped during the `MappedRegion`'s lifetime.
|
/// can't be unmapped during the `MappedRegion`'s lifetime.
|
||||||
pub unsafe trait MappedRegion: Send + Sync {
|
pub unsafe trait MappedRegion: Send + Sync {
|
||||||
|
// SAFETY:
|
||||||
/// Returns a pointer to the beginning of the memory region. Should only be
|
/// Returns a pointer to the beginning of the memory region. Should only be
|
||||||
/// used for passing this region to ioctls for setting guest memory.
|
/// used for passing this region to ioctls for setting guest memory.
|
||||||
fn as_ptr(&self) -> *mut u8;
|
fn as_ptr(&self) -> *mut u8;
|
||||||
|
@ -456,6 +464,7 @@ pub unsafe trait MappedRegion: Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because it exclusively forwards calls to a safe implementation.
|
// Safe because it exclusively forwards calls to a safe implementation.
|
||||||
unsafe impl MappedRegion for MemoryMapping {
|
unsafe impl MappedRegion for MemoryMapping {
|
||||||
fn as_ptr(&self) -> *mut u8 {
|
fn as_ptr(&self) -> *mut u8 {
|
||||||
|
@ -473,6 +482,10 @@ pub struct ExternalMapping {
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// `ptr`..`ptr+size` is an mmaped region and is owned by this object. Caller
|
||||||
|
// needs to ensure that the region is not unmapped during the `MappedRegion`'s
|
||||||
|
// lifetime.
|
||||||
unsafe impl MappedRegion for ExternalMapping {
|
unsafe impl MappedRegion for ExternalMapping {
|
||||||
/// used for passing this region to ioctls for setting guest memory.
|
/// used for passing this region to ioctls for setting guest memory.
|
||||||
fn as_ptr(&self) -> *mut u8 {
|
fn as_ptr(&self) -> *mut u8 {
|
||||||
|
|
|
@ -84,7 +84,9 @@ impl AcpiNotifyEvent {
|
||||||
// https://github.com/rust-lang/rust/issues/79089,
|
// https://github.com/rust-lang/rust/issues/79089,
|
||||||
// before using device_class further cast it to u8.
|
// before using device_class further cast it to u8.
|
||||||
let device_class: &[u8; 20usize] =
|
let device_class: &[u8; 20usize] =
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe { ::std::mem::transmute(&acpi_event.device_class) };
|
unsafe { ::std::mem::transmute(&acpi_event.device_class) };
|
||||||
|
// SAFETY: trivially safe
|
||||||
let bus_id: &[u8; 15usize] = unsafe { ::std::mem::transmute(&acpi_event.bus_id) };
|
let bus_id: &[u8; 15usize] = unsafe { ::std::mem::transmute(&acpi_event.bus_id) };
|
||||||
|
|
||||||
Ok(AcpiNotifyEvent {
|
Ok(AcpiNotifyEvent {
|
||||||
|
|
|
@ -20,9 +20,10 @@ extern "C" {
|
||||||
|
|
||||||
/// Drops all capabilities (permitted, inheritable, and effective) from the current process.
|
/// Drops all capabilities (permitted, inheritable, and effective) from the current process.
|
||||||
pub fn drop_capabilities() -> Result<()> {
|
pub fn drop_capabilities() -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we do not actually manipulate any memory handled by libcap
|
||||||
|
// and we check errors.
|
||||||
unsafe {
|
unsafe {
|
||||||
// Safe because we do not actually manipulate any memory handled by libcap
|
|
||||||
// and we check errors.
|
|
||||||
let caps = cap_init();
|
let caps = cap_init();
|
||||||
if caps.is_null() {
|
if caps.is_null() {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
|
|
|
@ -13,8 +13,10 @@ impl PartialEq for SafeDescriptor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// safe because we only use the return value and libc says it's always successful
|
// safe because we only use the return value and libc says it's always successful
|
||||||
let pid = unsafe { libc::getpid() };
|
let pid = unsafe { libc::getpid() };
|
||||||
|
// SAFETY:
|
||||||
// safe because we are passing everything by value and checking the return value
|
// safe because we are passing everything by value and checking the return value
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::syscall(
|
libc::syscall(
|
||||||
|
|
|
@ -55,21 +55,24 @@ impl EventExt for crate::Event {
|
||||||
impl PlatformEvent {
|
impl PlatformEvent {
|
||||||
/// Creates a new blocking eventfd with an initial value of 0.
|
/// Creates a new blocking eventfd with an initial value of 0.
|
||||||
pub fn new() -> Result<PlatformEvent> {
|
pub fn new() -> Result<PlatformEvent> {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because eventfd merely allocated an eventfd for our process and we handle
|
// This is safe because eventfd merely allocated an eventfd for our process and we handle
|
||||||
// the error case.
|
// the error case.
|
||||||
let ret = unsafe { eventfd(0, 0) };
|
let ret = unsafe { eventfd(0, 0) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
// This is safe because we checked ret for success and know the kernel gave us an fd that we
|
|
||||||
// own.
|
|
||||||
Ok(PlatformEvent {
|
Ok(PlatformEvent {
|
||||||
|
// SAFETY:
|
||||||
|
// This is safe because we checked ret for success and know the kernel gave us an fd that we
|
||||||
|
// own.
|
||||||
event_handle: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
|
event_handle: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See `EventExt::write_count`.
|
/// See `EventExt::write_count`.
|
||||||
pub fn write_count(&self, v: u64) -> Result<()> {
|
pub fn write_count(&self, v: u64) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we made this fd and the pointer we pass can not overflow because we
|
// This is safe because we made this fd and the pointer we pass can not overflow because we
|
||||||
// give the syscall's size parameter properly.
|
// give the syscall's size parameter properly.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -88,9 +91,10 @@ impl PlatformEvent {
|
||||||
/// See `EventExt::read_count`.
|
/// See `EventExt::read_count`.
|
||||||
pub fn read_count(&self) -> Result<u64> {
|
pub fn read_count(&self) -> Result<u64> {
|
||||||
let mut buf: u64 = 0;
|
let mut buf: u64 = 0;
|
||||||
|
// SAFETY:
|
||||||
|
// This is safe because we made this fd and the pointer we pass can not overflow because
|
||||||
|
// we give the syscall's size parameter properly.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
// This is safe because we made this fd and the pointer we pass can not overflow because
|
|
||||||
// we give the syscall's size parameter properly.
|
|
||||||
read(
|
read(
|
||||||
self.as_raw_descriptor(),
|
self.as_raw_descriptor(),
|
||||||
&mut buf as *mut u64 as *mut c_void,
|
&mut buf as *mut u64 as *mut c_void,
|
||||||
|
@ -121,6 +125,7 @@ impl PlatformEvent {
|
||||||
revents: 0,
|
revents: 0,
|
||||||
};
|
};
|
||||||
let timeoutspec: libc::timespec = duration_to_timespec(timeout);
|
let timeoutspec: libc::timespec = duration_to_timespec(timeout);
|
||||||
|
// SAFETY:
|
||||||
// Safe because this only modifies |pfd| and we check the return value
|
// Safe because this only modifies |pfd| and we check the return value
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::ppoll(
|
libc::ppoll(
|
||||||
|
|
|
@ -21,6 +21,7 @@ fn lseek(fd: &dyn AsRawDescriptor, offset: u64, option: LseekOption) -> Result<u
|
||||||
LseekOption::Data => libc::SEEK_DATA,
|
LseekOption::Data => libc::SEEK_DATA,
|
||||||
LseekOption::Hole => libc::SEEK_HOLE,
|
LseekOption::Hole => libc::SEEK_HOLE,
|
||||||
};
|
};
|
||||||
|
// SAFETY:
|
||||||
// safe because this doesn't modify any memory.
|
// safe because this doesn't modify any memory.
|
||||||
let ret = unsafe { libc::lseek64(fd.as_raw_descriptor(), offset as i64, whence) };
|
let ret = unsafe { libc::lseek64(fd.as_raw_descriptor(), offset as i64, whence) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub enum FileFlags {
|
||||||
|
|
||||||
impl FileFlags {
|
impl FileFlags {
|
||||||
pub fn from_file(file: &dyn AsRawDescriptor) -> Result<FileFlags> {
|
pub fn from_file(file: &dyn AsRawDescriptor) -> Result<FileFlags> {
|
||||||
|
// SAFETY:
|
||||||
// Trivially safe because fcntl with the F_GETFL command is totally safe and we check for
|
// Trivially safe because fcntl with the F_GETFL command is totally safe and we check for
|
||||||
// error.
|
// error.
|
||||||
let flags = unsafe { fcntl(file.as_raw_descriptor(), F_GETFL) };
|
let flags = unsafe { fcntl(file.as_raw_descriptor(), F_GETFL) };
|
||||||
|
|
|
@ -15,9 +15,11 @@ use crate::syscall;
|
||||||
#[allow(clippy::unnecessary_cast)]
|
#[allow(clippy::unnecessary_cast)]
|
||||||
pub fn get_filesystem_type(file: &File) -> Result<i64> {
|
pub fn get_filesystem_type(file: &File) -> Result<i64> {
|
||||||
let mut statfs_buf = MaybeUninit::<libc::statfs64>::uninit();
|
let mut statfs_buf = MaybeUninit::<libc::statfs64>::uninit();
|
||||||
|
// SAFETY:
|
||||||
// Safe because we just got the memory space with exact required amount and
|
// Safe because we just got the memory space with exact required amount and
|
||||||
// passing that on.
|
// passing that on.
|
||||||
syscall!(unsafe { fstatfs64(file.as_raw_fd(), statfs_buf.as_mut_ptr()) })?;
|
syscall!(unsafe { fstatfs64(file.as_raw_fd(), statfs_buf.as_mut_ptr()) })?;
|
||||||
|
// SAFETY:
|
||||||
// Safe because the kernel guarantees the struct is initialized.
|
// Safe because the kernel guarantees the struct is initialized.
|
||||||
let statfs_buf = unsafe { statfs_buf.assume_init() };
|
let statfs_buf = unsafe { statfs_buf.assume_init() };
|
||||||
Ok(statfs_buf.f_type as i64)
|
Ok(statfs_buf.f_type as i64)
|
||||||
|
|
|
@ -120,6 +120,7 @@ impl Syslog for PlatformSyslog {
|
||||||
// libraries in use that hard depend on libc's syslogger. Remove this and go back to making the
|
// libraries in use that hard depend on libc's syslogger. Remove this and go back to making the
|
||||||
// connection directly once minjail is ready.
|
// connection directly once minjail is ready.
|
||||||
fn openlog_and_get_socket() -> Result<UnixDatagram, Error> {
|
fn openlog_and_get_socket() -> Result<UnixDatagram, Error> {
|
||||||
|
// SAFETY:
|
||||||
// closelog first in case there was already a file descriptor open. Safe because it takes no
|
// closelog first in case there was already a file descriptor open. Safe because it takes no
|
||||||
// arguments and just closes an open file descriptor. Does nothing if the file descriptor
|
// arguments and just closes an open file descriptor. Does nothing if the file descriptor
|
||||||
// was not already open.
|
// was not already open.
|
||||||
|
@ -137,6 +138,7 @@ fn openlog_and_get_socket() -> Result<UnixDatagram, Error> {
|
||||||
.map_err(Error::GetLowestFd)?
|
.map_err(Error::GetLowestFd)?
|
||||||
.as_raw_fd();
|
.as_raw_fd();
|
||||||
|
|
||||||
|
// SAFETY: See comments for each unsafe line in the block.
|
||||||
unsafe {
|
unsafe {
|
||||||
// Safe because openlog accesses no pointers because `ident` is null, only valid flags are
|
// Safe because openlog accesses no pointers because `ident` is null, only valid flags are
|
||||||
// used, and it returns no error.
|
// used, and it returns no error.
|
||||||
|
@ -152,6 +154,7 @@ fn openlog_and_get_socket() -> Result<UnixDatagram, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_localtime() -> tm {
|
fn get_localtime() -> tm {
|
||||||
|
// SAFETY: See comments for each unsafe line in the block.
|
||||||
unsafe {
|
unsafe {
|
||||||
// Safe because tm is just a struct of plain data.
|
// Safe because tm is just a struct of plain data.
|
||||||
let mut tm: tm = mem::zeroed();
|
let mut tm: tm = mem::zeroed();
|
||||||
|
|
|
@ -60,6 +60,7 @@ impl dyn MappedRegion {
|
||||||
pub fn msync(&self, offset: usize, size: usize) -> Result<()> {
|
pub fn msync(&self, offset: usize, size: usize) -> Result<()> {
|
||||||
validate_includes_range(self.size(), offset, size)?;
|
validate_includes_range(self.size(), offset, size)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the MemoryMapping/MemoryMappingArena interface ensures our pointer and size
|
// Safe because the MemoryMapping/MemoryMappingArena interface ensures our pointer and size
|
||||||
// are correct, and we've validated that `offset`..`offset+size` is in the range owned by
|
// are correct, and we've validated that `offset`..`offset+size` is in the range owned by
|
||||||
// this `MappedRegion`.
|
// this `MappedRegion`.
|
||||||
|
@ -86,11 +87,13 @@ pub struct MemoryMapping {
|
||||||
size: usize,
|
size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Send and Sync aren't automatically inherited for the raw address pointer.
|
// Send and Sync aren't automatically inherited for the raw address pointer.
|
||||||
// Accessing that pointer is only done through the stateless interface which
|
// Accessing that pointer is only done through the stateless interface which
|
||||||
// allows the object to be shared by multiple threads without a decrease in
|
// allows the object to be shared by multiple threads without a decrease in
|
||||||
// safety.
|
// safety.
|
||||||
unsafe impl Send for MemoryMapping {}
|
unsafe impl Send for MemoryMapping {}
|
||||||
|
// SAFETY: See safety comments for impl Send
|
||||||
unsafe impl Sync for MemoryMapping {}
|
unsafe impl Sync for MemoryMapping {}
|
||||||
|
|
||||||
impl MemoryMapping {
|
impl MemoryMapping {
|
||||||
|
@ -108,6 +111,7 @@ impl MemoryMapping {
|
||||||
/// * `size` - Size of memory region in bytes.
|
/// * `size` - Size of memory region in bytes.
|
||||||
/// * `prot` - Protection (e.g. readable/writable) of the memory region.
|
/// * `prot` - Protection (e.g. readable/writable) of the memory region.
|
||||||
pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> {
|
pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we are creating an anonymous mapping in a place not already used by
|
// This is safe because we are creating an anonymous mapping in a place not already used by
|
||||||
// any other area in this process.
|
// any other area in this process.
|
||||||
unsafe { MemoryMapping::try_mmap(None, size, prot.into(), None) }
|
unsafe { MemoryMapping::try_mmap(None, size, prot.into(), None) }
|
||||||
|
@ -161,9 +165,10 @@ impl MemoryMapping {
|
||||||
prot: Protection,
|
prot: Protection,
|
||||||
populate: bool,
|
populate: bool,
|
||||||
) -> Result<MemoryMapping> {
|
) -> Result<MemoryMapping> {
|
||||||
|
// SAFETY:
|
||||||
|
// This is safe because we are creating an anonymous mapping in a place not already used
|
||||||
|
// by any other area in this process.
|
||||||
unsafe {
|
unsafe {
|
||||||
// This is safe because we are creating an anonymous mapping in a place not already used
|
|
||||||
// by any other area in this process.
|
|
||||||
MemoryMapping::try_mmap_populate(None, size, prot.into(), Some((fd, offset)), populate)
|
MemoryMapping::try_mmap_populate(None, size, prot.into(), Some((fd, offset)), populate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,6 +261,8 @@ impl MemoryMapping {
|
||||||
}
|
}
|
||||||
// Map private for read-only seal. See below for upstream relax of the restriction.
|
// Map private for read-only seal. See below for upstream relax of the restriction.
|
||||||
// - https://lore.kernel.org/bpf/20231013103208.kdffpyerufr4ygnw@quack3/T/
|
// - https://lore.kernel.org/bpf/20231013103208.kdffpyerufr4ygnw@quack3/T/
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because no third parameter is expected and we check the return result.
|
||||||
let seals = unsafe { libc::fcntl(fd.as_raw_descriptor(), libc::F_GET_SEALS) };
|
let seals = unsafe { libc::fcntl(fd.as_raw_descriptor(), libc::F_GET_SEALS) };
|
||||||
if (seals >= 0) && (seals & libc::F_SEAL_WRITE != 0) {
|
if (seals >= 0) && (seals & libc::F_SEAL_WRITE != 0) {
|
||||||
flags &= !libc::MAP_SHARED;
|
flags &= !libc::MAP_SHARED;
|
||||||
|
@ -288,6 +295,7 @@ impl MemoryMapping {
|
||||||
|
|
||||||
/// Madvise the kernel to unmap on fork.
|
/// Madvise the kernel to unmap on fork.
|
||||||
pub fn use_dontfork(&self) -> Result<()> {
|
pub fn use_dontfork(&self) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we call madvise with a valid address and size, and we check the
|
// This is safe because we call madvise with a valid address and size, and we check the
|
||||||
// return value.
|
// return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -314,6 +322,7 @@ impl MemoryMapping {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we call madvise with a valid address and size, and we check the
|
// This is safe because we call madvise with a valid address and size, and we check the
|
||||||
// return value.
|
// return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -332,6 +341,7 @@ impl MemoryMapping {
|
||||||
|
|
||||||
/// Calls msync with MS_SYNC on the mapping.
|
/// Calls msync with MS_SYNC on the mapping.
|
||||||
pub fn msync(&self) -> Result<()> {
|
pub fn msync(&self) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// This is safe since we use the exact address and length of a known
|
// This is safe since we use the exact address and length of a known
|
||||||
// good memory mapping.
|
// good memory mapping.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -352,6 +362,8 @@ impl MemoryMapping {
|
||||||
pub fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()> {
|
pub fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()> {
|
||||||
self.range_end(mem_offset, count)
|
self.range_end(mem_offset, count)
|
||||||
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
||||||
|
// SAFETY: Safe because all the args to madvise are valid and the return
|
||||||
|
// value is checked.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
// madvising away the region is the same as the guest changing it.
|
// madvising away the region is the same as the guest changing it.
|
||||||
// Next time it is read, it may return zero pages.
|
// Next time it is read, it may return zero pages.
|
||||||
|
@ -384,6 +396,7 @@ impl MemoryMapping {
|
||||||
// Validation
|
// Validation
|
||||||
self.range_end(mem_offset, count)
|
self.range_end(mem_offset, count)
|
||||||
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
||||||
|
// SAFETY:
|
||||||
// Safe because populating the pages from the backed file does not affect the Rust memory
|
// Safe because populating the pages from the backed file does not affect the Rust memory
|
||||||
// safety.
|
// safety.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -418,6 +431,7 @@ impl MemoryMapping {
|
||||||
// Validation
|
// Validation
|
||||||
self.range_end(mem_offset, count)
|
self.range_end(mem_offset, count)
|
||||||
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
||||||
|
// SAFETY:
|
||||||
// Safe because dropping the page cache does not affect the Rust memory safety.
|
// Safe because dropping the page cache does not affect the Rust memory safety.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::madvise(
|
libc::madvise(
|
||||||
|
@ -448,6 +462,7 @@ impl MemoryMapping {
|
||||||
self.range_end(mem_offset, count)
|
self.range_end(mem_offset, count)
|
||||||
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
||||||
let addr = self.addr as usize + mem_offset;
|
let addr = self.addr as usize + mem_offset;
|
||||||
|
// SAFETY:
|
||||||
// Safe because MLOCK_ONFAULT only affects the swap behavior of the kernel, so it has no
|
// Safe because MLOCK_ONFAULT only affects the swap behavior of the kernel, so it has no
|
||||||
// impact on rust semantics.
|
// impact on rust semantics.
|
||||||
let ret = unsafe { libc::mlock2(addr as *mut _, count, libc::MLOCK_ONFAULT) };
|
let ret = unsafe { libc::mlock2(addr as *mut _, count, libc::MLOCK_ONFAULT) };
|
||||||
|
@ -479,6 +494,7 @@ impl MemoryMapping {
|
||||||
// Validation
|
// Validation
|
||||||
self.range_end(mem_offset, count)
|
self.range_end(mem_offset, count)
|
||||||
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
.map_err(|_| Error::InvalidRange(mem_offset, count, self.size()))?;
|
||||||
|
// SAFETY:
|
||||||
// Safe because munlock(2) does not affect the Rust memory safety.
|
// Safe because munlock(2) does not affect the Rust memory safety.
|
||||||
let ret = unsafe { libc::munlock((self.addr as usize + mem_offset) as *mut _, count) };
|
let ret = unsafe { libc::munlock((self.addr as usize + mem_offset) as *mut _, count) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -498,6 +514,7 @@ impl MemoryMapping {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the pointer and size point to a memory range owned by this MemoryMapping that won't
|
// Safe because the pointer and size point to a memory range owned by this MemoryMapping that won't
|
||||||
// be unmapped until it's Dropped.
|
// be unmapped until it's Dropped.
|
||||||
unsafe impl MappedRegion for MemoryMapping {
|
unsafe impl MappedRegion for MemoryMapping {
|
||||||
|
@ -512,6 +529,7 @@ unsafe impl MappedRegion for MemoryMapping {
|
||||||
|
|
||||||
impl Drop for MemoryMapping {
|
impl Drop for MemoryMapping {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we mmap the area at addr ourselves, and nobody
|
// This is safe because we mmap the area at addr ourselves, and nobody
|
||||||
// else is holding a reference to it.
|
// else is holding a reference to it.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -527,11 +545,13 @@ pub struct MemoryMappingArena {
|
||||||
size: usize,
|
size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Send and Sync aren't automatically inherited for the raw address pointer.
|
// Send and Sync aren't automatically inherited for the raw address pointer.
|
||||||
// Accessing that pointer is only done through the stateless interface which
|
// Accessing that pointer is only done through the stateless interface which
|
||||||
// allows the object to be shared by multiple threads without a decrease in
|
// allows the object to be shared by multiple threads without a decrease in
|
||||||
// safety.
|
// safety.
|
||||||
unsafe impl Send for MemoryMappingArena {}
|
unsafe impl Send for MemoryMappingArena {}
|
||||||
|
// SAFETY: See safety comments for impl Send
|
||||||
unsafe impl Sync for MemoryMappingArena {}
|
unsafe impl Sync for MemoryMappingArena {}
|
||||||
|
|
||||||
impl MemoryMappingArena {
|
impl MemoryMappingArena {
|
||||||
|
@ -635,6 +655,7 @@ impl MemoryMappingArena {
|
||||||
}
|
}
|
||||||
validate_includes_range(self.size(), offset, size)?;
|
validate_includes_range(self.size(), offset, size)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// This is safe since the range has been validated.
|
// This is safe since the range has been validated.
|
||||||
let mmap = unsafe {
|
let mmap = unsafe {
|
||||||
match fd {
|
match fd {
|
||||||
|
@ -665,6 +686,7 @@ impl MemoryMappingArena {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the pointer and size point to a memory range owned by this MemoryMappingArena that
|
// Safe because the pointer and size point to a memory range owned by this MemoryMappingArena that
|
||||||
// won't be unmapped until it's Dropped.
|
// won't be unmapped until it's Dropped.
|
||||||
unsafe impl MappedRegion for MemoryMappingArena {
|
unsafe impl MappedRegion for MemoryMappingArena {
|
||||||
|
@ -712,6 +734,7 @@ impl From<CrateMemoryMapping> for MemoryMappingArena {
|
||||||
|
|
||||||
impl Drop for MemoryMappingArena {
|
impl Drop for MemoryMappingArena {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we own this memory range, and nobody else is holding a reference to
|
// This is safe because we own this memory range, and nobody else is holding a reference to
|
||||||
// it.
|
// it.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -902,6 +925,7 @@ mod tests {
|
||||||
fn slice_addr() {
|
fn slice_addr() {
|
||||||
let m = MemoryMappingBuilder::new(5).build().unwrap();
|
let m = MemoryMappingBuilder::new(5).build().unwrap();
|
||||||
let s = m.get_slice(2, 3).unwrap();
|
let s = m.get_slice(2, 3).unwrap();
|
||||||
|
// SAFETY: all addresses are known to exist.
|
||||||
assert_eq!(s.as_ptr(), unsafe { m.as_ptr().offset(2) });
|
assert_eq!(s.as_ptr(), unsafe { m.as_ptr().offset(2) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,7 @@ pub type Mode = libc::mode_t;
|
||||||
/// elsewhere.
|
/// elsewhere.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn getpid() -> Pid {
|
pub fn getpid() -> Pid {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this syscall can never fail and we give it a valid syscall number.
|
// Safe because this syscall can never fail and we give it a valid syscall number.
|
||||||
unsafe { syscall(SYS_getpid as c_long) as Pid }
|
unsafe { syscall(SYS_getpid as c_long) as Pid }
|
||||||
}
|
}
|
||||||
|
@ -131,12 +132,14 @@ pub fn getpid() -> Pid {
|
||||||
/// Safe wrapper for the geppid Linux systemcall.
|
/// Safe wrapper for the geppid Linux systemcall.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn getppid() -> Pid {
|
pub fn getppid() -> Pid {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this syscall can never fail and we give it a valid syscall number.
|
// Safe because this syscall can never fail and we give it a valid syscall number.
|
||||||
unsafe { syscall(SYS_getppid as c_long) as Pid }
|
unsafe { syscall(SYS_getppid as c_long) as Pid }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safe wrapper for the gettid Linux systemcall.
|
/// Safe wrapper for the gettid Linux systemcall.
|
||||||
pub fn gettid() -> Pid {
|
pub fn gettid() -> Pid {
|
||||||
|
// SAFETY:
|
||||||
// Calling the gettid() sycall is always safe.
|
// Calling the gettid() sycall is always safe.
|
||||||
unsafe { syscall(SYS_gettid as c_long) as Pid }
|
unsafe { syscall(SYS_gettid as c_long) as Pid }
|
||||||
}
|
}
|
||||||
|
@ -144,6 +147,7 @@ pub fn gettid() -> Pid {
|
||||||
/// Safe wrapper for `geteuid(2)`.
|
/// Safe wrapper for `geteuid(2)`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn geteuid() -> Uid {
|
pub fn geteuid() -> Uid {
|
||||||
|
// SAFETY:
|
||||||
// trivially safe
|
// trivially safe
|
||||||
unsafe { libc::geteuid() }
|
unsafe { libc::geteuid() }
|
||||||
}
|
}
|
||||||
|
@ -151,6 +155,7 @@ pub fn geteuid() -> Uid {
|
||||||
/// Safe wrapper for `getegid(2)`.
|
/// Safe wrapper for `getegid(2)`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn getegid() -> Gid {
|
pub fn getegid() -> Gid {
|
||||||
|
// SAFETY:
|
||||||
// trivially safe
|
// trivially safe
|
||||||
unsafe { libc::getegid() }
|
unsafe { libc::getegid() }
|
||||||
}
|
}
|
||||||
|
@ -176,6 +181,7 @@ pub fn flock<F: AsRawDescriptor>(file: &F, op: FlockOperation, nonblocking: bool
|
||||||
operation |= libc::LOCK_NB;
|
operation |= libc::LOCK_NB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe since we pass in a valid fd and flock operation, and check the return value.
|
// Safe since we pass in a valid fd and flock operation, and check the return value.
|
||||||
syscall!(unsafe { libc::flock(file.as_raw_descriptor(), operation) }).map(|_| ())
|
syscall!(unsafe { libc::flock(file.as_raw_descriptor(), operation) }).map(|_| ())
|
||||||
}
|
}
|
||||||
|
@ -222,6 +228,7 @@ pub fn fallocate<F: AsRawDescriptor>(
|
||||||
len as libc::off64_t
|
len as libc::off64_t
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe since we pass in a valid fd and fallocate mode, validate offset and len,
|
// Safe since we pass in a valid fd and fallocate mode, validate offset and len,
|
||||||
// and check the return value.
|
// and check the return value.
|
||||||
syscall!(unsafe { libc::fallocate64(file.as_raw_descriptor(), mode.into(), offset, len) })
|
syscall!(unsafe { libc::fallocate64(file.as_raw_descriptor(), mode.into(), offset, len) })
|
||||||
|
@ -232,10 +239,12 @@ pub fn fallocate<F: AsRawDescriptor>(
|
||||||
pub fn fstat<F: AsRawDescriptor>(f: &F) -> Result<libc::stat64> {
|
pub fn fstat<F: AsRawDescriptor>(f: &F) -> Result<libc::stat64> {
|
||||||
let mut st = MaybeUninit::<libc::stat64>::zeroed();
|
let mut st = MaybeUninit::<libc::stat64>::zeroed();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the kernel will only write data in `st` and we check the return
|
// Safe because the kernel will only write data in `st` and we check the return
|
||||||
// value.
|
// value.
|
||||||
syscall!(unsafe { libc::fstat64(f.as_raw_descriptor(), st.as_mut_ptr()) })?;
|
syscall!(unsafe { libc::fstat64(f.as_raw_descriptor(), st.as_mut_ptr()) })?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the kernel guarantees that the struct is now fully initialized.
|
// Safe because the kernel guarantees that the struct is now fully initialized.
|
||||||
Ok(unsafe { st.assume_init() })
|
Ok(unsafe { st.assume_init() })
|
||||||
}
|
}
|
||||||
|
@ -252,7 +261,7 @@ ioctl_io_nr!(BLKDISCARD, BLOCK_IO_TYPE, 119);
|
||||||
/// Discards the given range of a block file.
|
/// Discards the given range of a block file.
|
||||||
pub fn discard_block<F: AsRawDescriptor>(file: &F, offset: u64, len: u64) -> Result<()> {
|
pub fn discard_block<F: AsRawDescriptor>(file: &F, offset: u64, len: u64) -> Result<()> {
|
||||||
let range: [u64; 2] = [offset, len];
|
let range: [u64; 2] = [offset, len];
|
||||||
// # Safety
|
// SAFETY:
|
||||||
// Safe because
|
// Safe because
|
||||||
// - we check the return value.
|
// - we check the return value.
|
||||||
// - ioctl(BLKDISCARD) does not hold the descriptor after the call.
|
// - ioctl(BLKDISCARD) does not hold the descriptor after the call.
|
||||||
|
@ -287,6 +296,7 @@ impl AsRawPid for std::process::Child {
|
||||||
pub fn wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>, ExitStatus)> {
|
pub fn wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>, ExitStatus)> {
|
||||||
let pid = pid.as_raw_pid();
|
let pid = pid.as_raw_pid();
|
||||||
let mut status: c_int = 1;
|
let mut status: c_int = 1;
|
||||||
|
// SAFETY:
|
||||||
// Safe because status is owned and the error is checked.
|
// Safe because status is owned and the error is checked.
|
||||||
let ret = unsafe { libc::waitpid(pid, &mut status, options) };
|
let ret = unsafe { libc::waitpid(pid, &mut status, options) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -324,6 +334,7 @@ pub fn wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>,
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn reap_child() -> Result<Pid> {
|
pub fn reap_child() -> Result<Pid> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
|
// Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
|
||||||
let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
|
let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
|
||||||
if ret == -1 {
|
if ret == -1 {
|
||||||
|
@ -338,6 +349,7 @@ pub fn reap_child() -> Result<Pid> {
|
||||||
/// On success, this kills all processes in the current process group, including the current
|
/// On success, this kills all processes in the current process group, including the current
|
||||||
/// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
|
/// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
|
||||||
pub fn kill_process_group() -> Result<()> {
|
pub fn kill_process_group() -> Result<()> {
|
||||||
|
// SAFETY: Safe because pid is 'self group' and return value doesn't matter.
|
||||||
unsafe { kill(0, SIGKILL) }?;
|
unsafe { kill(0, SIGKILL) }?;
|
||||||
// Kill succeeded, so this process never reaches here.
|
// Kill succeeded, so this process never reaches here.
|
||||||
unreachable!();
|
unreachable!();
|
||||||
|
@ -349,12 +361,14 @@ pub fn kill_process_group() -> Result<()> {
|
||||||
pub fn pipe(close_on_exec: bool) -> Result<(File, File)> {
|
pub fn pipe(close_on_exec: bool) -> Result<(File, File)> {
|
||||||
let flags = if close_on_exec { O_CLOEXEC } else { 0 };
|
let flags = if close_on_exec { O_CLOEXEC } else { 0 };
|
||||||
let mut pipe_fds = [-1; 2];
|
let mut pipe_fds = [-1; 2];
|
||||||
|
// SAFETY:
|
||||||
// Safe because pipe2 will only write 2 element array of i32 to the given pointer, and we check
|
// Safe because pipe2 will only write 2 element array of i32 to the given pointer, and we check
|
||||||
// for error.
|
// for error.
|
||||||
let ret = unsafe { pipe2(&mut pipe_fds[0], flags) };
|
let ret = unsafe { pipe2(&mut pipe_fds[0], flags) };
|
||||||
if ret == -1 {
|
if ret == -1 {
|
||||||
errno_result()
|
errno_result()
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY:
|
||||||
// Safe because both fds must be valid for pipe2 to have returned sucessfully and we have
|
// Safe because both fds must be valid for pipe2 to have returned sucessfully and we have
|
||||||
// exclusive ownership of them.
|
// exclusive ownership of them.
|
||||||
Ok(unsafe {
|
Ok(unsafe {
|
||||||
|
@ -370,6 +384,7 @@ pub fn pipe(close_on_exec: bool) -> Result<(File, File)> {
|
||||||
///
|
///
|
||||||
/// Returns the new size of the pipe or an error if the OS fails to set the pipe size.
|
/// Returns the new size of the pipe or an error if the OS fails to set the pipe size.
|
||||||
pub fn set_pipe_size(fd: RawFd, size: usize) -> Result<usize> {
|
pub fn set_pipe_size(fd: RawFd, size: usize) -> Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because fcntl with the `F_SETPIPE_SZ` arg doesn't touch memory.
|
// Safe because fcntl with the `F_SETPIPE_SZ` arg doesn't touch memory.
|
||||||
syscall!(unsafe { fcntl(fd, libc::F_SETPIPE_SZ, size as c_int) }).map(|ret| ret as usize)
|
syscall!(unsafe { fcntl(fd, libc::F_SETPIPE_SZ, size as c_int) }).map(|ret| ret as usize)
|
||||||
}
|
}
|
||||||
|
@ -450,12 +465,14 @@ pub fn validate_raw_descriptor(raw_descriptor: RawDescriptor) -> Result<RawDescr
|
||||||
pub fn validate_raw_fd(raw_fd: RawFd) -> Result<RawFd> {
|
pub fn validate_raw_fd(raw_fd: RawFd) -> Result<RawFd> {
|
||||||
// Checking that close-on-exec isn't set helps filter out FDs that were opened by
|
// Checking that close-on-exec isn't set helps filter out FDs that were opened by
|
||||||
// crosvm as all crosvm FDs are close on exec.
|
// crosvm as all crosvm FDs are close on exec.
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let flags = unsafe { libc::fcntl(raw_fd, libc::F_GETFD) };
|
let flags = unsafe { libc::fcntl(raw_fd, libc::F_GETFD) };
|
||||||
if flags < 0 || (flags & libc::FD_CLOEXEC) != 0 {
|
if flags < 0 || (flags & libc::FD_CLOEXEC) != 0 {
|
||||||
return Err(Error::new(libc::EBADF));
|
return Err(Error::new(libc::EBADF));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Duplicate the fd to ensure that we don't accidentally close an fd previously
|
// Duplicate the fd to ensure that we don't accidentally close an fd previously
|
||||||
// opened by another subsystem. Safe because this doesn't modify any memory and
|
// opened by another subsystem. Safe because this doesn't modify any memory and
|
||||||
// we check the return value.
|
// we check the return value.
|
||||||
|
@ -476,6 +493,7 @@ pub fn poll_in<F: AsRawDescriptor>(fd: &F) -> bool {
|
||||||
events: libc::POLLIN,
|
events: libc::POLLIN,
|
||||||
revents: 0,
|
revents: 0,
|
||||||
};
|
};
|
||||||
|
// SAFETY:
|
||||||
// Safe because we give a valid pointer to a list (of 1) FD and check the return value.
|
// Safe because we give a valid pointer to a list (of 1) FD and check the return value.
|
||||||
let ret = unsafe { libc::poll(&mut fds, 1, 0) };
|
let ret = unsafe { libc::poll(&mut fds, 1, 0) };
|
||||||
// An error probably indicates an invalid FD, or an FD that can't be polled. Returning false in
|
// An error probably indicates an invalid FD, or an FD that can't be polled. Returning false in
|
||||||
|
@ -515,6 +533,7 @@ pub fn safe_descriptor_from_path<P: AsRef<Path>>(path: P) -> Result<Option<SafeD
|
||||||
.ok_or_else(|| Error::new(EINVAL))?;
|
.ok_or_else(|| Error::new(EINVAL))?;
|
||||||
let validated_fd = validate_raw_fd(raw_descriptor)?;
|
let validated_fd = validate_raw_fd(raw_descriptor)?;
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
|
// SAFETY:
|
||||||
// Safe because nothing else has access to validated_fd after this call.
|
// Safe because nothing else has access to validated_fd after this call.
|
||||||
unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
|
unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
|
||||||
))
|
))
|
||||||
|
@ -543,9 +562,11 @@ pub fn open_file_or_duplicate<P: AsRef<Path>>(path: P, options: &OpenOptions) ->
|
||||||
pub fn max_open_files() -> Result<u64> {
|
pub fn max_open_files() -> Result<u64> {
|
||||||
let mut buf = mem::MaybeUninit::<libc::rlimit64>::zeroed();
|
let mut buf = mem::MaybeUninit::<libc::rlimit64>::zeroed();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because this will only modify `buf` and we check the return value.
|
// Safe because this will only modify `buf` and we check the return value.
|
||||||
let res = unsafe { libc::prlimit64(0, libc::RLIMIT_NOFILE, ptr::null(), buf.as_mut_ptr()) };
|
let res = unsafe { libc::prlimit64(0, libc::RLIMIT_NOFILE, ptr::null(), buf.as_mut_ptr()) };
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the kernel guarantees that the struct is fully initialized.
|
// Safe because the kernel guarantees that the struct is fully initialized.
|
||||||
let limit = unsafe { buf.assume_init() };
|
let limit = unsafe { buf.assume_init() };
|
||||||
Ok(limit.rlim_max)
|
Ok(limit.rlim_max)
|
||||||
|
@ -624,6 +645,7 @@ impl sched_attr {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sched_setattr(pid: Pid, attr: &mut sched_attr, flags: u32) -> Result<()> {
|
pub fn sched_setattr(pid: Pid, attr: &mut sched_attr, flags: u32) -> Result<()> {
|
||||||
|
// SAFETY: Safe becuase all the args are valid and the return valud is checked.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::syscall(
|
libc::syscall(
|
||||||
libc::SYS_sched_setattr,
|
libc::SYS_sched_setattr,
|
||||||
|
|
|
@ -143,6 +143,7 @@ impl UnixSeqpacketListener {
|
||||||
///
|
///
|
||||||
/// The returned socket has the close-on-exec flag set.
|
/// The returned socket has the close-on-exec flag set.
|
||||||
pub fn accept(&self) -> io::Result<UnixSeqpacket> {
|
pub fn accept(&self) -> io::Result<UnixSeqpacket> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we own this fd and the kernel will not write to null pointers.
|
// Safe because we own this fd and the kernel will not write to null pointers.
|
||||||
match unsafe {
|
match unsafe {
|
||||||
libc::accept4(
|
libc::accept4(
|
||||||
|
@ -154,11 +155,11 @@ impl UnixSeqpacketListener {
|
||||||
} {
|
} {
|
||||||
-1 => Err(io::Error::last_os_error()),
|
-1 => Err(io::Error::last_os_error()),
|
||||||
fd => {
|
fd => {
|
||||||
// Safe because we checked the return value of accept. Therefore, the return value
|
Ok(UnixSeqpacket::from(
|
||||||
// must be a valid socket.
|
// SAFETY: Safe because we checked the return value of accept. Therefore, the
|
||||||
Ok(UnixSeqpacket::from(unsafe {
|
// return value must be a valid socket.
|
||||||
SafeDescriptor::from_raw_descriptor(fd)
|
unsafe { SafeDescriptor::from_raw_descriptor(fd) },
|
||||||
}))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,6 +179,7 @@ impl AsRawDescriptor for NetlinkGenericSocket {
|
||||||
impl NetlinkGenericSocket {
|
impl NetlinkGenericSocket {
|
||||||
/// Create and bind a new `NETLINK_GENERIC` socket.
|
/// Create and bind a new `NETLINK_GENERIC` socket.
|
||||||
pub fn new(nl_groups: u32) -> Result<Self> {
|
pub fn new(nl_groups: u32) -> Result<Self> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we check the return value and convert the raw fd into a SafeDescriptor.
|
// Safe because we check the return value and convert the raw fd into a SafeDescriptor.
|
||||||
let sock = unsafe {
|
let sock = unsafe {
|
||||||
let fd = libc::socket(
|
let fd = libc::socket(
|
||||||
|
@ -193,12 +194,14 @@ impl NetlinkGenericSocket {
|
||||||
SafeDescriptor::from_raw_descriptor(fd)
|
SafeDescriptor::from_raw_descriptor(fd)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// This MaybeUninit dance is needed because sockaddr_nl has a private padding field and
|
// This MaybeUninit dance is needed because sockaddr_nl has a private padding field and
|
||||||
// doesn't implement Default. Safe because all 0s is valid data for sockaddr_nl.
|
// doesn't implement Default. Safe because all 0s is valid data for sockaddr_nl.
|
||||||
let mut sa = unsafe { MaybeUninit::<libc::sockaddr_nl>::zeroed().assume_init() };
|
let mut sa = unsafe { MaybeUninit::<libc::sockaddr_nl>::zeroed().assume_init() };
|
||||||
sa.nl_family = libc::AF_NETLINK as libc::sa_family_t;
|
sa.nl_family = libc::AF_NETLINK as libc::sa_family_t;
|
||||||
sa.nl_groups = nl_groups;
|
sa.nl_groups = nl_groups;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass a descriptor that we own and valid pointer/size for sockaddr.
|
// Safe because we pass a descriptor that we own and valid pointer/size for sockaddr.
|
||||||
unsafe {
|
unsafe {
|
||||||
let res = libc::bind(
|
let res = libc::bind(
|
||||||
|
@ -223,6 +226,7 @@ impl NetlinkGenericSocket {
|
||||||
.map_err(|_| Error::new(EINVAL))?;
|
.map_err(|_| Error::new(EINVAL))?;
|
||||||
let allocation = LayoutAllocation::uninitialized(layout);
|
let allocation = LayoutAllocation::uninitialized(layout);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass a valid, owned socket fd and a valid pointer/size for the buffer.
|
// Safe because we pass a valid, owned socket fd and a valid pointer/size for the buffer.
|
||||||
let bytes_read = unsafe {
|
let bytes_read = unsafe {
|
||||||
let res = libc::recv(self.sock.as_raw_fd(), allocation.as_ptr(), buf_size, 0);
|
let res = libc::recv(self.sock.as_raw_fd(), allocation.as_ptr(), buf_size, 0);
|
||||||
|
@ -252,6 +256,7 @@ impl NetlinkGenericSocket {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut allocation = LayoutAllocation::zeroed(layout);
|
let mut allocation = LayoutAllocation::zeroed(layout);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the data in allocation was initialized up to `buf_size` and is
|
// Safe because the data in allocation was initialized up to `buf_size` and is
|
||||||
// sufficiently aligned.
|
// sufficiently aligned.
|
||||||
let data = unsafe { allocation.as_mut_slice(buf_size) };
|
let data = unsafe { allocation.as_mut_slice(buf_size) };
|
||||||
|
@ -288,6 +293,7 @@ impl NetlinkGenericSocket {
|
||||||
let payload_end = payload_start + family_name.len();
|
let payload_end = payload_start + family_name.len();
|
||||||
data[payload_start..payload_end].copy_from_slice(family_name.as_bytes());
|
data[payload_start..payload_end].copy_from_slice(family_name.as_bytes());
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass a valid, owned socket fd and a valid pointer/size for the buffer.
|
// Safe because we pass a valid, owned socket fd and a valid pointer/size for the buffer.
|
||||||
unsafe {
|
unsafe {
|
||||||
let res = libc::send(
|
let res = libc::send(
|
||||||
|
@ -430,6 +436,7 @@ pub struct NetlinkGenericRead {
|
||||||
|
|
||||||
impl NetlinkGenericRead {
|
impl NetlinkGenericRead {
|
||||||
pub fn iter(&self) -> NetlinkMessageIter {
|
pub fn iter(&self) -> NetlinkMessageIter {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the data in allocation was initialized up to `self.len` by `recv()` and is
|
// Safe because the data in allocation was initialized up to `self.len` by `recv()` and is
|
||||||
// sufficiently aligned.
|
// sufficiently aligned.
|
||||||
let data = unsafe { &self.allocation.as_slice(self.len) };
|
let data = unsafe { &self.allocation.as_slice(self.len) };
|
||||||
|
|
|
@ -61,12 +61,15 @@ pub struct EventContext<T> {
|
||||||
impl<T: EventToken> EventContext<T> {
|
impl<T: EventToken> EventContext<T> {
|
||||||
/// Creates a new `EventContext`.
|
/// Creates a new `EventContext`.
|
||||||
pub fn new() -> Result<EventContext<T>> {
|
pub fn new() -> Result<EventContext<T>> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we check the return value.
|
// Safe because we check the return value.
|
||||||
let epoll_fd = unsafe { epoll_create1(EPOLL_CLOEXEC) };
|
let epoll_fd = unsafe { epoll_create1(EPOLL_CLOEXEC) };
|
||||||
if epoll_fd < 0 {
|
if epoll_fd < 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
Ok(EventContext {
|
Ok(EventContext {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because epoll_fd is valid.
|
||||||
epoll_ctx: unsafe { File::from_raw_descriptor(epoll_fd) },
|
epoll_ctx: unsafe { File::from_raw_descriptor(epoll_fd) },
|
||||||
tokens: PhantomData,
|
tokens: PhantomData,
|
||||||
})
|
})
|
||||||
|
@ -122,6 +125,7 @@ impl<T: EventToken> EventContext<T> {
|
||||||
events: event_type.into(),
|
events: event_type.into(),
|
||||||
u64: token.as_raw_token(),
|
u64: token.as_raw_token(),
|
||||||
};
|
};
|
||||||
|
// SAFETY:
|
||||||
// Safe because we give a valid epoll FD and FD to watch, as well as a valid epoll_event
|
// Safe because we give a valid epoll FD and FD to watch, as well as a valid epoll_event
|
||||||
// structure. Then we check the return value.
|
// structure. Then we check the return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -145,6 +149,7 @@ impl<T: EventToken> EventContext<T> {
|
||||||
events: event_type.into(),
|
events: event_type.into(),
|
||||||
u64: token.as_raw_token(),
|
u64: token.as_raw_token(),
|
||||||
};
|
};
|
||||||
|
// SAFETY:
|
||||||
// Safe because we give a valid epoll FD and FD to modify, as well as a valid epoll_event
|
// Safe because we give a valid epoll FD and FD to modify, as well as a valid epoll_event
|
||||||
// structure. Then we check the return value.
|
// structure. Then we check the return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -169,6 +174,7 @@ impl<T: EventToken> EventContext<T> {
|
||||||
/// Failure to do so will cause the `wait` method to always return immediately, causing ~100%
|
/// Failure to do so will cause the `wait` method to always return immediately, causing ~100%
|
||||||
/// CPU load.
|
/// CPU load.
|
||||||
pub fn delete(&self, fd: &dyn AsRawDescriptor) -> Result<()> {
|
pub fn delete(&self, fd: &dyn AsRawDescriptor) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we give a valid epoll FD and FD to stop watching. Then we check the return
|
// Safe because we give a valid epoll FD and FD to stop watching. Then we check the return
|
||||||
// value.
|
// value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -203,12 +209,12 @@ impl<T: EventToken> EventContext<T> {
|
||||||
/// This may return earlier than `timeout` with zero events if the duration indicated exceeds
|
/// This may return earlier than `timeout` with zero events if the duration indicated exceeds
|
||||||
/// system limits.
|
/// system limits.
|
||||||
pub fn wait_timeout(&self, timeout: Duration) -> Result<SmallVec<[TriggeredEvent<T>; 16]>> {
|
pub fn wait_timeout(&self, timeout: Duration) -> Result<SmallVec<[TriggeredEvent<T>; 16]>> {
|
||||||
// SAFETY:
|
|
||||||
// `MaybeUnint<T>` has the same layout as plain `T` (`epoll_event` in our case).
|
|
||||||
// We submit an uninitialized array to the `epoll_wait` system call, which returns how many
|
|
||||||
// elements it initialized, and then we convert only the initialized `MaybeUnint` values
|
|
||||||
// into `epoll_event` structures after the call.
|
|
||||||
let mut epoll_events: [MaybeUninit<epoll_event>; EVENT_CONTEXT_MAX_EVENTS] =
|
let mut epoll_events: [MaybeUninit<epoll_event>; EVENT_CONTEXT_MAX_EVENTS] =
|
||||||
|
// SAFETY:
|
||||||
|
// `MaybeUnint<T>` has the same layout as plain `T` (`epoll_event` in our case).
|
||||||
|
// We submit an uninitialized array to the `epoll_wait` system call, which returns how many
|
||||||
|
// elements it initialized, and then we convert only the initialized `MaybeUnint` values
|
||||||
|
// into `epoll_event` structures after the call.
|
||||||
unsafe { MaybeUninit::uninit().assume_init() };
|
unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
|
|
||||||
let timeout_millis = if timeout.as_secs() as i64 == i64::max_value() {
|
let timeout_millis = if timeout.as_secs() as i64 == i64::max_value() {
|
||||||
|
@ -227,6 +233,7 @@ impl<T: EventToken> EventContext<T> {
|
||||||
};
|
};
|
||||||
let ret = {
|
let ret = {
|
||||||
let max_events = epoll_events.len() as c_int;
|
let max_events = epoll_events.len() as c_int;
|
||||||
|
// SAFETY:
|
||||||
// Safe because we give an epoll context and a properly sized epoll_events array
|
// Safe because we give an epoll context and a properly sized epoll_events array
|
||||||
// pointer, which we trust the kernel to fill in properly. The `transmute` is safe,
|
// pointer, which we trust the kernel to fill in properly. The `transmute` is safe,
|
||||||
// since `MaybeUnint<T>` has the same layout as `T`, and the `epoll_wait` syscall will
|
// since `MaybeUnint<T>` has the same layout as `T`, and the `epoll_wait` syscall will
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub fn set_rt_prio_limit(limit: u64) -> Result<()> {
|
||||||
rlim_cur: limit,
|
rlim_cur: limit,
|
||||||
rlim_max: limit,
|
rlim_max: limit,
|
||||||
};
|
};
|
||||||
|
// SAFETY:
|
||||||
// Safe because the kernel doesn't modify memory that is accessible to the process here.
|
// Safe because the kernel doesn't modify memory that is accessible to the process here.
|
||||||
let res = unsafe { libc::setrlimit64(libc::RLIMIT_RTPRIO, &rt_limit_arg) };
|
let res = unsafe { libc::setrlimit64(libc::RLIMIT_RTPRIO, &rt_limit_arg) };
|
||||||
|
|
||||||
|
@ -25,13 +26,15 @@ pub fn set_rt_prio_limit(limit: u64) -> Result<()> {
|
||||||
|
|
||||||
/// Sets the current thread to be scheduled using the round robin real time class with `priority`.
|
/// Sets the current thread to be scheduled using the round robin real time class with `priority`.
|
||||||
pub fn set_rt_round_robin(priority: i32) -> Result<()> {
|
pub fn set_rt_round_robin(priority: i32) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because sched_param only contains primitive types for which zero
|
// Safe because sched_param only contains primitive types for which zero
|
||||||
// initialization is valid.
|
// initialization is valid.
|
||||||
let mut sched_param: libc::sched_param = unsafe { MaybeUninit::zeroed().assume_init() };
|
let mut sched_param: libc::sched_param = unsafe { MaybeUninit::zeroed().assume_init() };
|
||||||
sched_param.sched_priority = priority;
|
sched_param.sched_priority = priority;
|
||||||
|
|
||||||
// Safe because the kernel doesn't modify memory that is accessible to the process here.
|
|
||||||
let res =
|
let res =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because the kernel doesn't modify memory that is accessible to the process here.
|
||||||
unsafe { libc::pthread_setschedparam(libc::pthread_self(), libc::SCHED_RR, &sched_param) };
|
unsafe { libc::pthread_setschedparam(libc::pthread_self(), libc::SCHED_RR, &sched_param) };
|
||||||
|
|
||||||
if res != 0 {
|
if res != 0 {
|
||||||
|
|
|
@ -98,6 +98,7 @@ where
|
||||||
|
|
||||||
let tz = std::env::var("TZ").unwrap_or_default();
|
let tz = std::env::var("TZ").unwrap_or_default();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the program is still single threaded.
|
// Safe because the program is still single threaded.
|
||||||
// We own the jail object and nobody else will try to reuse it.
|
// We own the jail object and nobody else will try to reuse it.
|
||||||
let pid = match unsafe { jail.fork(Some(&keep_rds)) }? {
|
let pid = match unsafe { jail.fork(Some(&keep_rds)) }? {
|
||||||
|
@ -119,6 +120,7 @@ where
|
||||||
[..std::cmp::min(MAX_THREAD_LABEL_LEN, debug_label.len())];
|
[..std::cmp::min(MAX_THREAD_LABEL_LEN, debug_label.len())];
|
||||||
match CString::new(debug_label_trimmed) {
|
match CString::new(debug_label_trimmed) {
|
||||||
Ok(thread_name) => {
|
Ok(thread_name) => {
|
||||||
|
// SAFETY:
|
||||||
// Safe because thread_name is a valid pointer and setting name of this
|
// Safe because thread_name is a valid pointer and setting name of this
|
||||||
// thread should be safe.
|
// thread should be safe.
|
||||||
let _ = unsafe {
|
let _ = unsafe {
|
||||||
|
@ -150,6 +152,7 @@ where
|
||||||
None => "process.rs: no debug label".to_owned(),
|
None => "process.rs: no debug label".to_owned(),
|
||||||
},
|
},
|
||||||
// Can't use safe wrapper because jail crate depends on base
|
// Can't use safe wrapper because jail crate depends on base
|
||||||
|
// SAFETY:
|
||||||
// Safe because it's only doing a read within bound checked by static assert
|
// Safe because it's only doing a read within bound checked by static assert
|
||||||
unsafe {*(&jail as *const Minijail as *const usize)}
|
unsafe {*(&jail as *const Minijail as *const usize)}
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,8 +26,10 @@ struct CpuSet(cpu_set_t);
|
||||||
|
|
||||||
impl CpuSet {
|
impl CpuSet {
|
||||||
pub fn new() -> CpuSet {
|
pub fn new() -> CpuSet {
|
||||||
|
// SAFETY:
|
||||||
// cpu_set_t is a C struct and can be safely initialized with zeroed memory.
|
// cpu_set_t is a C struct and can be safely initialized with zeroed memory.
|
||||||
let mut cpuset: cpu_set_t = unsafe { mem::MaybeUninit::zeroed().assume_init() };
|
let mut cpuset: cpu_set_t = unsafe { mem::MaybeUninit::zeroed().assume_init() };
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass a valid cpuset pointer.
|
// Safe because we pass a valid cpuset pointer.
|
||||||
unsafe { CPU_ZERO(&mut cpuset) };
|
unsafe { CPU_ZERO(&mut cpuset) };
|
||||||
CpuSet(cpuset)
|
CpuSet(cpuset)
|
||||||
|
@ -36,6 +38,7 @@ impl CpuSet {
|
||||||
pub fn to_cpus(&self) -> Vec<usize> {
|
pub fn to_cpus(&self) -> Vec<usize> {
|
||||||
let mut cpus = Vec::new();
|
let mut cpus = Vec::new();
|
||||||
for i in 0..(CPU_SETSIZE as usize) {
|
for i in 0..(CPU_SETSIZE as usize) {
|
||||||
|
// SAFETY: Safe because `i` and `self.0` are valid.
|
||||||
if unsafe { CPU_ISSET(i, &self.0) } {
|
if unsafe { CPU_ISSET(i, &self.0) } {
|
||||||
cpus.push(i);
|
cpus.push(i);
|
||||||
}
|
}
|
||||||
|
@ -48,6 +51,7 @@ impl FromIterator<usize> for CpuSet {
|
||||||
fn from_iter<I: IntoIterator<Item = usize>>(cpus: I) -> Self {
|
fn from_iter<I: IntoIterator<Item = usize>>(cpus: I) -> Self {
|
||||||
let mut cpuset = CpuSet::new();
|
let mut cpuset = CpuSet::new();
|
||||||
for cpu in cpus {
|
for cpu in cpus {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass a valid cpu index and cpuset.0 is a valid pointer.
|
// Safe because we pass a valid cpu index and cpuset.0 is a valid pointer.
|
||||||
unsafe { CPU_SET(cpu, &mut cpuset.0) };
|
unsafe { CPU_SET(cpu, &mut cpuset.0) };
|
||||||
}
|
}
|
||||||
|
@ -78,6 +82,7 @@ pub fn set_cpu_affinity<I: IntoIterator<Item = usize>>(cpus: I) -> Result<()> {
|
||||||
})
|
})
|
||||||
.collect::<Result<CpuSet>>()?;
|
.collect::<Result<CpuSet>>()?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass 0 for the current thread, and cpuset is a valid pointer and only
|
// Safe because we pass 0 for the current thread, and cpuset is a valid pointer and only
|
||||||
// used for the duration of this call.
|
// used for the duration of this call.
|
||||||
crate::syscall!(unsafe { sched_setaffinity(0, mem::size_of_val(&cpuset), &cpuset) })?;
|
crate::syscall!(unsafe { sched_setaffinity(0, mem::size_of_val(&cpuset), &cpuset) })?;
|
||||||
|
@ -88,6 +93,7 @@ pub fn set_cpu_affinity<I: IntoIterator<Item = usize>>(cpus: I) -> Result<()> {
|
||||||
pub fn get_cpu_affinity() -> Result<Vec<usize>> {
|
pub fn get_cpu_affinity() -> Result<Vec<usize>> {
|
||||||
let mut cpu_set = CpuSet::new();
|
let mut cpu_set = CpuSet::new();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass 0 for the current thread, and cpu_set.0 is a valid pointer and only
|
// Safe because we pass 0 for the current thread, and cpu_set.0 is a valid pointer and only
|
||||||
// used for the duration of this call.
|
// used for the duration of this call.
|
||||||
crate::syscall!(unsafe { sched_getaffinity(0, mem::size_of_val(&cpu_set.0), &mut cpu_set.0) })?;
|
crate::syscall!(unsafe { sched_getaffinity(0, mem::size_of_val(&cpu_set.0), &mut cpu_set.0) })?;
|
||||||
|
@ -115,6 +121,7 @@ pub fn enable_core_scheduling() -> Result<()> {
|
||||||
PIDTYPE_PGID,
|
PIDTYPE_PGID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: Safe because we check the return value to prctl.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
prctl(
|
prctl(
|
||||||
PR_SCHED_CORE,
|
PR_SCHED_CORE,
|
||||||
|
|
|
@ -40,6 +40,8 @@ use crate::SharedMemory;
|
||||||
const MFD_CLOEXEC: c_uint = 0x0001;
|
const MFD_CLOEXEC: c_uint = 0x0001;
|
||||||
const MFD_NOEXEC_SEAL: c_uint = 0x0008;
|
const MFD_NOEXEC_SEAL: c_uint = 0x0008;
|
||||||
|
|
||||||
|
// SAFETY: It is caller's responsibility to ensure the args are valid and check the
|
||||||
|
// return value of the function.
|
||||||
unsafe fn memfd_create(name: *const c_char, flags: c_uint) -> c_int {
|
unsafe fn memfd_create(name: *const c_char, flags: c_uint) -> c_int {
|
||||||
syscall(SYS_memfd_create as c_long, name, flags) as c_int
|
syscall(SYS_memfd_create as c_long, name, flags) as c_int
|
||||||
}
|
}
|
||||||
|
@ -165,15 +167,19 @@ impl PlatformSharedMemory for SharedMemory {
|
||||||
}
|
}
|
||||||
|
|
||||||
let shm_name = debug_name.as_ptr() as *const c_char;
|
let shm_name = debug_name.as_ptr() as *const c_char;
|
||||||
|
// SAFETY:
|
||||||
// The following are safe because we give a valid C string and check the
|
// The following are safe because we give a valid C string and check the
|
||||||
// results of the memfd_create call.
|
// results of the memfd_create call.
|
||||||
let fd = unsafe { memfd_create(shm_name, flags) };
|
let fd = unsafe { memfd_create(shm_name, flags) };
|
||||||
if fd < 0 {
|
if fd < 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
|
// SAFETY: Safe because fd is valid.
|
||||||
let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
|
let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
|
||||||
|
|
||||||
// Set the size of the memfd.
|
// Set the size of the memfd.
|
||||||
|
// SAFETY: Safe because we check the return value to ftruncate64 and all the args to the
|
||||||
|
// function are valid.
|
||||||
let ret = unsafe { ftruncate64(descriptor.as_raw_descriptor(), size as off64_t) };
|
let ret = unsafe { ftruncate64(descriptor.as_raw_descriptor(), size as off64_t) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
|
@ -219,6 +225,8 @@ impl SharedMemoryLinux for SharedMemory {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_seals(&self) -> Result<MemfdSeals> {
|
fn get_seals(&self) -> Result<MemfdSeals> {
|
||||||
|
// SAFETY: Safe because we check the return value to fcntl and all the args to the
|
||||||
|
// function are valid.
|
||||||
let ret = unsafe { fcntl(self.descriptor.as_raw_descriptor(), F_GET_SEALS) };
|
let ret = unsafe { fcntl(self.descriptor.as_raw_descriptor(), F_GET_SEALS) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
|
@ -227,6 +235,8 @@ impl SharedMemoryLinux for SharedMemory {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_seals(&mut self, seals: MemfdSeals) -> Result<()> {
|
fn add_seals(&mut self, seals: MemfdSeals) -> Result<()> {
|
||||||
|
// SAFETY: Safe because we check the return value to fcntl and all the args to the
|
||||||
|
// function are valid.
|
||||||
let ret = unsafe { fcntl(self.descriptor.as_raw_descriptor(), F_ADD_SEALS, seals) };
|
let ret = unsafe { fcntl(self.descriptor.as_raw_descriptor(), F_ADD_SEALS, seals) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
|
|
|
@ -277,12 +277,14 @@ extern "C" {
|
||||||
/// Returns the minimum (inclusive) real-time signal number.
|
/// Returns the minimum (inclusive) real-time signal number.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn SIGRTMIN() -> c_int {
|
pub fn SIGRTMIN() -> c_int {
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe { __libc_current_sigrtmin() }
|
unsafe { __libc_current_sigrtmin() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the maximum (inclusive) real-time signal number.
|
/// Returns the maximum (inclusive) real-time signal number.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn SIGRTMAX() -> c_int {
|
pub fn SIGRTMAX() -> c_int {
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe { __libc_current_sigrtmax() }
|
unsafe { __libc_current_sigrtmax() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,11 +313,13 @@ pub unsafe fn register_signal_handler(num: c_int, handler: extern "C" fn(c_int))
|
||||||
|
|
||||||
/// Resets the signal handler of signum `num` back to the default.
|
/// Resets the signal handler of signum `num` back to the default.
|
||||||
pub fn clear_signal_handler(num: c_int) -> Result<()> {
|
pub fn clear_signal_handler(num: c_int) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because sigaction is owned and expected to be initialized ot zeros.
|
// Safe because sigaction is owned and expected to be initialized ot zeros.
|
||||||
let mut sigact: sigaction = unsafe { mem::zeroed() };
|
let mut sigact: sigaction = unsafe { mem::zeroed() };
|
||||||
sigact.sa_flags = SA_RESTART;
|
sigact.sa_flags = SA_RESTART;
|
||||||
sigact.sa_sigaction = SIG_DFL;
|
sigact.sa_sigaction = SIG_DFL;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because sigact is owned, and this is restoring the default signal handler.
|
// Safe because sigact is owned, and this is restoring the default signal handler.
|
||||||
let ret = unsafe { sigaction(num, &sigact, null_mut()) };
|
let ret = unsafe { sigaction(num, &sigact, null_mut()) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -345,9 +349,11 @@ pub unsafe fn register_rt_signal_handler(num: c_int, handler: extern "C" fn(c_in
|
||||||
///
|
///
|
||||||
/// This is a helper function used when we want to manipulate signals.
|
/// This is a helper function used when we want to manipulate signals.
|
||||||
pub fn create_sigset(signals: &[c_int]) -> Result<sigset_t> {
|
pub fn create_sigset(signals: &[c_int]) -> Result<sigset_t> {
|
||||||
|
// SAFETY:
|
||||||
// sigset will actually be initialized by sigemptyset below.
|
// sigset will actually be initialized by sigemptyset below.
|
||||||
let mut sigset: sigset_t = unsafe { mem::zeroed() };
|
let mut sigset: sigset_t = unsafe { mem::zeroed() };
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe - return value is checked.
|
// Safe - return value is checked.
|
||||||
let ret = unsafe { sigemptyset(&mut sigset) };
|
let ret = unsafe { sigemptyset(&mut sigset) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -355,6 +361,7 @@ pub fn create_sigset(signals: &[c_int]) -> Result<sigset_t> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for signal in signals {
|
for signal in signals {
|
||||||
|
// SAFETY:
|
||||||
// Safe - return value is checked.
|
// Safe - return value is checked.
|
||||||
let ret = unsafe { sigaddset(&mut sigset, *signal) };
|
let ret = unsafe { sigaddset(&mut sigset, *signal) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -373,6 +380,7 @@ pub fn wait_for_signal(signals: &[c_int], timeout: Option<Duration>) -> Result<c
|
||||||
match timeout {
|
match timeout {
|
||||||
Some(timeout) => {
|
Some(timeout) => {
|
||||||
let ts = duration_to_timespec(timeout);
|
let ts = duration_to_timespec(timeout);
|
||||||
|
// SAFETY:
|
||||||
// Safe - return value is checked.
|
// Safe - return value is checked.
|
||||||
let ret = handle_eintr_errno!(unsafe { sigtimedwait(&sigset, null_mut(), &ts) });
|
let ret = handle_eintr_errno!(unsafe { sigtimedwait(&sigset, null_mut(), &ts) });
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -383,6 +391,7 @@ pub fn wait_for_signal(signals: &[c_int], timeout: Option<Duration>) -> Result<c
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let mut ret: c_int = 0;
|
let mut ret: c_int = 0;
|
||||||
|
// SAFETY: Safe because args are valid and the return value is checked.
|
||||||
let err = handle_eintr_rc!(unsafe { sigwait(&sigset, &mut ret as *mut c_int) });
|
let err = handle_eintr_rc!(unsafe { sigwait(&sigset, &mut ret as *mut c_int) });
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
Err(ErrnoError::new(err))
|
Err(ErrnoError::new(err))
|
||||||
|
@ -397,6 +406,7 @@ pub fn wait_for_signal(signals: &[c_int], timeout: Option<Duration>) -> Result<c
|
||||||
pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
|
pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
|
||||||
let mut mask = Vec::new();
|
let mut mask = Vec::new();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe - return values are checked.
|
// Safe - return values are checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut old_sigset: sigset_t = mem::zeroed();
|
let mut old_sigset: sigset_t = mem::zeroed();
|
||||||
|
@ -422,6 +432,7 @@ pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
|
||||||
pub fn block_signal(num: c_int) -> SignalResult<()> {
|
pub fn block_signal(num: c_int) -> SignalResult<()> {
|
||||||
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
|
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe - return values are checked.
|
// Safe - return values are checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut old_sigset: sigset_t = mem::zeroed();
|
let mut old_sigset: sigset_t = mem::zeroed();
|
||||||
|
@ -447,6 +458,7 @@ pub fn block_signal(num: c_int) -> SignalResult<()> {
|
||||||
pub fn unblock_signal(num: c_int) -> SignalResult<()> {
|
pub fn unblock_signal(num: c_int) -> SignalResult<()> {
|
||||||
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
|
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe - return value is checked.
|
// Safe - return value is checked.
|
||||||
let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) };
|
let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -460,6 +472,7 @@ pub fn clear_signal(num: c_int) -> SignalResult<()> {
|
||||||
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
|
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
|
||||||
|
|
||||||
while {
|
while {
|
||||||
|
// SAFETY:
|
||||||
// This is safe as we are rigorously checking return values
|
// This is safe as we are rigorously checking return values
|
||||||
// of libc calls.
|
// of libc calls.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -535,6 +548,7 @@ pub unsafe trait Killable {
|
||||||
return Err(ErrnoError::new(EINVAL));
|
return Err(ErrnoError::new(EINVAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we ensure we are using a valid pthread handle, a valid signal number, and
|
// Safe because we ensure we are using a valid pthread handle, a valid signal number, and
|
||||||
// check the return result.
|
// check the return result.
|
||||||
let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
|
let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
|
||||||
|
@ -545,6 +559,7 @@ pub unsafe trait Killable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we fulfill our contract of returning a genuine pthread handle.
|
// Safe because we fulfill our contract of returning a genuine pthread handle.
|
||||||
unsafe impl<T> Killable for JoinHandle<T> {
|
unsafe impl<T> Killable for JoinHandle<T> {
|
||||||
fn pthread_handle(&self) -> pthread_t {
|
fn pthread_handle(&self) -> pthread_t {
|
||||||
|
|
|
@ -67,6 +67,7 @@ impl SignalFd {
|
||||||
pub fn new(signal: c_int) -> Result<SignalFd> {
|
pub fn new(signal: c_int) -> Result<SignalFd> {
|
||||||
let sigset = signal::create_sigset(&[signal]).map_err(Error::CreateSigset)?;
|
let sigset = signal::create_sigset(&[signal]).map_err(Error::CreateSigset)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// This is safe as we check the return value and know that fd is valid.
|
// This is safe as we check the return value and know that fd is valid.
|
||||||
let fd = unsafe { signalfd(-1, &sigset, SFD_CLOEXEC | SFD_NONBLOCK) };
|
let fd = unsafe { signalfd(-1, &sigset, SFD_CLOEXEC | SFD_NONBLOCK) };
|
||||||
if fd < 0 {
|
if fd < 0 {
|
||||||
|
@ -76,6 +77,7 @@ impl SignalFd {
|
||||||
// Mask out the normal handler for the signal.
|
// Mask out the normal handler for the signal.
|
||||||
signal::block_signal(signal).map_err(Error::CreateBlockSignal)?;
|
signal::block_signal(signal).map_err(Error::CreateBlockSignal)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we checked fd for success and know the
|
// This is safe because we checked fd for success and know the
|
||||||
// kernel gave us an fd that we own.
|
// kernel gave us an fd that we own.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -88,10 +90,12 @@ impl SignalFd {
|
||||||
|
|
||||||
/// Read a siginfo struct from the signalfd, if available.
|
/// Read a siginfo struct from the signalfd, if available.
|
||||||
pub fn read(&self) -> Result<Option<signalfd_siginfo>> {
|
pub fn read(&self) -> Result<Option<signalfd_siginfo>> {
|
||||||
|
// SAFETY:
|
||||||
// signalfd_siginfo doesn't have a default, so just zero it.
|
// signalfd_siginfo doesn't have a default, so just zero it.
|
||||||
let mut siginfo: signalfd_siginfo = unsafe { mem::zeroed() };
|
let mut siginfo: signalfd_siginfo = unsafe { mem::zeroed() };
|
||||||
let siginfo_size = mem::size_of::<signalfd_siginfo>();
|
let siginfo_size = mem::size_of::<signalfd_siginfo>();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// This read is safe since we've got the space allocated for a
|
// This read is safe since we've got the space allocated for a
|
||||||
// single signalfd_siginfo, and that's exactly how much we're
|
// single signalfd_siginfo, and that's exactly how much we're
|
||||||
// reading. Handling of EINTR is not required since SFD_NONBLOCK
|
// reading. Handling of EINTR is not required since SFD_NONBLOCK
|
||||||
|
@ -166,6 +170,7 @@ mod tests {
|
||||||
let sigid = SIGRTMIN() + 1;
|
let sigid = SIGRTMIN() + 1;
|
||||||
let sigrt_fd = SignalFd::new(sigid).unwrap();
|
let sigrt_fd = SignalFd::new(sigid).unwrap();
|
||||||
|
|
||||||
|
// SAFETY: Safe because sigid is valid and return value is checked.
|
||||||
let ret = unsafe { raise(sigid) };
|
let ret = unsafe { raise(sigid) };
|
||||||
assert_eq!(ret, 0);
|
assert_eq!(ret, 0);
|
||||||
|
|
||||||
|
@ -178,6 +183,7 @@ mod tests {
|
||||||
let sigid = SIGRTMIN() + 2;
|
let sigid = SIGRTMIN() + 2;
|
||||||
|
|
||||||
let sigrt_fd = SignalFd::new(sigid).unwrap();
|
let sigrt_fd = SignalFd::new(sigid).unwrap();
|
||||||
|
// SAFETY: Safe because sigset and sigid are valid and return value is checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut sigset: sigset_t = mem::zeroed();
|
let mut sigset: sigset_t = mem::zeroed();
|
||||||
pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
|
pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
|
||||||
|
@ -187,6 +193,7 @@ mod tests {
|
||||||
mem::drop(sigrt_fd);
|
mem::drop(sigrt_fd);
|
||||||
|
|
||||||
// The signal should no longer be masked.
|
// The signal should no longer be masked.
|
||||||
|
// SAFETY: Safe because sigset and sigid are valid and return value is checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut sigset: sigset_t = mem::zeroed();
|
let mut sigset: sigset_t = mem::zeroed();
|
||||||
pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
|
pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
|
||||||
|
|
|
@ -24,20 +24,26 @@ use crate::unix::add_fd_flags;
|
||||||
use crate::unix::clear_fd_flags;
|
use crate::unix::clear_fd_flags;
|
||||||
|
|
||||||
fn modify_mode<F: FnOnce(&mut termios)>(fd: RawFd, f: F) -> Result<()> {
|
fn modify_mode<F: FnOnce(&mut termios)>(fd: RawFd, f: F) -> Result<()> {
|
||||||
|
// Safety:
|
||||||
// Safe because we check the return value of isatty.
|
// Safe because we check the return value of isatty.
|
||||||
if unsafe { isatty(fd) } != 1 {
|
if unsafe { isatty(fd) } != 1 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Safety:
|
||||||
// The following pair are safe because termios gets totally overwritten by tcgetattr and we
|
// The following pair are safe because termios gets totally overwritten by tcgetattr and we
|
||||||
// check the return result.
|
// check the return result.
|
||||||
let mut termios: termios = unsafe { zeroed() };
|
let mut termios: termios = unsafe { zeroed() };
|
||||||
|
// Safety:
|
||||||
|
// The following pair are safe because termios gets totally overwritten by tcgetattr and we
|
||||||
|
// check the return result.
|
||||||
let ret = unsafe { tcgetattr(fd, &mut termios as *mut _) };
|
let ret = unsafe { tcgetattr(fd, &mut termios as *mut _) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
let mut new_termios = termios;
|
let mut new_termios = termios;
|
||||||
f(&mut new_termios);
|
f(&mut new_termios);
|
||||||
|
// SAFETY:
|
||||||
// Safe because the syscall will only read the extent of termios and we check the return result.
|
// Safe because the syscall will only read the extent of termios and we check the return result.
|
||||||
let ret = unsafe { tcsetattr(fd, TCSANOW, &new_termios as *const _) };
|
let ret = unsafe { tcsetattr(fd, TCSANOW, &new_termios as *const _) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -47,6 +53,8 @@ fn modify_mode<F: FnOnce(&mut termios)>(fd: RawFd, f: F) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
/// Safe only when the FD given is valid and reading the fd will have no Rust safety implications.
|
/// Safe only when the FD given is valid and reading the fd will have no Rust safety implications.
|
||||||
unsafe fn read_raw(fd: RawFd, out: &mut [u8]) -> Result<usize> {
|
unsafe fn read_raw(fd: RawFd, out: &mut [u8]) -> Result<usize> {
|
||||||
let ret = read(fd, out.as_mut_ptr() as *mut _, out.len());
|
let ret = read(fd, out.as_mut_ptr() as *mut _, out.len());
|
||||||
|
@ -63,6 +71,7 @@ unsafe fn read_raw(fd: RawFd, out: &mut [u8]) -> Result<usize> {
|
||||||
/// around stdin that the stdlib usually uses. If other code is using stdin, it is undefined who
|
/// around stdin that the stdlib usually uses. If other code is using stdin, it is undefined who
|
||||||
/// will get the underlying bytes.
|
/// will get the underlying bytes.
|
||||||
pub fn read_raw_stdin(out: &mut [u8]) -> Result<usize> {
|
pub fn read_raw_stdin(out: &mut [u8]) -> Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because reading from stdin shouldn't have any safety implications.
|
// Safe because reading from stdin shouldn't have any safety implications.
|
||||||
unsafe { read_raw(STDIN_FILENO, out) }
|
unsafe { read_raw(STDIN_FILENO, out) }
|
||||||
}
|
}
|
||||||
|
@ -99,6 +108,7 @@ pub unsafe trait Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// # SAFETY:
|
||||||
// Safe because we return a genuine terminal fd that never changes and shares our lifetime.
|
// Safe because we return a genuine terminal fd that never changes and shares our lifetime.
|
||||||
unsafe impl Terminal for Stdin {
|
unsafe impl Terminal for Stdin {
|
||||||
fn tty_fd(&self) -> RawFd {
|
fn tty_fd(&self) -> RawFd {
|
||||||
|
|
|
@ -37,14 +37,16 @@ impl Timer {
|
||||||
/// Creates a new timerfd. The timer is initally disarmed and must be armed by calling
|
/// Creates a new timerfd. The timer is initally disarmed and must be armed by calling
|
||||||
/// `reset`.
|
/// `reset`.
|
||||||
pub fn new() -> Result<Timer> {
|
pub fn new() -> Result<Timer> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe { timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC) };
|
let ret = unsafe { timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe because we uniquely own the file descriptor.
|
|
||||||
Ok(Timer {
|
Ok(Timer {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we uniquely own the file descriptor.
|
||||||
handle: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
|
handle: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
|
||||||
interval: None,
|
interval: None,
|
||||||
})
|
})
|
||||||
|
@ -61,6 +63,7 @@ impl Timer {
|
||||||
it_value: duration_to_timespec(dur.unwrap_or_default()),
|
it_value: duration_to_timespec(dur.unwrap_or_default()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe { timerfd_settime(self.as_raw_descriptor(), 0, &spec, ptr::null_mut()) };
|
let ret = unsafe { timerfd_settime(self.as_raw_descriptor(), 0, &spec, ptr::null_mut()) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -87,6 +90,7 @@ impl TimerTrait for Timer {
|
||||||
revents: 0,
|
revents: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because this only modifies |pfd| and we check the return value
|
// Safe because this only modifies |pfd| and we check the return value
|
||||||
let ret = handle_eintr_errno!(unsafe {
|
let ret = handle_eintr_errno!(unsafe {
|
||||||
libc::ppoll(
|
libc::ppoll(
|
||||||
|
@ -113,6 +117,7 @@ impl TimerTrait for Timer {
|
||||||
fn mark_waited(&mut self) -> Result<bool> {
|
fn mark_waited(&mut self) -> Result<bool> {
|
||||||
let mut count = 0u64;
|
let mut count = 0u64;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// The timerfd is in non-blocking mode, so this should return immediately.
|
// The timerfd is in non-blocking mode, so this should return immediately.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::read(
|
libc::read(
|
||||||
|
@ -134,9 +139,11 @@ impl TimerTrait for Timer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolution(&self) -> Result<Duration> {
|
fn resolution(&self) -> Result<Duration> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we are zero-initializing a struct with only primitive member fields.
|
// Safe because we are zero-initializing a struct with only primitive member fields.
|
||||||
let mut res: libc::timespec = unsafe { mem::zeroed() };
|
let mut res: libc::timespec = unsafe { mem::zeroed() };
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because it only modifies a local struct and we check the return value.
|
// Safe because it only modifies a local struct and we check the return value.
|
||||||
let ret = unsafe { clock_getres(CLOCK_MONOTONIC, &mut res) };
|
let ret = unsafe { clock_getres(CLOCK_MONOTONIC, &mut res) };
|
||||||
|
|
||||||
|
|
|
@ -214,6 +214,7 @@ pub struct VsockSocket {
|
||||||
|
|
||||||
impl VsockSocket {
|
impl VsockSocket {
|
||||||
pub fn new() -> io::Result<Self> {
|
pub fn new() -> io::Result<Self> {
|
||||||
|
// SAFETY: trivially safe
|
||||||
let fd = unsafe { libc::socket(libc::AF_VSOCK, libc::SOCK_STREAM | libc::SOCK_CLOEXEC, 0) };
|
let fd = unsafe { libc::socket(libc::AF_VSOCK, libc::SOCK_STREAM | libc::SOCK_CLOEXEC, 0) };
|
||||||
if fd < 0 {
|
if fd < 0 {
|
||||||
Err(io::Error::last_os_error())
|
Err(io::Error::last_os_error())
|
||||||
|
@ -237,6 +238,7 @@ impl VsockSocket {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::bind(
|
libc::bind(
|
||||||
|
@ -265,6 +267,7 @@ impl VsockSocket {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because this just connects a vsock socket, and the return value is checked.
|
// Safe because this just connects a vsock socket, and the return value is checked.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::connect(
|
libc::connect(
|
||||||
|
@ -282,6 +285,7 @@ impl VsockSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen(self) -> io::Result<VsockListener> {
|
pub fn listen(self) -> io::Result<VsockListener> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe { libc::listen(self.fd, 1) };
|
let ret = unsafe { libc::listen(self.fd, 1) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -295,8 +299,9 @@ impl VsockSocket {
|
||||||
pub fn local_port(&self) -> io::Result<u32> {
|
pub fn local_port(&self) -> io::Result<u32> {
|
||||||
let mut svm: sockaddr_vm = Default::default();
|
let mut svm: sockaddr_vm = Default::default();
|
||||||
|
|
||||||
// Safe because we give a valid pointer for addrlen and check the length.
|
|
||||||
let mut addrlen = size_of::<sockaddr_vm>() as socklen_t;
|
let mut addrlen = size_of::<sockaddr_vm>() as socklen_t;
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we give a valid pointer for addrlen and check the length.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
// Get the socket address that was actually bound.
|
// Get the socket address that was actually bound.
|
||||||
libc::getsockname(
|
libc::getsockname(
|
||||||
|
@ -317,6 +322,7 @@ impl VsockSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_clone(&self) -> io::Result<Self> {
|
pub fn try_clone(&self) -> io::Result<Self> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let dup_fd = unsafe { libc::fcntl(self.fd, libc::F_DUPFD_CLOEXEC, 0) };
|
let dup_fd = unsafe { libc::fcntl(self.fd, libc::F_DUPFD_CLOEXEC, 0) };
|
||||||
if dup_fd < 0 {
|
if dup_fd < 0 {
|
||||||
|
@ -327,6 +333,7 @@ impl VsockSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_nonblocking(&mut self, nonblocking: bool) -> io::Result<()> {
|
pub fn set_nonblocking(&mut self, nonblocking: bool) -> io::Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the fd is valid and owned by this stream.
|
// Safe because the fd is valid and owned by this stream.
|
||||||
unsafe { set_nonblocking(self.fd, nonblocking) }
|
unsafe { set_nonblocking(self.fd, nonblocking) }
|
||||||
}
|
}
|
||||||
|
@ -348,6 +355,7 @@ impl AsRawFd for VsockSocket {
|
||||||
|
|
||||||
impl Drop for VsockSocket {
|
impl Drop for VsockSocket {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we are the only
|
// Safe because this doesn't modify any memory and we are the only
|
||||||
// owner of the file descriptor.
|
// owner of the file descriptor.
|
||||||
unsafe { libc::close(self.fd) };
|
unsafe { libc::close(self.fd) };
|
||||||
|
@ -382,6 +390,7 @@ impl VsockStream {
|
||||||
|
|
||||||
impl io::Read for VsockStream {
|
impl io::Read for VsockStream {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this will only modify the contents of |buf| and we check the return value.
|
// Safe because this will only modify the contents of |buf| and we check the return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::read(
|
libc::read(
|
||||||
|
@ -400,6 +409,7 @@ impl io::Read for VsockStream {
|
||||||
|
|
||||||
impl io::Write for VsockStream {
|
impl io::Write for VsockStream {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::write(
|
libc::write(
|
||||||
|
@ -459,8 +469,9 @@ impl VsockListener {
|
||||||
pub fn accept(&self) -> io::Result<(VsockStream, SocketAddr)> {
|
pub fn accept(&self) -> io::Result<(VsockStream, SocketAddr)> {
|
||||||
let mut svm: sockaddr_vm = Default::default();
|
let mut svm: sockaddr_vm = Default::default();
|
||||||
|
|
||||||
// Safe because this will only modify |svm| and we check the return value.
|
|
||||||
let mut socklen: socklen_t = size_of::<sockaddr_vm>() as socklen_t;
|
let mut socklen: socklen_t = size_of::<sockaddr_vm>() as socklen_t;
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because this will only modify |svm| and we check the return value.
|
||||||
let fd = unsafe {
|
let fd = unsafe {
|
||||||
libc::accept4(
|
libc::accept4(
|
||||||
self.sock.as_raw_fd(),
|
self.sock.as_raw_fd(),
|
||||||
|
|
|
@ -43,6 +43,7 @@ pub fn clone_descriptor(descriptor: &dyn AsRawDescriptor) -> Result<RawDescripto
|
||||||
/// `fd`. The cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file
|
/// `fd`. The cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file
|
||||||
/// descriptor flags with `fd`.
|
/// descriptor flags with `fd`.
|
||||||
fn clone_fd(fd: &dyn AsRawFd) -> Result<RawFd> {
|
fn clone_fd(fd: &dyn AsRawFd) -> Result<RawFd> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) };
|
let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -60,6 +61,7 @@ pub fn clear_descriptor_cloexec<A: AsRawDescriptor>(fd_owner: &A) -> Result<()>
|
||||||
/// Clears CLOEXEC flag on fd
|
/// Clears CLOEXEC flag on fd
|
||||||
fn clear_fd_cloexec<A: AsRawFd>(fd_owner: &A) -> Result<()> {
|
fn clear_fd_cloexec<A: AsRawFd>(fd_owner: &A) -> Result<()> {
|
||||||
let fd = fd_owner.as_raw_fd();
|
let fd = fd_owner.as_raw_fd();
|
||||||
|
// SAFETY:
|
||||||
// Safe because fd is read only.
|
// Safe because fd is read only.
|
||||||
let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
|
let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
|
||||||
if flags == -1 {
|
if flags == -1 {
|
||||||
|
@ -67,6 +69,7 @@ fn clear_fd_cloexec<A: AsRawFd>(fd_owner: &A) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let masked_flags = flags & !libc::FD_CLOEXEC;
|
let masked_flags = flags & !libc::FD_CLOEXEC;
|
||||||
|
// SAFETY:
|
||||||
// Safe because this has no side effect(s) on the current process.
|
// Safe because this has no side effect(s) on the current process.
|
||||||
if masked_flags != flags && unsafe { libc::fcntl(fd, libc::F_SETFD, masked_flags) } == -1 {
|
if masked_flags != flags && unsafe { libc::fcntl(fd, libc::F_SETFD, masked_flags) } == -1 {
|
||||||
errno_result()
|
errno_result()
|
||||||
|
@ -77,6 +80,8 @@ fn clear_fd_cloexec<A: AsRawFd>(fd_owner: &A) -> Result<()> {
|
||||||
|
|
||||||
impl Drop for SafeDescriptor {
|
impl Drop for SafeDescriptor {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because descriptor is valid.
|
||||||
let _ = unsafe { libc::close(self.descriptor) };
|
let _ = unsafe { libc::close(self.descriptor) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,6 +106,7 @@ impl SafeDescriptor {
|
||||||
/// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
|
/// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
|
||||||
/// share the same underlying count within the kernel.
|
/// share the same underlying count within the kernel.
|
||||||
pub fn try_clone(&self) -> Result<SafeDescriptor> {
|
pub fn try_clone(&self) -> Result<SafeDescriptor> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let descriptor = unsafe { libc::fcntl(self.descriptor, libc::F_DUPFD_CLOEXEC, 0) };
|
let descriptor = unsafe { libc::fcntl(self.descriptor, libc::F_DUPFD_CLOEXEC, 0) };
|
||||||
if descriptor < 0 {
|
if descriptor < 0 {
|
||||||
|
@ -113,6 +119,7 @@ impl SafeDescriptor {
|
||||||
|
|
||||||
impl From<SafeDescriptor> for File {
|
impl From<SafeDescriptor> for File {
|
||||||
fn from(s: SafeDescriptor) -> File {
|
fn from(s: SafeDescriptor) -> File {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we own the SafeDescriptor at this point.
|
// Safe because we own the SafeDescriptor at this point.
|
||||||
unsafe { File::from_raw_fd(s.into_raw_descriptor()) }
|
unsafe { File::from_raw_fd(s.into_raw_descriptor()) }
|
||||||
}
|
}
|
||||||
|
@ -120,6 +127,7 @@ impl From<SafeDescriptor> for File {
|
||||||
|
|
||||||
impl From<SafeDescriptor> for TcpListener {
|
impl From<SafeDescriptor> for TcpListener {
|
||||||
fn from(s: SafeDescriptor) -> Self {
|
fn from(s: SafeDescriptor) -> Self {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we own the SafeDescriptor at this point.
|
// Safe because we own the SafeDescriptor at this point.
|
||||||
unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
|
unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
|
||||||
}
|
}
|
||||||
|
@ -127,6 +135,7 @@ impl From<SafeDescriptor> for TcpListener {
|
||||||
|
|
||||||
impl From<SafeDescriptor> for TcpStream {
|
impl From<SafeDescriptor> for TcpStream {
|
||||||
fn from(s: SafeDescriptor) -> Self {
|
fn from(s: SafeDescriptor) -> Self {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we own the SafeDescriptor at this point.
|
// Safe because we own the SafeDescriptor at this point.
|
||||||
unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
|
unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
|
||||||
}
|
}
|
||||||
|
@ -134,6 +143,7 @@ impl From<SafeDescriptor> for TcpStream {
|
||||||
|
|
||||||
impl From<SafeDescriptor> for UnixStream {
|
impl From<SafeDescriptor> for UnixStream {
|
||||||
fn from(s: SafeDescriptor) -> Self {
|
fn from(s: SafeDescriptor) -> Self {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we own the SafeDescriptor at this point.
|
// Safe because we own the SafeDescriptor at this point.
|
||||||
unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
|
unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,24 @@ use crate::syscall;
|
||||||
///
|
///
|
||||||
/// Returns an error if the OS indicates the flags can't be retrieved.
|
/// Returns an error if the OS indicates the flags can't be retrieved.
|
||||||
fn get_fd_flags(fd: RawFd) -> Result<c_int> {
|
fn get_fd_flags(fd: RawFd) -> Result<c_int> {
|
||||||
// Safe because no third parameter is expected and we check the return result.
|
syscall!(
|
||||||
syscall!(unsafe { fcntl(fd, F_GETFL) })
|
// SAFETY:
|
||||||
|
// Safe because no third parameter is expected and we check the return result.
|
||||||
|
unsafe { fcntl(fd, F_GETFL) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the file flags set for the given `RawFD`.
|
/// Sets the file flags set for the given `RawFD`.
|
||||||
///
|
///
|
||||||
/// Returns an error if the OS indicates the flags can't be retrieved.
|
/// Returns an error if the OS indicates the flags can't be retrieved.
|
||||||
fn set_fd_flags(fd: RawFd, flags: c_int) -> Result<()> {
|
fn set_fd_flags(fd: RawFd, flags: c_int) -> Result<()> {
|
||||||
// Safe because we supply the third parameter and we check the return result.
|
syscall!(
|
||||||
// fcntlt is trusted not to modify the memory of the calling process.
|
// SAFETY:
|
||||||
syscall!(unsafe { fcntl(fd, F_SETFL, flags) }).map(|_| ())
|
// Safe because we supply the third parameter and we check the return result.
|
||||||
|
// fcntlt is trusted not to modify the memory of the calling process.
|
||||||
|
unsafe { fcntl(fd, F_SETFL, flags) }
|
||||||
|
)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs a logical OR of the given flags with the FD's flags, setting the given bits for the
|
/// Performs a logical OR of the given flags with the FD's flags, setting the given bits for the
|
||||||
|
|
|
@ -31,6 +31,7 @@ macro_rules! volatile_impl {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
impl FileReadWriteVolatile for $ty {
|
impl FileReadWriteVolatile for $ty {
|
||||||
fn read_volatile(&mut self, slice: $crate::VolatileSlice) -> std::io::Result<usize> {
|
fn read_volatile(&mut self, slice: $crate::VolatileSlice) -> std::io::Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
||||||
// to handle arbitrary memory for I/O.
|
// to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -58,6 +59,7 @@ macro_rules! volatile_impl {
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the buffers are accessed and the kernel is
|
// Safe because only bytes inside the buffers are accessed and the kernel is
|
||||||
// expected to handle arbitrary memory for I/O.
|
// expected to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -75,6 +77,7 @@ macro_rules! volatile_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_volatile(&mut self, slice: $crate::VolatileSlice) -> std::io::Result<usize> {
|
fn write_volatile(&mut self, slice: $crate::VolatileSlice) -> std::io::Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
||||||
// to handle arbitrary memory for I/O.
|
// to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -102,6 +105,7 @@ macro_rules! volatile_impl {
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the buffers are accessed and the kernel is
|
// Safe because only bytes inside the buffers are accessed and the kernel is
|
||||||
// expected to handle arbitrary memory for I/O.
|
// expected to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -130,6 +134,7 @@ macro_rules! volatile_at_impl {
|
||||||
slice: $crate::VolatileSlice,
|
slice: $crate::VolatileSlice,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
) -> std::io::Result<usize> {
|
) -> std::io::Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
||||||
// to handle arbitrary memory for I/O.
|
// to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -160,6 +165,7 @@ macro_rules! volatile_at_impl {
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the buffers are accessed and the kernel is
|
// Safe because only bytes inside the buffers are accessed and the kernel is
|
||||||
// expected to handle arbitrary memory for I/O.
|
// expected to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -182,6 +188,7 @@ macro_rules! volatile_at_impl {
|
||||||
slice: $crate::VolatileSlice,
|
slice: $crate::VolatileSlice,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
) -> std::io::Result<usize> {
|
) -> std::io::Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
||||||
// to handle arbitrary memory for I/O.
|
// to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -212,6 +219,7 @@ macro_rules! volatile_at_impl {
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the buffers are accessed and the kernel is
|
// Safe because only bytes inside the buffers are accessed and the kernel is
|
||||||
// expected to handle arbitrary memory for I/O.
|
// expected to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
|
|
@ -189,6 +189,7 @@ mod tests {
|
||||||
libc::__error()
|
libc::__error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe {
|
unsafe {
|
||||||
*errno_location() = e;
|
*errno_location() = e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,9 +81,11 @@ pub(in crate::sys) fn socket(
|
||||||
sock_type: c_int,
|
sock_type: c_int,
|
||||||
protocol: c_int,
|
protocol: c_int,
|
||||||
) -> io::Result<SafeDescriptor> {
|
) -> io::Result<SafeDescriptor> {
|
||||||
|
// SAFETY:
|
||||||
// Safe socket initialization since we handle the returned error.
|
// Safe socket initialization since we handle the returned error.
|
||||||
match unsafe { libc::socket(domain, sock_type, protocol) } {
|
match unsafe { libc::socket(domain, sock_type, protocol) } {
|
||||||
-1 => Err(io::Error::last_os_error()),
|
-1 => Err(io::Error::last_os_error()),
|
||||||
|
// SAFETY:
|
||||||
// Safe because we own the file descriptor.
|
// Safe because we own the file descriptor.
|
||||||
fd => Ok(unsafe { SafeDescriptor::from_raw_descriptor(fd) }),
|
fd => Ok(unsafe { SafeDescriptor::from_raw_descriptor(fd) }),
|
||||||
}
|
}
|
||||||
|
@ -95,16 +97,20 @@ pub(in crate::sys) fn socketpair(
|
||||||
protocol: c_int,
|
protocol: c_int,
|
||||||
) -> io::Result<(SafeDescriptor, SafeDescriptor)> {
|
) -> io::Result<(SafeDescriptor, SafeDescriptor)> {
|
||||||
let mut fds = [0, 0];
|
let mut fds = [0, 0];
|
||||||
|
// SAFETY:
|
||||||
// Safe because we give enough space to store all the fds and we check the return value.
|
// Safe because we give enough space to store all the fds and we check the return value.
|
||||||
match unsafe { libc::socketpair(domain, sock_type, protocol, fds.as_mut_ptr()) } {
|
match unsafe { libc::socketpair(domain, sock_type, protocol, fds.as_mut_ptr()) } {
|
||||||
-1 => Err(io::Error::last_os_error()),
|
-1 => Err(io::Error::last_os_error()),
|
||||||
// Safe because we own the file descriptors.
|
_ => Ok(
|
||||||
_ => Ok(unsafe {
|
// SAFETY:
|
||||||
(
|
// Safe because we own the file descriptors.
|
||||||
SafeDescriptor::from_raw_descriptor(fds[0]),
|
unsafe {
|
||||||
SafeDescriptor::from_raw_descriptor(fds[1]),
|
(
|
||||||
)
|
SafeDescriptor::from_raw_descriptor(fds[0]),
|
||||||
}),
|
SafeDescriptor::from_raw_descriptor(fds[1]),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +136,7 @@ impl TcpSocket {
|
||||||
let ret = match sockaddr {
|
let ret = match sockaddr {
|
||||||
SocketAddr::V4(a) => {
|
SocketAddr::V4(a) => {
|
||||||
let sin = sockaddrv4_to_lib_c(&a);
|
let sin = sockaddrv4_to_lib_c(&a);
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::bind(
|
libc::bind(
|
||||||
|
@ -141,6 +148,7 @@ impl TcpSocket {
|
||||||
}
|
}
|
||||||
SocketAddr::V6(a) => {
|
SocketAddr::V6(a) => {
|
||||||
let sin6 = sockaddrv6_to_lib_c(&a);
|
let sin6 = sockaddrv6_to_lib_c(&a);
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::bind(
|
libc::bind(
|
||||||
|
@ -169,6 +177,7 @@ impl TcpSocket {
|
||||||
let ret = match sockaddr {
|
let ret = match sockaddr {
|
||||||
SocketAddr::V4(a) => {
|
SocketAddr::V4(a) => {
|
||||||
let sin = sockaddrv4_to_lib_c(&a);
|
let sin = sockaddrv4_to_lib_c(&a);
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::connect(
|
libc::connect(
|
||||||
|
@ -180,6 +189,7 @@ impl TcpSocket {
|
||||||
}
|
}
|
||||||
SocketAddr::V6(a) => {
|
SocketAddr::V6(a) => {
|
||||||
let sin6 = sockaddrv6_to_lib_c(&a);
|
let sin6 = sockaddrv6_to_lib_c(&a);
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::connect(
|
libc::connect(
|
||||||
|
@ -200,6 +210,7 @@ impl TcpSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen(self) -> io::Result<TcpListener> {
|
pub fn listen(self) -> io::Result<TcpListener> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe { libc::listen(self.as_raw_descriptor(), 1) };
|
let ret = unsafe { libc::listen(self.as_raw_descriptor(), 1) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -216,8 +227,9 @@ impl TcpSocket {
|
||||||
InetVersion::V4 => {
|
InetVersion::V4 => {
|
||||||
let mut sin = sockaddrv4_to_lib_c(&SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0));
|
let mut sin = sockaddrv4_to_lib_c(&SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0));
|
||||||
|
|
||||||
// Safe because we give a valid pointer for addrlen and check the length.
|
|
||||||
let mut addrlen = size_of::<sockaddr_in>() as socklen_t;
|
let mut addrlen = size_of::<sockaddr_in>() as socklen_t;
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we give a valid pointer for addrlen and check the length.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
// Get the socket address that was actually bound.
|
// Get the socket address that was actually bound.
|
||||||
libc::getsockname(
|
libc::getsockname(
|
||||||
|
@ -244,8 +256,9 @@ impl TcpSocket {
|
||||||
0,
|
0,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Safe because we give a valid pointer for addrlen and check the length.
|
|
||||||
let mut addrlen = size_of::<sockaddr_in6>() as socklen_t;
|
let mut addrlen = size_of::<sockaddr_in6>() as socklen_t;
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we give a valid pointer for addrlen and check the length.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
// Get the socket address that was actually bound.
|
// Get the socket address that was actually bound.
|
||||||
libc::getsockname(
|
libc::getsockname(
|
||||||
|
@ -279,6 +292,7 @@ pub(in crate::sys) fn sun_path_offset() -> usize {
|
||||||
// Prefer 0 to null() so that we do not need to subtract from the `sub_path` pointer.
|
// Prefer 0 to null() so that we do not need to subtract from the `sub_path` pointer.
|
||||||
#[allow(clippy::zero_ptr)]
|
#[allow(clippy::zero_ptr)]
|
||||||
let addr = 0 as *const libc::sockaddr_un;
|
let addr = 0 as *const libc::sockaddr_un;
|
||||||
|
// SAFETY:
|
||||||
// Safe because we only use the dereference to create a pointer to the desired field in
|
// Safe because we only use the dereference to create a pointer to the desired field in
|
||||||
// calculating the offset.
|
// calculating the offset.
|
||||||
unsafe { &(*addr).sun_path as *const _ as usize }
|
unsafe { &(*addr).sun_path as *const _ as usize }
|
||||||
|
@ -302,6 +316,7 @@ impl UnixSeqpacket {
|
||||||
pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||||
let descriptor = socket(libc::AF_UNIX, libc::SOCK_SEQPACKET, 0)?;
|
let descriptor = socket(libc::AF_UNIX, libc::SOCK_SEQPACKET, 0)?;
|
||||||
let (addr, len) = sockaddr_un(path.as_ref())?;
|
let (addr, len) = sockaddr_un(path.as_ref())?;
|
||||||
|
// SAFETY:
|
||||||
// Safe connect since we handle the error and use the right length generated from
|
// Safe connect since we handle the error and use the right length generated from
|
||||||
// `sockaddr_un`.
|
// `sockaddr_un`.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -325,6 +340,8 @@ impl UnixSeqpacket {
|
||||||
/// Gets the number of bytes that can be read from this socket without blocking.
|
/// Gets the number of bytes that can be read from this socket without blocking.
|
||||||
pub fn get_readable_bytes(&self) -> io::Result<usize> {
|
pub fn get_readable_bytes(&self) -> io::Result<usize> {
|
||||||
let mut byte_count = 0i32;
|
let mut byte_count = 0i32;
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because self has valid raw descriptor and return value are checked.
|
||||||
let ret = unsafe { libc::ioctl(self.as_raw_descriptor(), libc::FIONREAD, &mut byte_count) };
|
let ret = unsafe { libc::ioctl(self.as_raw_descriptor(), libc::FIONREAD, &mut byte_count) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
Err(io::Error::last_os_error())
|
Err(io::Error::last_os_error())
|
||||||
|
@ -345,6 +362,7 @@ impl UnixSeqpacket {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
let buf = &mut 0 as *mut _ as *mut _;
|
let buf = &mut 0 as *mut _ as *mut _;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// This form of recvfrom doesn't modify any data because all null pointers are used. We only
|
// This form of recvfrom doesn't modify any data because all null pointers are used. We only
|
||||||
// use the return value and check for errors on an FD owned by this structure.
|
// use the return value and check for errors on an FD owned by this structure.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -375,6 +393,7 @@ impl UnixSeqpacket {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns error when `libc::write` failed.
|
/// Returns error when `libc::write` failed.
|
||||||
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
|
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe since we make sure the input `count` == `buf.len()` and handle the returned error.
|
// Safe since we make sure the input `count` == `buf.len()` and handle the returned error.
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = libc::write(
|
let ret = libc::write(
|
||||||
|
@ -401,6 +420,7 @@ impl UnixSeqpacket {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns error when `libc::read` failed.
|
/// Returns error when `libc::read` failed.
|
||||||
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
|
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe since we make sure the input `count` == `buf.len()` and handle the returned error.
|
// Safe since we make sure the input `count` == `buf.len()` and handle the returned error.
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = libc::read(
|
let ret = libc::read(
|
||||||
|
@ -466,6 +486,7 @@ impl UnixSeqpacket {
|
||||||
tv_usec: 0,
|
tv_usec: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
// SAFETY:
|
||||||
// Safe because we own the fd, and the length of the pointer's data is the same as the
|
// Safe because we own the fd, and the length of the pointer's data is the same as the
|
||||||
// passed in length parameter. The level argument is valid, the kind is assumed to be valid,
|
// passed in length parameter. The level argument is valid, the kind is assumed to be valid,
|
||||||
// and the return value is checked.
|
// and the return value is checked.
|
||||||
|
@ -498,6 +519,7 @@ impl UnixSeqpacket {
|
||||||
/// Sets the blocking mode for this socket.
|
/// Sets the blocking mode for this socket.
|
||||||
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
||||||
let mut nonblocking = nonblocking as libc::c_int;
|
let mut nonblocking = nonblocking as libc::c_int;
|
||||||
|
// SAFETY:
|
||||||
// Safe because the return value is checked, and this ioctl call sets the nonblocking mode
|
// Safe because the return value is checked, and this ioctl call sets the nonblocking mode
|
||||||
// and does not continue holding the file descriptor after the call.
|
// and does not continue holding the file descriptor after the call.
|
||||||
let ret = unsafe { libc::ioctl(self.as_raw_descriptor(), libc::FIONBIO, &mut nonblocking) };
|
let ret = unsafe { libc::ioctl(self.as_raw_descriptor(), libc::FIONBIO, &mut nonblocking) };
|
||||||
|
@ -575,6 +597,7 @@ impl UnixSeqpacketListener {
|
||||||
.expect("fd should be an integer");
|
.expect("fd should be an integer");
|
||||||
let mut result: c_int = 0;
|
let mut result: c_int = 0;
|
||||||
let mut result_len = size_of::<c_int>() as libc::socklen_t;
|
let mut result_len = size_of::<c_int>() as libc::socklen_t;
|
||||||
|
// SAFETY: Safe because fd and other args are valid and the return value is checked.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
libc::getsockopt(
|
libc::getsockopt(
|
||||||
fd,
|
fd,
|
||||||
|
@ -593,6 +616,7 @@ impl UnixSeqpacketListener {
|
||||||
"specified descriptor is not a listening socket",
|
"specified descriptor is not a listening socket",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
// SAFETY:
|
||||||
// Safe because we validated the socket file descriptor.
|
// Safe because we validated the socket file descriptor.
|
||||||
let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
|
let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
|
||||||
return Ok(UnixSeqpacketListener {
|
return Ok(UnixSeqpacketListener {
|
||||||
|
@ -604,6 +628,7 @@ impl UnixSeqpacketListener {
|
||||||
let descriptor = socket(libc::AF_UNIX, libc::SOCK_SEQPACKET, 0)?;
|
let descriptor = socket(libc::AF_UNIX, libc::SOCK_SEQPACKET, 0)?;
|
||||||
let (addr, len) = sockaddr_un(path.as_ref())?;
|
let (addr, len) = sockaddr_un(path.as_ref())?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe connect since we handle the error and use the right length generated from
|
// Safe connect since we handle the error and use the right length generated from
|
||||||
// `sockaddr_un`.
|
// `sockaddr_un`.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -638,6 +663,7 @@ impl UnixSeqpacketListener {
|
||||||
let elapsed = Instant::now().saturating_duration_since(start);
|
let elapsed = Instant::now().saturating_duration_since(start);
|
||||||
let remaining = timeout.checked_sub(elapsed).unwrap_or(Duration::ZERO);
|
let remaining = timeout.checked_sub(elapsed).unwrap_or(Duration::ZERO);
|
||||||
let cur_timeout_ms = i32::try_from(remaining.as_millis()).unwrap_or(i32::MAX);
|
let cur_timeout_ms = i32::try_from(remaining.as_millis()).unwrap_or(i32::MAX);
|
||||||
|
// SAFETY:
|
||||||
// Safe because we give a valid pointer to a list (of 1) FD and we check
|
// Safe because we give a valid pointer to a list (of 1) FD and we check
|
||||||
// the return value.
|
// the return value.
|
||||||
match unsafe { libc::poll(&mut fds, 1, cur_timeout_ms) }.cmp(&0) {
|
match unsafe { libc::poll(&mut fds, 1, cur_timeout_ms) }.cmp(&0) {
|
||||||
|
@ -665,6 +691,7 @@ impl UnixSeqpacketListener {
|
||||||
- &addr.sun_family as *const _ as usize)
|
- &addr.sun_family as *const _ as usize)
|
||||||
as libc::socklen_t;
|
as libc::socklen_t;
|
||||||
let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t;
|
let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t;
|
||||||
|
// SAFETY:
|
||||||
// Safe because the length given matches the length of the data of the given pointer, and we
|
// Safe because the length given matches the length of the data of the given pointer, and we
|
||||||
// check the return value.
|
// check the return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -699,6 +726,7 @@ impl UnixSeqpacketListener {
|
||||||
/// Sets the blocking mode for this socket.
|
/// Sets the blocking mode for this socket.
|
||||||
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
||||||
let mut nonblocking = nonblocking as libc::c_int;
|
let mut nonblocking = nonblocking as libc::c_int;
|
||||||
|
// SAFETY:
|
||||||
// Safe because the return value is checked, and this ioctl call sets the nonblocking mode
|
// Safe because the return value is checked, and this ioctl call sets the nonblocking mode
|
||||||
// and does not continue holding the file descriptor after the call.
|
// and does not continue holding the file descriptor after the call.
|
||||||
let ret = unsafe { libc::ioctl(self.as_raw_descriptor(), libc::FIONBIO, &mut nonblocking) };
|
let ret = unsafe { libc::ioctl(self.as_raw_descriptor(), libc::FIONBIO, &mut nonblocking) };
|
||||||
|
|
|
@ -100,6 +100,7 @@ impl CmsgBuffer {
|
||||||
} else {
|
} else {
|
||||||
CmsgBuffer::Heap(
|
CmsgBuffer::Heap(
|
||||||
vec![
|
vec![
|
||||||
|
// SAFETY:
|
||||||
// Safe because cmsghdr only contains primitive types for
|
// Safe because cmsghdr only contains primitive types for
|
||||||
// which zero initialization is valid.
|
// which zero initialization is valid.
|
||||||
unsafe { MaybeUninit::<cmsghdr>::zeroed().assume_init() };
|
unsafe { MaybeUninit::<cmsghdr>::zeroed().assume_init() };
|
||||||
|
@ -125,6 +126,7 @@ fn raw_sendmsg(fd: RawFd, iovec: &[iovec], out_fds: &[RawFd]) -> io::Result<usiz
|
||||||
let cmsg_capacity = CMSG_SPACE(size_of_val(out_fds));
|
let cmsg_capacity = CMSG_SPACE(size_of_val(out_fds));
|
||||||
let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
|
let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// msghdr on musl has private __pad1 and __pad2 fields that cannot be initialized.
|
// msghdr on musl has private __pad1 and __pad2 fields that cannot be initialized.
|
||||||
// Safe because msghdr only contains primitive types for which zero
|
// Safe because msghdr only contains primitive types for which zero
|
||||||
// initialization is valid.
|
// initialization is valid.
|
||||||
|
@ -133,6 +135,7 @@ fn raw_sendmsg(fd: RawFd, iovec: &[iovec], out_fds: &[RawFd]) -> io::Result<usiz
|
||||||
msg.msg_iovlen = iovec.len().try_into().unwrap();
|
msg.msg_iovlen = iovec.len().try_into().unwrap();
|
||||||
|
|
||||||
if !out_fds.is_empty() {
|
if !out_fds.is_empty() {
|
||||||
|
// SAFETY:
|
||||||
// msghdr on musl has an extra __pad1 field, initialize the whole struct to zero.
|
// msghdr on musl has an extra __pad1 field, initialize the whole struct to zero.
|
||||||
// Safe because cmsghdr only contains primitive types for which zero
|
// Safe because cmsghdr only contains primitive types for which zero
|
||||||
// initialization is valid.
|
// initialization is valid.
|
||||||
|
@ -140,9 +143,12 @@ fn raw_sendmsg(fd: RawFd, iovec: &[iovec], out_fds: &[RawFd]) -> io::Result<usiz
|
||||||
cmsg.cmsg_len = CMSG_LEN(size_of_val(out_fds)).try_into().unwrap();
|
cmsg.cmsg_len = CMSG_LEN(size_of_val(out_fds)).try_into().unwrap();
|
||||||
cmsg.cmsg_level = SOL_SOCKET;
|
cmsg.cmsg_level = SOL_SOCKET;
|
||||||
cmsg.cmsg_type = SCM_RIGHTS;
|
cmsg.cmsg_type = SCM_RIGHTS;
|
||||||
|
// SAFETY: See call specific comments within unsafe block.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
// SAFETY:
|
||||||
// Safe because cmsg_buffer was allocated to be large enough to contain cmsghdr.
|
// Safe because cmsg_buffer was allocated to be large enough to contain cmsghdr.
|
||||||
write_unaligned(cmsg_buffer.as_mut_ptr(), cmsg);
|
write_unaligned(cmsg_buffer.as_mut_ptr(), cmsg);
|
||||||
|
// SAFETY:
|
||||||
// Safe because the cmsg_buffer was allocated to be large enough to hold out_fds.len()
|
// Safe because the cmsg_buffer was allocated to be large enough to hold out_fds.len()
|
||||||
// file descriptors.
|
// file descriptors.
|
||||||
copy_nonoverlapping(
|
copy_nonoverlapping(
|
||||||
|
@ -156,6 +162,7 @@ fn raw_sendmsg(fd: RawFd, iovec: &[iovec], out_fds: &[RawFd]) -> io::Result<usiz
|
||||||
msg.msg_controllen = cmsg_capacity.try_into().unwrap();
|
msg.msg_controllen = cmsg_capacity.try_into().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the msghdr was properly constructed from valid (or null) pointers of the
|
// Safe because the msghdr was properly constructed from valid (or null) pointers of the
|
||||||
// indicated length and we check the return value.
|
// indicated length and we check the return value.
|
||||||
let write_count = unsafe { sendmsg(fd, &msg, 0) };
|
let write_count = unsafe { sendmsg(fd, &msg, 0) };
|
||||||
|
@ -178,6 +185,7 @@ fn raw_recvmsg(
|
||||||
let cmsg_capacity = CMSG_SPACE(max_fds * size_of::<RawFd>());
|
let cmsg_capacity = CMSG_SPACE(max_fds * size_of::<RawFd>());
|
||||||
let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
|
let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// msghdr on musl has private __pad1 and __pad2 fields that cannot be initialized.
|
// msghdr on musl has private __pad1 and __pad2 fields that cannot be initialized.
|
||||||
// Safe because msghdr only contains primitive types for which zero
|
// Safe because msghdr only contains primitive types for which zero
|
||||||
// initialization is valid.
|
// initialization is valid.
|
||||||
|
@ -190,6 +198,7 @@ fn raw_recvmsg(
|
||||||
msg.msg_controllen = cmsg_capacity.try_into().unwrap();
|
msg.msg_controllen = cmsg_capacity.try_into().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the msghdr was properly constructed from valid (or null) pointers of the
|
// Safe because the msghdr was properly constructed from valid (or null) pointers of the
|
||||||
// indicated length and we check the return value.
|
// indicated length and we check the return value.
|
||||||
let total_read = unsafe { recvmsg(fd, &mut msg, 0) };
|
let total_read = unsafe { recvmsg(fd, &mut msg, 0) };
|
||||||
|
@ -205,6 +214,7 @@ fn raw_recvmsg(
|
||||||
let mut cmsg_ptr = msg.msg_control as *mut cmsghdr;
|
let mut cmsg_ptr = msg.msg_control as *mut cmsghdr;
|
||||||
let mut in_fds: Vec<SafeDescriptor> = Vec::with_capacity(max_fds);
|
let mut in_fds: Vec<SafeDescriptor> = Vec::with_capacity(max_fds);
|
||||||
while !cmsg_ptr.is_null() {
|
while !cmsg_ptr.is_null() {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we checked that cmsg_ptr was non-null, and the loop is constructed such that
|
// Safe because we checked that cmsg_ptr was non-null, and the loop is constructed such that
|
||||||
// that only happens when there is at least sizeof(cmsghdr) space after the pointer to read.
|
// that only happens when there is at least sizeof(cmsghdr) space after the pointer to read.
|
||||||
let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() };
|
let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() };
|
||||||
|
@ -378,6 +388,7 @@ pub unsafe trait AsIobuf: Sized {
|
||||||
fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec];
|
fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because there are no other mutable references to the memory described by `IoSlice` and it is
|
// Safe because there are no other mutable references to the memory described by `IoSlice` and it is
|
||||||
// guaranteed to be ABI-compatible with `iovec`.
|
// guaranteed to be ABI-compatible with `iovec`.
|
||||||
unsafe impl<'a> AsIobuf for IoSlice<'a> {
|
unsafe impl<'a> AsIobuf for IoSlice<'a> {
|
||||||
|
@ -389,16 +400,19 @@ unsafe impl<'a> AsIobuf for IoSlice<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
|
fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `IoSlice` is guaranteed to be ABI-compatible with `iovec`.
|
// Safe because `IoSlice` is guaranteed to be ABI-compatible with `iovec`.
|
||||||
unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
|
unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
|
fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `IoSlice` is guaranteed to be ABI-compatible with `iovec`.
|
// Safe because `IoSlice` is guaranteed to be ABI-compatible with `iovec`.
|
||||||
unsafe { slice::from_raw_parts_mut(bufs.as_mut_ptr() as *mut iovec, bufs.len()) }
|
unsafe { slice::from_raw_parts_mut(bufs.as_mut_ptr() as *mut iovec, bufs.len()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because there are no other references to the memory described by `IoSliceMut` and it is
|
// Safe because there are no other references to the memory described by `IoSliceMut` and it is
|
||||||
// guaranteed to be ABI-compatible with `iovec`.
|
// guaranteed to be ABI-compatible with `iovec`.
|
||||||
unsafe impl<'a> AsIobuf for IoSliceMut<'a> {
|
unsafe impl<'a> AsIobuf for IoSliceMut<'a> {
|
||||||
|
@ -410,16 +424,19 @@ unsafe impl<'a> AsIobuf for IoSliceMut<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
|
fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `IoSliceMut` is guaranteed to be ABI-compatible with `iovec`.
|
// Safe because `IoSliceMut` is guaranteed to be ABI-compatible with `iovec`.
|
||||||
unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
|
unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
|
fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `IoSliceMut` is guaranteed to be ABI-compatible with `iovec`.
|
// Safe because `IoSliceMut` is guaranteed to be ABI-compatible with `iovec`.
|
||||||
unsafe { slice::from_raw_parts_mut(bufs.as_mut_ptr() as *mut iovec, bufs.len()) }
|
unsafe { slice::from_raw_parts_mut(bufs.as_mut_ptr() as *mut iovec, bufs.len()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because volatile slices are only ever accessed with other volatile interfaces and the
|
// Safe because volatile slices are only ever accessed with other volatile interfaces and the
|
||||||
// pointer and size are guaranteed to be accurate.
|
// pointer and size are guaranteed to be accurate.
|
||||||
unsafe impl<'a> AsIobuf for VolatileSlice<'a> {
|
unsafe impl<'a> AsIobuf for VolatileSlice<'a> {
|
||||||
|
@ -455,6 +472,7 @@ mod tests {
|
||||||
($len:literal) => {
|
($len:literal) => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
CMSG_SPACE(size_of::<[RawFd; $len]>()) as libc::c_uint,
|
CMSG_SPACE(size_of::<[RawFd; $len]>()) as libc::c_uint,
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe { libc::CMSG_SPACE(size_of::<[RawFd; $len]>() as libc::c_uint) }
|
unsafe { libc::CMSG_SPACE(size_of::<[RawFd; $len]>() as libc::c_uint) }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -530,6 +548,7 @@ mod tests {
|
||||||
assert_ne!(file.as_raw_fd(), s2.as_raw_descriptor());
|
assert_ne!(file.as_raw_fd(), s2.as_raw_descriptor());
|
||||||
assert_ne!(file.as_raw_fd(), evt.as_raw_descriptor());
|
assert_ne!(file.as_raw_fd(), evt.as_raw_descriptor());
|
||||||
|
|
||||||
|
// SAFETY: trivially safe
|
||||||
file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
|
file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
|
||||||
.expect("failed to write to sent fd");
|
.expect("failed to write to sent fd");
|
||||||
|
|
||||||
|
@ -564,6 +583,7 @@ mod tests {
|
||||||
|
|
||||||
let mut file = File::from(files.swap_remove(0));
|
let mut file = File::from(files.swap_remove(0));
|
||||||
|
|
||||||
|
// SAFETY: trivially safe
|
||||||
file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
|
file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
|
||||||
.expect("failed to write to sent fd");
|
.expect("failed to write to sent fd");
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,7 @@ impl StreamChannel {
|
||||||
// (see sys::decode_error_kind) on Windows, so we preserve this behavior on POSIX even
|
// (see sys::decode_error_kind) on Windows, so we preserve this behavior on POSIX even
|
||||||
// though one could argue ErrorKind::UnexpectedEof is a closer match to the true error.
|
// though one could argue ErrorKind::UnexpectedEof is a closer match to the true error.
|
||||||
SocketType::Message(sock) => {
|
SocketType::Message(sock) => {
|
||||||
|
// SAFETY:
|
||||||
// Safe because buf is valid, we pass buf's size to recv to bound the return
|
// Safe because buf is valid, we pass buf's size to recv to bound the return
|
||||||
// length, and we check the return code.
|
// length, and we check the return code.
|
||||||
let retval = unsafe {
|
let retval = unsafe {
|
||||||
|
|
|
@ -12,6 +12,7 @@ use crate::Result;
|
||||||
/// Safe wrapper for `sysconf(_SC_IOV_MAX)`.
|
/// Safe wrapper for `sysconf(_SC_IOV_MAX)`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn iov_max() -> usize {
|
pub fn iov_max() -> usize {
|
||||||
|
// SAFETY:
|
||||||
// Trivially safe
|
// Trivially safe
|
||||||
unsafe { sysconf(_SC_IOV_MAX) as usize }
|
unsafe { sysconf(_SC_IOV_MAX) as usize }
|
||||||
}
|
}
|
||||||
|
@ -19,6 +20,7 @@ pub fn iov_max() -> usize {
|
||||||
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
|
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn pagesize() -> usize {
|
pub fn pagesize() -> usize {
|
||||||
|
// SAFETY:
|
||||||
// Trivially safe
|
// Trivially safe
|
||||||
unsafe { sysconf(_SC_PAGESIZE) as usize }
|
unsafe { sysconf(_SC_PAGESIZE) as usize }
|
||||||
}
|
}
|
||||||
|
@ -26,6 +28,7 @@ pub fn pagesize() -> usize {
|
||||||
/// Returns the number of online logical cores on the system.
|
/// Returns the number of online logical cores on the system.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn number_of_logical_cores() -> Result<usize> {
|
pub fn number_of_logical_cores() -> Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass a flag for this call and the host supports this system call
|
// Safe because we pass a flag for this call and the host supports this system call
|
||||||
Ok(unsafe { sysconf(_SC_NPROCESSORS_CONF) } as usize)
|
Ok(unsafe { sysconf(_SC_NPROCESSORS_CONF) } as usize)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ impl Console {
|
||||||
impl Read for Console {
|
impl Read for Console {
|
||||||
fn read(&mut self, out: &mut [u8]) -> Result<usize> {
|
fn read(&mut self, out: &mut [u8]) -> Result<usize> {
|
||||||
let mut num_of_bytes_read: u32 = 0;
|
let mut num_of_bytes_read: u32 = 0;
|
||||||
|
// SAFETY:
|
||||||
// Safe because `out` is guarenteed to be a valid mutable array
|
// Safe because `out` is guarenteed to be a valid mutable array
|
||||||
// and `num_of_bytes_read` is a valid u32.
|
// and `num_of_bytes_read` is a valid u32.
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
|
|
|
@ -46,6 +46,7 @@ impl PartialEq for SafeDescriptor {
|
||||||
|
|
||||||
impl Drop for SafeDescriptor {
|
impl Drop for SafeDescriptor {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe { CloseHandle(self.descriptor) };
|
unsafe { CloseHandle(self.descriptor) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,11 +62,13 @@ static mut KERNELBASE_LIBRARY: MaybeUninit<HMODULE> = MaybeUninit::uninit();
|
||||||
|
|
||||||
fn compare_object_handles(first: RawHandle, second: RawHandle) -> bool {
|
fn compare_object_handles(first: RawHandle, second: RawHandle) -> bool {
|
||||||
KERNELBASE_INIT.call_once(|| {
|
KERNELBASE_INIT.call_once(|| {
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe {
|
unsafe {
|
||||||
*KERNELBASE_LIBRARY.as_mut_ptr() =
|
*KERNELBASE_LIBRARY.as_mut_ptr() =
|
||||||
libloaderapi::LoadLibraryW(win32_wide_string("Kernelbase").as_ptr());
|
libloaderapi::LoadLibraryW(win32_wide_string("Kernelbase").as_ptr());
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
// SAFETY: the return value is checked.
|
||||||
let handle = unsafe { KERNELBASE_LIBRARY.assume_init() };
|
let handle = unsafe { KERNELBASE_LIBRARY.assume_init() };
|
||||||
if handle.is_null() {
|
if handle.is_null() {
|
||||||
return first == second;
|
return first == second;
|
||||||
|
@ -73,11 +76,13 @@ fn compare_object_handles(first: RawHandle, second: RawHandle) -> bool {
|
||||||
|
|
||||||
let addr = CString::new("CompareObjectHandles").unwrap();
|
let addr = CString::new("CompareObjectHandles").unwrap();
|
||||||
let addr_ptr = addr.as_ptr();
|
let addr_ptr = addr.as_ptr();
|
||||||
|
// SAFETY: the return value is checked.
|
||||||
let symbol = unsafe { libloaderapi::GetProcAddress(handle, addr_ptr) };
|
let symbol = unsafe { libloaderapi::GetProcAddress(handle, addr_ptr) };
|
||||||
if symbol.is_null() {
|
if symbol.is_null() {
|
||||||
return first == second;
|
return first == second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: trivially safe
|
||||||
let func = unsafe {
|
let func = unsafe {
|
||||||
std::mem::transmute::<
|
std::mem::transmute::<
|
||||||
*mut winapi::shared::minwindef::__some_function,
|
*mut winapi::shared::minwindef::__some_function,
|
||||||
|
@ -102,6 +107,7 @@ impl SafeDescriptor {
|
||||||
/// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
|
/// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
|
||||||
/// share the same underlying count within the kernel.
|
/// share the same underlying count within the kernel.
|
||||||
pub fn try_clone(&self) -> Result<SafeDescriptor> {
|
pub fn try_clone(&self) -> Result<SafeDescriptor> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `duplicate_handle` will return a valid handle, or at the very least error
|
// Safe because `duplicate_handle` will return a valid handle, or at the very least error
|
||||||
// out.
|
// out.
|
||||||
Ok(unsafe {
|
Ok(unsafe {
|
||||||
|
@ -110,15 +116,19 @@ impl SafeDescriptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// On Windows, RawHandles are represented by raw pointers but are not used as such in
|
// On Windows, RawHandles are represented by raw pointers but are not used as such in
|
||||||
// rust code, and are therefore safe to send between threads.
|
// rust code, and are therefore safe to send between threads.
|
||||||
unsafe impl Send for SafeDescriptor {}
|
unsafe impl Send for SafeDescriptor {}
|
||||||
|
// SAFETY: See comments for impl Send
|
||||||
unsafe impl Sync for SafeDescriptor {}
|
unsafe impl Sync for SafeDescriptor {}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// On Windows, RawHandles are represented by raw pointers but are opaque to the
|
// On Windows, RawHandles are represented by raw pointers but are opaque to the
|
||||||
// userspace and cannot be derefenced by rust code, and are therefore safe to
|
// userspace and cannot be derefenced by rust code, and are therefore safe to
|
||||||
// send between threads.
|
// send between threads.
|
||||||
unsafe impl Send for Descriptor {}
|
unsafe impl Send for Descriptor {}
|
||||||
|
// SAFETY: See comments for impl Send
|
||||||
unsafe impl Sync for Descriptor {}
|
unsafe impl Sync for Descriptor {}
|
||||||
|
|
||||||
macro_rules! AsRawDescriptor {
|
macro_rules! AsRawDescriptor {
|
||||||
|
@ -134,6 +144,7 @@ macro_rules! AsRawDescriptor {
|
||||||
macro_rules! FromRawDescriptor {
|
macro_rules! FromRawDescriptor {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
impl FromRawDescriptor for $name {
|
impl FromRawDescriptor for $name {
|
||||||
|
// SAFETY: It is caller's responsibility to ensure that the descriptor is valid.
|
||||||
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
|
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
|
||||||
return $name::from_raw_handle(descriptor);
|
return $name::from_raw_handle(descriptor);
|
||||||
}
|
}
|
||||||
|
@ -171,6 +182,7 @@ fn clone_equality() {
|
||||||
use crate::Event;
|
use crate::Event;
|
||||||
|
|
||||||
let evt = Event::new().unwrap();
|
let evt = Event::new().unwrap();
|
||||||
|
// SAFETY: Given evt is created above and is valid.
|
||||||
let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(evt.into_raw_descriptor()) };
|
let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(evt.into_raw_descriptor()) };
|
||||||
|
|
||||||
assert_eq!(descriptor, descriptor);
|
assert_eq!(descriptor, descriptor);
|
||||||
|
@ -181,6 +193,7 @@ fn clone_equality() {
|
||||||
);
|
);
|
||||||
|
|
||||||
let evt2 = Event::new().unwrap();
|
let evt2 = Event::new().unwrap();
|
||||||
|
// SAFETY: Given evt2 is created above and is valid.
|
||||||
let another = unsafe { SafeDescriptor::from_raw_descriptor(evt2.into_raw_descriptor()) };
|
let another = unsafe { SafeDescriptor::from_raw_descriptor(evt2.into_raw_descriptor()) };
|
||||||
|
|
||||||
assert_ne!(descriptor, another);
|
assert_ne!(descriptor, another);
|
||||||
|
|
|
@ -73,6 +73,7 @@ impl EventExt for Event {
|
||||||
|
|
||||||
impl PlatformEvent {
|
impl PlatformEvent {
|
||||||
pub fn new_with_manual_reset(manual_reset: bool) -> Result<PlatformEvent> {
|
pub fn new_with_manual_reset(manual_reset: bool) -> Result<PlatformEvent> {
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let handle = unsafe {
|
let handle = unsafe {
|
||||||
CreateEventA(
|
CreateEventA(
|
||||||
SecurityAttributes::new_with_security_descriptor(
|
SecurityAttributes::new_with_security_descriptor(
|
||||||
|
@ -89,12 +90,15 @@ impl PlatformEvent {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
Ok(PlatformEvent {
|
Ok(PlatformEvent {
|
||||||
event_handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
|
event_handle:
|
||||||
|
// SAFETY: Safe because the descriptor is valid.
|
||||||
|
unsafe { SafeDescriptor::from_raw_descriptor(handle) },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_event_with_name(name: &str) -> Result<PlatformEvent> {
|
pub fn create_event_with_name(name: &str) -> Result<PlatformEvent> {
|
||||||
let event_str = CString::new(String::from(name)).unwrap();
|
let event_str = CString::new(String::from(name)).unwrap();
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let handle = unsafe {
|
let handle = unsafe {
|
||||||
CreateEventA(
|
CreateEventA(
|
||||||
SecurityAttributes::new_with_security_descriptor(
|
SecurityAttributes::new_with_security_descriptor(
|
||||||
|
@ -111,7 +115,9 @@ impl PlatformEvent {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
Ok(PlatformEvent {
|
Ok(PlatformEvent {
|
||||||
event_handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
|
event_handle:
|
||||||
|
// SAFETY: Safe because the descriptor is valid.
|
||||||
|
unsafe { SafeDescriptor::from_raw_descriptor(handle) },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,17 +128,21 @@ impl PlatformEvent {
|
||||||
|
|
||||||
pub fn open(name: &str) -> Result<PlatformEvent> {
|
pub fn open(name: &str) -> Result<PlatformEvent> {
|
||||||
let event_str = CString::new(String::from(name)).unwrap();
|
let event_str = CString::new(String::from(name)).unwrap();
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let handle = unsafe { OpenEventA(EVENT_MODIFY_STATE, FALSE, event_str.as_ptr()) };
|
let handle = unsafe { OpenEventA(EVENT_MODIFY_STATE, FALSE, event_str.as_ptr()) };
|
||||||
if handle.is_null() {
|
if handle.is_null() {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
Ok(PlatformEvent {
|
Ok(PlatformEvent {
|
||||||
event_handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
|
event_handle:
|
||||||
|
// SAFETY: Safe because the descriptor is valid.
|
||||||
|
unsafe { SafeDescriptor::from_raw_descriptor(handle) },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See `Event::signal`.
|
/// See `Event::signal`.
|
||||||
pub fn signal(&self) -> Result<()> {
|
pub fn signal(&self) -> Result<()> {
|
||||||
|
// SAFETY: Safe because the descriptor is valid.
|
||||||
let event_result = unsafe { SetEvent(self.event_handle.as_raw_descriptor()) };
|
let event_result = unsafe { SetEvent(self.event_handle.as_raw_descriptor()) };
|
||||||
if event_result == 0 {
|
if event_result == 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
|
@ -141,6 +151,7 @@ impl PlatformEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&self) -> Result<()> {
|
pub fn reset(&self) -> Result<()> {
|
||||||
|
// SAFETY: Safe because the descriptor is valid.
|
||||||
let res = unsafe { ResetEvent(self.event_handle.as_raw_descriptor()) };
|
let res = unsafe { ResetEvent(self.event_handle.as_raw_descriptor()) };
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
errno_result()
|
errno_result()
|
||||||
|
@ -156,6 +167,7 @@ impl PlatformEvent {
|
||||||
None => INFINITE,
|
None => INFINITE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass an event object handle owned by this PlatformEvent.
|
// Safe because we pass an event object handle owned by this PlatformEvent.
|
||||||
let wait_result = match unsafe {
|
let wait_result = match unsafe {
|
||||||
WaitForSingleObject(self.event_handle.as_raw_descriptor(), milliseconds)
|
WaitForSingleObject(self.event_handle.as_raw_descriptor(), milliseconds)
|
||||||
|
@ -189,6 +201,7 @@ impl PlatformEvent {
|
||||||
|
|
||||||
pub fn try_clone(&self) -> Result<PlatformEvent> {
|
pub fn try_clone(&self) -> Result<PlatformEvent> {
|
||||||
let mut event_clone: HANDLE = MaybeUninit::uninit().as_mut_ptr();
|
let mut event_clone: HANDLE = MaybeUninit::uninit().as_mut_ptr();
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let duplicate_result = unsafe {
|
let duplicate_result = unsafe {
|
||||||
DuplicateHandle(
|
DuplicateHandle(
|
||||||
GetCurrentProcess(),
|
GetCurrentProcess(),
|
||||||
|
@ -203,7 +216,10 @@ impl PlatformEvent {
|
||||||
if duplicate_result == 0 {
|
if duplicate_result == 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
Ok(unsafe { PlatformEvent::from_raw_descriptor(event_clone) })
|
Ok(
|
||||||
|
// SAFETY: Safe because the descriptor is valid.
|
||||||
|
unsafe { PlatformEvent::from_raw_descriptor(event_clone) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +230,7 @@ impl AsRawDescriptor for PlatformEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromRawDescriptor for PlatformEvent {
|
impl FromRawDescriptor for PlatformEvent {
|
||||||
|
// SAFETY: Safe because the descriptor is expected to be valid.
|
||||||
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
|
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
|
||||||
PlatformEvent {
|
PlatformEvent {
|
||||||
event_handle: SafeDescriptor::from_raw_descriptor(descriptor),
|
event_handle: SafeDescriptor::from_raw_descriptor(descriptor),
|
||||||
|
@ -245,10 +262,12 @@ impl From<SafeDescriptor> for PlatformEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Safety:
|
||||||
// PlatformEvent is safe for send & Sync despite containing a raw handle to its
|
// PlatformEvent is safe for send & Sync despite containing a raw handle to its
|
||||||
// file mapping object. As long as the instance to PlatformEvent stays alive, this
|
// file mapping object. As long as the instance to PlatformEvent stays alive, this
|
||||||
// pointer will be a valid handle.
|
// pointer will be a valid handle.
|
||||||
unsafe impl Send for PlatformEvent {}
|
unsafe impl Send for PlatformEvent {}
|
||||||
|
// Safety: See comments for impl Send
|
||||||
unsafe impl Sync for PlatformEvent {}
|
unsafe impl Sync for PlatformEvent {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -277,10 +296,12 @@ mod tests {
|
||||||
evt.signal().unwrap();
|
evt.signal().unwrap();
|
||||||
|
|
||||||
// Wait for the notification.
|
// Wait for the notification.
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), INFINITE) };
|
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), INFINITE) };
|
||||||
assert_eq!(result, WAIT_OBJECT_0);
|
assert_eq!(result, WAIT_OBJECT_0);
|
||||||
|
|
||||||
// The notification should have reset since we already received it.
|
// The notification should have reset since we already received it.
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
|
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
|
||||||
assert_eq!(result, WAIT_TIMEOUT);
|
assert_eq!(result, WAIT_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
@ -291,15 +312,18 @@ mod tests {
|
||||||
evt.signal().unwrap();
|
evt.signal().unwrap();
|
||||||
|
|
||||||
// Wait for the notification.
|
// Wait for the notification.
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), INFINITE) };
|
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), INFINITE) };
|
||||||
assert_eq!(result, WAIT_OBJECT_0);
|
assert_eq!(result, WAIT_OBJECT_0);
|
||||||
|
|
||||||
// The notification should still be active because read wasn't called.
|
// The notification should still be active because read wasn't called.
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
|
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
|
||||||
assert_eq!(result, WAIT_OBJECT_0);
|
assert_eq!(result, WAIT_OBJECT_0);
|
||||||
|
|
||||||
// Read and ensure the notification has cleared.
|
// Read and ensure the notification has cleared.
|
||||||
evt.wait().expect("Failed to read event.");
|
evt.wait().expect("Failed to read event.");
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
|
let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
|
||||||
assert_eq!(result, WAIT_TIMEOUT);
|
assert_eq!(result, WAIT_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,10 @@ use crate::WriteZeroesAt;
|
||||||
|
|
||||||
impl FileReadWriteVolatile for File {
|
impl FileReadWriteVolatile for File {
|
||||||
fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
||||||
|
let mut bytes = 0;
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
||||||
// to handle arbitrary memory for I/O.
|
// to handle arbitrary memory for I/O.
|
||||||
let mut bytes = 0;
|
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
winapi::um::fileapi::ReadFile(
|
winapi::um::fileapi::ReadFile(
|
||||||
self.as_raw_descriptor(),
|
self.as_raw_descriptor(),
|
||||||
|
@ -55,9 +56,10 @@ impl FileReadWriteVolatile for File {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
||||||
|
let mut bytes = 0;
|
||||||
|
// SAFETY:
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
||||||
// to handle arbitrary memory for I/O.
|
// to handle arbitrary memory for I/O.
|
||||||
let mut bytes = 0;
|
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
winapi::um::fileapi::WriteFile(
|
winapi::um::fileapi::WriteFile(
|
||||||
self.as_raw_descriptor(),
|
self.as_raw_descriptor(),
|
||||||
|
@ -101,10 +103,11 @@ impl FileReadWriteAtVolatile for File {
|
||||||
// The unix implementation uses pread, which doesn't modify the file
|
// The unix implementation uses pread, which doesn't modify the file
|
||||||
// pointer. Windows doesn't have an option for that, unfortunately.
|
// pointer. Windows doesn't have an option for that, unfortunately.
|
||||||
|
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
||||||
// to handle arbitrary memory for I/O.
|
|
||||||
let mut bytes = 0;
|
let mut bytes = 0;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
||||||
|
// to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
let mut overlapped: winapi::um::minwinbase::OVERLAPPED = std::mem::zeroed();
|
let mut overlapped: winapi::um::minwinbase::OVERLAPPED = std::mem::zeroed();
|
||||||
overlapped.u.s_mut().Offset = offset as u32;
|
overlapped.u.s_mut().Offset = offset as u32;
|
||||||
|
@ -149,10 +152,11 @@ impl FileReadWriteAtVolatile for File {
|
||||||
// The unix implementation uses pwrite, which doesn't modify the file
|
// The unix implementation uses pwrite, which doesn't modify the file
|
||||||
// pointer. Windows doesn't have an option for that, unfortunately.
|
// pointer. Windows doesn't have an option for that, unfortunately.
|
||||||
|
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
||||||
// to handle arbitrary memory for I/O.
|
|
||||||
let mut bytes = 0;
|
let mut bytes = 0;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
||||||
|
// to handle arbitrary memory for I/O.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
let mut overlapped: winapi::um::minwinbase::OVERLAPPED = std::mem::zeroed();
|
let mut overlapped: winapi::um::minwinbase::OVERLAPPED = std::mem::zeroed();
|
||||||
overlapped.u.s_mut().Offset = offset as u32;
|
overlapped.u.s_mut().Offset = offset as u32;
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub fn open_file_or_duplicate<P: AsRef<Path>>(path: P, options: &OpenOptions) ->
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// handle *must* be File. We accept all AsRawDescriptors for convenience.
|
/// handle *must* be File. We accept all AsRawDescriptors for convenience.
|
||||||
pub fn set_sparse_file<T: AsRawDescriptor>(handle: &T) -> io::Result<()> {
|
pub fn set_sparse_file<T: AsRawDescriptor>(handle: &T) -> io::Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we check the return value and handle is guaranteed to be a
|
// Safe because we check the return value and handle is guaranteed to be a
|
||||||
// valid file handle by the caller.
|
// valid file handle by the caller.
|
||||||
let result = unsafe {
|
let result = unsafe {
|
||||||
|
@ -60,7 +61,7 @@ struct FileAllocatedRangeBuffer {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Within this scope it is not possible to use LARGE_INTEGER as something else.
|
/// Within this scope it is not possible to use LARGE_INTEGER as something else.
|
||||||
fn large_integer_as_u64(lint: &LARGE_INTEGER) -> u64 {
|
fn large_integer_as_u64(lint: &LARGE_INTEGER) -> u64 {
|
||||||
// # Safety
|
// SAFETY:
|
||||||
// Safe because we use LARGE_INTEGER only as i64 or as u64 within this scope.
|
// Safe because we use LARGE_INTEGER only as i64 or as u64 within this scope.
|
||||||
unsafe { *lint.QuadPart() as u64 }
|
unsafe { *lint.QuadPart() as u64 }
|
||||||
}
|
}
|
||||||
|
@ -90,7 +91,7 @@ pub fn get_allocated_ranges<T: AsRawDescriptor>(descriptor: &T) -> Result<Vec<Ra
|
||||||
let mut ranges = vec![];
|
let mut ranges = vec![];
|
||||||
let mut file_size = *LargeInteger::new(0);
|
let mut file_size = *LargeInteger::new(0);
|
||||||
|
|
||||||
// # Safety
|
// SAFETY:
|
||||||
// Safe because we check return value.
|
// Safe because we check return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
let failed = GetFileSizeEx(descriptor.as_raw_descriptor(), &mut file_size);
|
let failed = GetFileSizeEx(descriptor.as_raw_descriptor(), &mut file_size);
|
||||||
|
@ -114,7 +115,7 @@ pub fn get_allocated_ranges<T: AsRawDescriptor>(descriptor: &T) -> Result<Vec<Ra
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut bytes_ret: u32 = 0;
|
let mut bytes_ret: u32 = 0;
|
||||||
// # Safety
|
// SAFETY:
|
||||||
// Safe because we return error on failure and all references have
|
// Safe because we return error on failure and all references have
|
||||||
// bounded lifetime.
|
// bounded lifetime.
|
||||||
// If the `alloc_ranges` buffer is smaller than the actual allocated ranges,
|
// If the `alloc_ranges` buffer is smaller than the actual allocated ranges,
|
||||||
|
|
|
@ -12,6 +12,7 @@ use crate::Result;
|
||||||
/// only when the emulator is in the foreground, and will persist only until the next user
|
/// only when the emulator is in the foreground, and will persist only until the next user
|
||||||
/// interaction with the window
|
/// interaction with the window
|
||||||
pub fn give_foregrounding_permission(process_id: DWORD) -> Result<()> {
|
pub fn give_foregrounding_permission(process_id: DWORD) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this API does not modify memory, and process_id remains in scope for
|
// Safe because this API does not modify memory, and process_id remains in scope for
|
||||||
// the duration of the call.
|
// the duration of the call.
|
||||||
match unsafe { AllowSetForegroundWindow(process_id) } {
|
match unsafe { AllowSetForegroundWindow(process_id) } {
|
||||||
|
|
|
@ -372,6 +372,7 @@ mod tests {
|
||||||
f.sync_all().expect("Failed to sync all.");
|
f.sync_all().expect("Failed to sync all.");
|
||||||
|
|
||||||
// read the compression status
|
// read the compression status
|
||||||
|
// SAFETY: safe because return value is checked.
|
||||||
let ecode = unsafe {
|
let ecode = unsafe {
|
||||||
super::super::ioctl::ioctl_with_mut_ref(&f, FSCTL_GET_COMPRESSION, &mut compressed)
|
super::super::ioctl::ioctl_with_mut_ref(&f, FSCTL_GET_COMPRESSION, &mut compressed)
|
||||||
};
|
};
|
||||||
|
@ -396,6 +397,7 @@ mod tests {
|
||||||
// https://github.com/rust-lang/rust/blob/master/src/libstd/sys/windows/fs.rs#L260
|
// https://github.com/rust-lang/rust/blob/master/src/libstd/sys/windows/fs.rs#L260
|
||||||
// For now I'm just going to leave this test as-is.
|
// For now I'm just going to leave this test as-is.
|
||||||
//
|
//
|
||||||
|
// SAFETY: safe because return value is checked.
|
||||||
let f = unsafe {
|
let f = unsafe {
|
||||||
File::from_raw_handle(CreateFileW(
|
File::from_raw_handle(CreateFileW(
|
||||||
to_u16s(file_path).unwrap().as_ptr(),
|
to_u16s(file_path).unwrap().as_ptr(),
|
||||||
|
@ -410,6 +412,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
let ecode =
|
let ecode =
|
||||||
|
// SAFETY: safe because return value is checked.
|
||||||
unsafe { super::super::ioctl::ioctl_with_ref(&f, FSCTL_SET_COMPRESSION, &compressed) };
|
unsafe { super::super::ioctl::ioctl_with_ref(&f, FSCTL_SET_COMPRESSION, &compressed) };
|
||||||
|
|
||||||
assert_eq!(ecode, 0);
|
assert_eq!(ecode, 0);
|
||||||
|
@ -418,6 +421,7 @@ mod tests {
|
||||||
// is writing anything to the compressed pointer.
|
// is writing anything to the compressed pointer.
|
||||||
compressed = 0;
|
compressed = 0;
|
||||||
|
|
||||||
|
// SAFETY: safe because return value is checked.
|
||||||
let ecode = unsafe {
|
let ecode = unsafe {
|
||||||
super::super::ioctl::ioctl_with_mut_ref(&f, FSCTL_GET_COMPRESSION, &mut compressed)
|
super::super::ioctl::ioctl_with_mut_ref(&f, FSCTL_GET_COMPRESSION, &mut compressed)
|
||||||
};
|
};
|
||||||
|
@ -462,6 +466,7 @@ mod tests {
|
||||||
// https://github.com/rust-lang/rust/blob/master/src/libstd/sys/windows/fs.rs#L260
|
// https://github.com/rust-lang/rust/blob/master/src/libstd/sys/windows/fs.rs#L260
|
||||||
// For now I'm just going to leave this test as-is.
|
// For now I'm just going to leave this test as-is.
|
||||||
//
|
//
|
||||||
|
// SAFETY: safe because return value is checked.
|
||||||
let f = unsafe {
|
let f = unsafe {
|
||||||
File::from_raw_handle(CreateFileW(
|
File::from_raw_handle(CreateFileW(
|
||||||
to_u16s(file_path).unwrap().as_ptr(),
|
to_u16s(file_path).unwrap().as_ptr(),
|
||||||
|
@ -477,6 +482,7 @@ mod tests {
|
||||||
|
|
||||||
// now we call ioctl_with_val, which isn't particularly any more helpful than
|
// now we call ioctl_with_val, which isn't particularly any more helpful than
|
||||||
// ioctl_with_ref except for the cases where the input is only a word long
|
// ioctl_with_ref except for the cases where the input is only a word long
|
||||||
|
// SAFETY: safe because return value is checked.
|
||||||
let ecode = unsafe {
|
let ecode = unsafe {
|
||||||
super::super::ioctl::ioctl_with_val(&f, FSCTL_SET_COMPRESSION, compressed.into())
|
super::super::ioctl::ioctl_with_val(&f, FSCTL_SET_COMPRESSION, compressed.into())
|
||||||
};
|
};
|
||||||
|
@ -487,6 +493,7 @@ mod tests {
|
||||||
// is writing anything to the compressed pointer.
|
// is writing anything to the compressed pointer.
|
||||||
compressed = 0;
|
compressed = 0;
|
||||||
|
|
||||||
|
// SAFETY: safe because return value is checked.
|
||||||
let ecode = unsafe {
|
let ecode = unsafe {
|
||||||
super::super::ioctl::ioctl_with_mut_ref(&f, FSCTL_GET_COMPRESSION, &mut compressed)
|
super::super::ioctl::ioctl_with_mut_ref(&f, FSCTL_GET_COMPRESSION, &mut compressed)
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,6 +41,7 @@ impl dyn MappedRegion {
|
||||||
pub fn msync(&self, offset: usize, size: usize) -> Result<()> {
|
pub fn msync(&self, offset: usize, size: usize) -> Result<()> {
|
||||||
validate_includes_range(self.size(), offset, size)?;
|
validate_includes_range(self.size(), offset, size)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the MemoryMapping/MemoryMappingArena interface ensures our pointer and size
|
// Safe because the MemoryMapping/MemoryMappingArena interface ensures our pointer and size
|
||||||
// are correct, and we've validated that `offset`..`offset+size` is in the range owned by
|
// are correct, and we've validated that `offset`..`offset+size` is in the range owned by
|
||||||
// this `MappedRegion`.
|
// this `MappedRegion`.
|
||||||
|
@ -68,11 +69,13 @@ pub struct MemoryMapping {
|
||||||
pub(crate) size: usize,
|
pub(crate) size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Send and Sync aren't automatically inherited for the raw address pointer.
|
// Send and Sync aren't automatically inherited for the raw address pointer.
|
||||||
// Accessing that pointer is only done through the stateless interface which
|
// Accessing that pointer is only done through the stateless interface which
|
||||||
// allows the object to be shared by multiple threads without a decrease in
|
// allows the object to be shared by multiple threads without a decrease in
|
||||||
// safety.
|
// safety.
|
||||||
unsafe impl Send for MemoryMapping {}
|
unsafe impl Send for MemoryMapping {}
|
||||||
|
// SAFETY: See comments for impl Send
|
||||||
unsafe impl Sync for MemoryMapping {}
|
unsafe impl Sync for MemoryMapping {}
|
||||||
|
|
||||||
impl MemoryMapping {
|
impl MemoryMapping {
|
||||||
|
@ -123,6 +126,9 @@ impl MemoryMapping {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because the pointer and size point to a memory range owned by this MemoryMapping that won't
|
||||||
|
// be unmapped until it's Dropped.
|
||||||
unsafe impl MappedRegion for MemoryMapping {
|
unsafe impl MappedRegion for MemoryMapping {
|
||||||
fn as_ptr(&self) -> *mut u8 {
|
fn as_ptr(&self) -> *mut u8 {
|
||||||
self.addr as *mut u8
|
self.addr as *mut u8
|
||||||
|
@ -171,6 +177,7 @@ impl<'a> MemoryMappingBuilder<'a> {
|
||||||
// handle for it first. That handle is then provided to Self::wrap, which
|
// handle for it first. That handle is then provided to Self::wrap, which
|
||||||
// performs the actual mmap (creating a mapped view).
|
// performs the actual mmap (creating a mapped view).
|
||||||
//
|
//
|
||||||
|
// SAFETY:
|
||||||
// Safe because self.descriptor is guaranteed to be a valid handle.
|
// Safe because self.descriptor is guaranteed to be a valid handle.
|
||||||
let mapping_handle = unsafe {
|
let mapping_handle = unsafe {
|
||||||
create_file_mapping(
|
create_file_mapping(
|
||||||
|
@ -182,6 +189,7 @@ impl<'a> MemoryMappingBuilder<'a> {
|
||||||
}
|
}
|
||||||
.map_err(Error::StdSyscallFailed)?;
|
.map_err(Error::StdSyscallFailed)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// The above comment block is why the SafeDescriptor wrap is safe.
|
// The above comment block is why the SafeDescriptor wrap is safe.
|
||||||
Some(unsafe { SafeDescriptor::from_raw_descriptor(mapping_handle) })
|
Some(unsafe { SafeDescriptor::from_raw_descriptor(mapping_handle) })
|
||||||
} else {
|
} else {
|
||||||
|
@ -219,6 +227,7 @@ impl<'a> MemoryMappingBuilder<'a> {
|
||||||
file_descriptor: Option<&'a dyn AsRawDescriptor>,
|
file_descriptor: Option<&'a dyn AsRawDescriptor>,
|
||||||
) -> Result<CrateMemoryMapping> {
|
) -> Result<CrateMemoryMapping> {
|
||||||
let file_descriptor = match file_descriptor {
|
let file_descriptor = match file_descriptor {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `duplicate_handle` will return a handle or at least error out.
|
// Safe because `duplicate_handle` will return a handle or at least error out.
|
||||||
Some(descriptor) => unsafe {
|
Some(descriptor) => unsafe {
|
||||||
Some(SafeDescriptor::from_raw_descriptor(
|
Some(SafeDescriptor::from_raw_descriptor(
|
||||||
|
@ -280,6 +289,7 @@ mod tests {
|
||||||
let shm = SharedMemory::new("test", 1028).unwrap();
|
let shm = SharedMemory::new("test", 1028).unwrap();
|
||||||
let m = to_crate_mmap(MemoryMapping::from_descriptor(&shm, 5).unwrap());
|
let m = to_crate_mmap(MemoryMapping::from_descriptor(&shm, 5).unwrap());
|
||||||
let s = m.get_slice(2, 3).unwrap();
|
let s = m.get_slice(2, 3).unwrap();
|
||||||
|
// SAFETY: trivially safe
|
||||||
assert_eq!(s.as_ptr(), unsafe { m.as_ptr().offset(2) });
|
assert_eq!(s.as_ptr(), unsafe { m.as_ptr().offset(2) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ impl MemoryMapping {
|
||||||
/// * `size` - Size of memory region in bytes.
|
/// * `size` - Size of memory region in bytes.
|
||||||
/// * `prot` - Protection (e.g. readable/writable) of the memory region.
|
/// * `prot` - Protection (e.g. readable/writable) of the memory region.
|
||||||
pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> {
|
pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we are creating an anonymous mapping in a place not already used by
|
// This is safe because we are creating an anonymous mapping in a place not already used by
|
||||||
// any other area in this process.
|
// any other area in this process.
|
||||||
unsafe { MemoryMapping::try_mmap(None, size, prot.into(), None) }
|
unsafe { MemoryMapping::try_mmap(None, size, prot.into(), None) }
|
||||||
|
@ -64,6 +65,7 @@ impl MemoryMapping {
|
||||||
file_handle: RawDescriptor,
|
file_handle: RawDescriptor,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> Result<MemoryMapping> {
|
) -> Result<MemoryMapping> {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we are creating an anonymous mapping in a place not already used by
|
// This is safe because we are creating an anonymous mapping in a place not already used by
|
||||||
// any other area in this process.
|
// any other area in this process.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -89,6 +91,7 @@ impl MemoryMapping {
|
||||||
offset: u64,
|
offset: u64,
|
||||||
prot: Protection,
|
prot: Protection,
|
||||||
) -> Result<MemoryMapping> {
|
) -> Result<MemoryMapping> {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we are creating an anonymous mapping in a place not already used by
|
// This is safe because we are creating an anonymous mapping in a place not already used by
|
||||||
// any other area in this process.
|
// any other area in this process.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -198,6 +201,7 @@ impl MemoryMapping {
|
||||||
/// Calls FlushViewOfFile on the mapped memory range, ensuring all changes that would
|
/// Calls FlushViewOfFile on the mapped memory range, ensuring all changes that would
|
||||||
/// be written to disk are written immediately
|
/// be written to disk are written immediately
|
||||||
pub fn msync(&self) -> Result<()> {
|
pub fn msync(&self) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because self can only be created as a successful memory mapping
|
// Safe because self can only be created as a successful memory mapping
|
||||||
unsafe {
|
unsafe {
|
||||||
if FlushViewOfFile(self.addr, self.size) == 0 {
|
if FlushViewOfFile(self.addr, self.size) == 0 {
|
||||||
|
@ -210,6 +214,7 @@ impl MemoryMapping {
|
||||||
|
|
||||||
impl Drop for MemoryMapping {
|
impl Drop for MemoryMapping {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because we MapViewOfFile the area at addr ourselves, and nobody
|
// This is safe because we MapViewOfFile the area at addr ourselves, and nobody
|
||||||
// else is holding a reference to it.
|
// else is holding a reference to it.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -240,6 +245,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn map_invalid_fd() {
|
fn map_invalid_fd() {
|
||||||
|
// SAFETY: trivially safe to create an invalid File.
|
||||||
let descriptor = unsafe { std::fs::File::from_raw_descriptor(ptr::null_mut()) };
|
let descriptor = unsafe { std::fs::File::from_raw_descriptor(ptr::null_mut()) };
|
||||||
let res = MemoryMappingBuilder::new(1024)
|
let res = MemoryMappingBuilder::new(1024)
|
||||||
.from_file(&descriptor)
|
.from_file(&descriptor)
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub struct MultiProcessMutex {
|
||||||
|
|
||||||
impl MultiProcessMutex {
|
impl MultiProcessMutex {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
// Trivially safe (no memory passed, error checked).
|
// SAFETY: Trivially safe (no memory passed, error checked).
|
||||||
//
|
//
|
||||||
// Note that we intentionally make this handle uninheritable by default via the mutex attrs.
|
// Note that we intentionally make this handle uninheritable by default via the mutex attrs.
|
||||||
let lock_handle = unsafe {
|
let lock_handle = unsafe {
|
||||||
|
@ -44,6 +44,7 @@ impl MultiProcessMutex {
|
||||||
Err(Error::last())
|
Err(Error::last())
|
||||||
} else {
|
} else {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the handle is valid & we own it exclusively.
|
// Safe because the handle is valid & we own it exclusively.
|
||||||
lock: unsafe { SafeDescriptor::from_raw_descriptor(lock_handle) },
|
lock: unsafe { SafeDescriptor::from_raw_descriptor(lock_handle) },
|
||||||
})
|
})
|
||||||
|
@ -63,6 +64,7 @@ impl MultiProcessMutex {
|
||||||
/// Tries to lock the mutex, returning a RAII guard similar to std::sync::Mutex if we obtained
|
/// Tries to lock the mutex, returning a RAII guard similar to std::sync::Mutex if we obtained
|
||||||
/// the lock within the timeout.
|
/// the lock within the timeout.
|
||||||
pub fn try_lock(&self, timeout_ms: u32) -> Option<MultiProcessMutexGuard> {
|
pub fn try_lock(&self, timeout_ms: u32) -> Option<MultiProcessMutexGuard> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the mutex handle is guaranteed to exist.
|
// Safe because the mutex handle is guaranteed to exist.
|
||||||
match unsafe { WaitForSingleObject(self.lock.as_raw_descriptor(), timeout_ms) } {
|
match unsafe { WaitForSingleObject(self.lock.as_raw_descriptor(), timeout_ms) } {
|
||||||
WAIT_OBJECT_0 => Some(MultiProcessMutexGuard { lock: &self.lock }),
|
WAIT_OBJECT_0 => Some(MultiProcessMutexGuard { lock: &self.lock }),
|
||||||
|
@ -93,6 +95,7 @@ pub struct MultiProcessMutexGuard<'a> {
|
||||||
|
|
||||||
impl<'a> Drop for MultiProcessMutexGuard<'a> {
|
impl<'a> Drop for MultiProcessMutexGuard<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY: We own the descriptor and is expected to be valid.
|
||||||
if unsafe { ReleaseMutex(self.lock.as_raw_descriptor()) } == 0 {
|
if unsafe { ReleaseMutex(self.lock.as_raw_descriptor()) } == 0 {
|
||||||
panic!("Failed to unlock mutex: {:?}.", Error::last())
|
panic!("Failed to unlock mutex: {:?}.", Error::last())
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,6 +137,7 @@ impl OverlappedWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because all of the contained fields may be safely sent to another thread.
|
// Safe because all of the contained fields may be safely sent to another thread.
|
||||||
unsafe impl Send for OverlappedWrapper {}
|
unsafe impl Send for OverlappedWrapper {}
|
||||||
|
|
||||||
|
@ -242,6 +243,7 @@ impl From<&BlockingMode> for DWORD {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the handle state for a named pipe in a rust friendly way.
|
/// Sets the handle state for a named pipe in a rust friendly way.
|
||||||
|
/// SAFETY:
|
||||||
/// This is safe if the pipe handle is open.
|
/// This is safe if the pipe handle is open.
|
||||||
unsafe fn set_named_pipe_handle_state(
|
unsafe fn set_named_pipe_handle_state(
|
||||||
pipe_handle: RawDescriptor,
|
pipe_handle: RawDescriptor,
|
||||||
|
@ -377,9 +379,10 @@ pub fn create_server_pipe(
|
||||||
|
|
||||||
// This sets flags so there will be an error if >1 instance (server end)
|
// This sets flags so there will be an error if >1 instance (server end)
|
||||||
// of this pipe name is opened because we expect exactly one.
|
// of this pipe name is opened because we expect exactly one.
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because security attributes are valid, pipe_name is valid C string,
|
||||||
|
// and we're checking the return code
|
||||||
let server_handle = unsafe {
|
let server_handle = unsafe {
|
||||||
// Safe because security attributes are valid, pipe_name is valid C string,
|
|
||||||
// and we're checking the return code
|
|
||||||
CreateNamedPipeA(
|
CreateNamedPipeA(
|
||||||
c_pipe_name.as_ptr(),
|
c_pipe_name.as_ptr(),
|
||||||
/* dwOpenMode= */
|
/* dwOpenMode= */
|
||||||
|
@ -405,6 +408,7 @@ pub fn create_server_pipe(
|
||||||
if server_handle == INVALID_HANDLE_VALUE {
|
if server_handle == INVALID_HANDLE_VALUE {
|
||||||
Err(io::Error::last_os_error())
|
Err(io::Error::last_os_error())
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY: Safe because server_handle is valid.
|
||||||
unsafe {
|
unsafe {
|
||||||
Ok(PipeConnection {
|
Ok(PipeConnection {
|
||||||
handle: SafeDescriptor::from_raw_descriptor(server_handle),
|
handle: SafeDescriptor::from_raw_descriptor(server_handle),
|
||||||
|
@ -448,12 +452,14 @@ pub fn create_client_pipe(
|
||||||
|
|
||||||
let mut client_mode = framing_mode.to_readmode() | DWORD::from(blocking_mode);
|
let mut client_mode = framing_mode.to_readmode() | DWORD::from(blocking_mode);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because client_handle's open() call did not return an error.
|
// Safe because client_handle's open() call did not return an error.
|
||||||
unsafe {
|
unsafe {
|
||||||
set_named_pipe_handle_state(client_handle, &mut client_mode)?;
|
set_named_pipe_handle_state(client_handle, &mut client_mode)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(PipeConnection {
|
Ok(PipeConnection {
|
||||||
|
// SAFETY:
|
||||||
// Safe because client_handle is valid
|
// Safe because client_handle is valid
|
||||||
handle: unsafe { SafeDescriptor::from_raw_descriptor(client_handle) },
|
handle: unsafe { SafeDescriptor::from_raw_descriptor(client_handle) },
|
||||||
framing_mode: *framing_mode,
|
framing_mode: *framing_mode,
|
||||||
|
@ -604,6 +610,7 @@ impl PipeConnection {
|
||||||
overlapped_wrapper: &mut OverlappedWrapper,
|
overlapped_wrapper: &mut OverlappedWrapper,
|
||||||
exit_event: &Event,
|
exit_event: &Event,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we are providing a valid buffer slice and also providing a valid
|
// Safe because we are providing a valid buffer slice and also providing a valid
|
||||||
// overlapped struct.
|
// overlapped struct.
|
||||||
match unsafe { self.read_overlapped(buf, overlapped_wrapper) } {
|
match unsafe { self.read_overlapped(buf, overlapped_wrapper) } {
|
||||||
|
@ -665,6 +672,7 @@ impl PipeConnection {
|
||||||
pub fn get_available_byte_count(&self) -> io::Result<u32> {
|
pub fn get_available_byte_count(&self) -> io::Result<u32> {
|
||||||
let mut total_bytes_avail: DWORD = 0;
|
let mut total_bytes_avail: DWORD = 0;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the underlying pipe handle is guaranteed to be open, and the output values
|
// Safe because the underlying pipe handle is guaranteed to be open, and the output values
|
||||||
// live at valid memory locations.
|
// live at valid memory locations.
|
||||||
fail_if_zero!(unsafe {
|
fail_if_zero!(unsafe {
|
||||||
|
@ -736,6 +744,7 @@ impl PipeConnection {
|
||||||
buf: &[T],
|
buf: &[T],
|
||||||
overlapped: Option<&mut OVERLAPPED>,
|
overlapped: Option<&mut OVERLAPPED>,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because buf points to memory valid until the write completes and we pass a valid
|
// Safe because buf points to memory valid until the write completes and we pass a valid
|
||||||
// length for that memory.
|
// length for that memory.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -753,6 +762,7 @@ impl PipeConnection {
|
||||||
let mut client_mode = DWORD::from(blocking_mode) | self.framing_mode.to_readmode();
|
let mut client_mode = DWORD::from(blocking_mode) | self.framing_mode.to_readmode();
|
||||||
self.blocking_mode = *blocking_mode;
|
self.blocking_mode = *blocking_mode;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the pipe has not been closed (it is managed by this object).
|
// Safe because the pipe has not been closed (it is managed by this object).
|
||||||
unsafe { set_named_pipe_handle_state(self.handle.as_raw_descriptor(), &mut client_mode) }
|
unsafe { set_named_pipe_handle_state(self.handle.as_raw_descriptor(), &mut client_mode) }
|
||||||
}
|
}
|
||||||
|
@ -827,6 +837,7 @@ impl PipeConnection {
|
||||||
overlapped_wrapper: &mut OverlappedWrapper,
|
overlapped_wrapper: &mut OverlappedWrapper,
|
||||||
should_block: bool,
|
should_block: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the handle is valid and we're checking the return
|
// Safe because the handle is valid and we're checking the return
|
||||||
// code according to the documentation
|
// code according to the documentation
|
||||||
//
|
//
|
||||||
|
@ -917,6 +928,7 @@ impl PipeConnection {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let mut size_transferred = 0;
|
let mut size_transferred = 0;
|
||||||
|
// SAFETY:
|
||||||
// Safe as long as `overlapped_struct` isn't copied and also contains a valid event.
|
// Safe as long as `overlapped_struct` isn't copied and also contains a valid event.
|
||||||
// Also the named pipe handle must created with `FILE_FLAG_OVERLAPPED`.
|
// Also the named pipe handle must created with `FILE_FLAG_OVERLAPPED`.
|
||||||
fail_if_zero!(unsafe {
|
fail_if_zero!(unsafe {
|
||||||
|
@ -934,12 +946,15 @@ impl PipeConnection {
|
||||||
/// Cancels I/O Operations in the current process. Since `lpOverlapped` is null, this will
|
/// Cancels I/O Operations in the current process. Since `lpOverlapped` is null, this will
|
||||||
/// cancel all I/O requests for the file handle passed in.
|
/// cancel all I/O requests for the file handle passed in.
|
||||||
pub fn cancel_io(&mut self) -> Result<()> {
|
pub fn cancel_io(&mut self) -> Result<()> {
|
||||||
fail_if_zero!(unsafe {
|
fail_if_zero!(
|
||||||
CancelIoEx(
|
// SAFETY: descriptor is valid and the return value is checked.
|
||||||
self.handle.as_raw_descriptor(),
|
unsafe {
|
||||||
/* lpOverlapped= */ std::ptr::null_mut(),
|
CancelIoEx(
|
||||||
)
|
self.handle.as_raw_descriptor(),
|
||||||
});
|
/* lpOverlapped= */ std::ptr::null_mut(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -979,6 +994,7 @@ impl PipeConnection {
|
||||||
/// call this if you are sure the client is reading the
|
/// call this if you are sure the client is reading the
|
||||||
/// data!
|
/// data!
|
||||||
pub fn flush_data_blocking(&self) -> Result<()> {
|
pub fn flush_data_blocking(&self) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the only buffers interacted with are
|
// Safe because the only buffers interacted with are
|
||||||
// outside of Rust memory
|
// outside of Rust memory
|
||||||
fail_if_zero!(unsafe { FlushFileBuffers(self.as_raw_descriptor()) });
|
fail_if_zero!(unsafe { FlushFileBuffers(self.as_raw_descriptor()) });
|
||||||
|
@ -987,6 +1003,7 @@ impl PipeConnection {
|
||||||
|
|
||||||
/// For a server pipe, disconnect all clients, discarding any buffered data.
|
/// For a server pipe, disconnect all clients, discarding any buffered data.
|
||||||
pub fn disconnect_clients(&self) -> Result<()> {
|
pub fn disconnect_clients(&self) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we own the handle passed in and know it will remain valid for the duration
|
// Safe because we own the handle passed in and know it will remain valid for the duration
|
||||||
// of the call. Discarded buffers are not managed by rust.
|
// of the call. Discarded buffers are not managed by rust.
|
||||||
fail_if_zero!(unsafe { DisconnectNamedPipe(self.as_raw_descriptor()) });
|
fail_if_zero!(unsafe { DisconnectNamedPipe(self.as_raw_descriptor()) });
|
||||||
|
@ -1006,11 +1023,14 @@ impl IntoRawDescriptor for PipeConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: Send safety is ensured by inner fields.
|
||||||
unsafe impl Send for PipeConnection {}
|
unsafe impl Send for PipeConnection {}
|
||||||
|
// SAFETY: Sync safety is ensured by inner fields.
|
||||||
unsafe impl Sync for PipeConnection {}
|
unsafe impl Sync for PipeConnection {}
|
||||||
|
|
||||||
impl io::Read for PipeConnection {
|
impl io::Read for PipeConnection {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
// SAFETY:
|
||||||
// This is safe because PipeConnection::read is always safe for u8
|
// This is safe because PipeConnection::read is always safe for u8
|
||||||
unsafe { PipeConnection::read(self, buf) }
|
unsafe { PipeConnection::read(self, buf) }
|
||||||
}
|
}
|
||||||
|
@ -1118,16 +1138,16 @@ impl MultiPartMessagePipe {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
/// `buf` and `overlapped_wrapper` will be in use for the duration of
|
|
||||||
/// the overlapped operation. These must not be reused and must live until
|
|
||||||
/// after `get_overlapped_result()` has been called which is done right
|
|
||||||
/// after this call.
|
|
||||||
fn write_overlapped_blocking_message_internal<T: PipeSendable>(
|
fn write_overlapped_blocking_message_internal<T: PipeSendable>(
|
||||||
pipe: &mut PipeConnection,
|
pipe: &mut PipeConnection,
|
||||||
buf: &[T],
|
buf: &[T],
|
||||||
overlapped_wrapper: &mut OverlappedWrapper,
|
overlapped_wrapper: &mut OverlappedWrapper,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
// Safety:
|
||||||
|
// `buf` and `overlapped_wrapper` will be in use for the duration of
|
||||||
|
// the overlapped operation. These must not be reused and must live until
|
||||||
|
// after `get_overlapped_result()` has been called which is done right
|
||||||
|
// after this call.
|
||||||
unsafe {
|
unsafe {
|
||||||
pipe.write_overlapped(buf, overlapped_wrapper)?;
|
pipe.write_overlapped(buf, overlapped_wrapper)?;
|
||||||
}
|
}
|
||||||
|
@ -1229,6 +1249,7 @@ mod tests {
|
||||||
let (p1, p2) = pair(&FramingMode::Byte, &BlockingMode::Wait, 0).unwrap();
|
let (p1, p2) = pair(&FramingMode::Byte, &BlockingMode::Wait, 0).unwrap();
|
||||||
|
|
||||||
// Test both forward and reverse direction since the underlying APIs are a bit asymmetrical
|
// Test both forward and reverse direction since the underlying APIs are a bit asymmetrical
|
||||||
|
// SAFETY: trivially safe with pipe created and return value checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
for (dir, sender, receiver) in [("1 -> 2", &p1, &p2), ("2 -> 1", &p2, &p1)].iter() {
|
for (dir, sender, receiver) in [("1 -> 2", &p1, &p2), ("2 -> 1", &p2, &p1)].iter() {
|
||||||
println!("{}", dir);
|
println!("{}", dir);
|
||||||
|
@ -1284,6 +1305,7 @@ mod tests {
|
||||||
let (p1, p2) = pair(&FramingMode::Message, &BlockingMode::Wait, 0).unwrap();
|
let (p1, p2) = pair(&FramingMode::Message, &BlockingMode::Wait, 0).unwrap();
|
||||||
|
|
||||||
// Test both forward and reverse direction since the underlying APIs are a bit asymmetrical
|
// Test both forward and reverse direction since the underlying APIs are a bit asymmetrical
|
||||||
|
// SAFETY: trivially safe with pipe created and return value checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
for (dir, sender, receiver) in [("1 -> 2", &p1, &p2), ("2 -> 1", &p2, &p1)].iter() {
|
for (dir, sender, receiver) in [("1 -> 2", &p1, &p2), ("2 -> 1", &p2, &p1)].iter() {
|
||||||
println!("{}", dir);
|
println!("{}", dir);
|
||||||
|
@ -1310,6 +1332,7 @@ mod tests {
|
||||||
let mut recv_buffer: [u8; 1] = [0; 1];
|
let mut recv_buffer: [u8; 1] = [0; 1];
|
||||||
|
|
||||||
// Test both forward and reverse direction since the underlying APIs are a bit asymmetrical
|
// Test both forward and reverse direction since the underlying APIs are a bit asymmetrical
|
||||||
|
// SAFETY: trivially safe with PipeConnection created and return value checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
for (dir, sender, receiver) in [("1 -> 2", &p1, &p2), ("2 -> 1", &p2, &p1)].iter() {
|
for (dir, sender, receiver) in [("1 -> 2", &p1, &p2), ("2 -> 1", &p2, &p1)].iter() {
|
||||||
println!("{}", dir);
|
println!("{}", dir);
|
||||||
|
@ -1362,6 +1385,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because `read_overlapped` can be called since overlapped struct is created.
|
// Safe because `read_overlapped` can be called since overlapped struct is created.
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut p1_overlapped_wrapper =
|
let mut p1_overlapped_wrapper =
|
||||||
|
@ -1419,9 +1443,9 @@ mod tests {
|
||||||
let res = unsafe { p1.write_overlapped(&data, &mut overlapped_wrapper) };
|
let res = unsafe { p1.write_overlapped(&data, &mut overlapped_wrapper) };
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
|
|
||||||
// SAFETY: safe because we know the unsafe re-use of overlapped wrapper
|
|
||||||
// will error out.
|
|
||||||
let res =
|
let res =
|
||||||
|
// SAFETY: safe because we know the unsafe re-use of overlapped wrapper
|
||||||
|
// will error out.
|
||||||
unsafe { p2.write_overlapped(&[75, 77, 54, 82, 76, 65], &mut overlapped_wrapper) };
|
unsafe { p2.write_overlapped(&[75, 77, 54, 82, 76, 65], &mut overlapped_wrapper) };
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ static mut NT_LIBRARY: MaybeUninit<HMODULE> = MaybeUninit::uninit();
|
||||||
#[inline]
|
#[inline]
|
||||||
fn init_ntdll() -> Result<HINSTANCE> {
|
fn init_ntdll() -> Result<HINSTANCE> {
|
||||||
NT_INIT.call_once(|| {
|
NT_INIT.call_once(|| {
|
||||||
|
// SAFETY: return value is checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
*NT_LIBRARY.as_mut_ptr() =
|
*NT_LIBRARY.as_mut_ptr() =
|
||||||
libloaderapi::LoadLibraryW(win32_wide_string("ntdll").as_ptr());
|
libloaderapi::LoadLibraryW(win32_wide_string("ntdll").as_ptr());
|
||||||
|
@ -44,6 +45,7 @@ fn init_ntdll() -> Result<HINSTANCE> {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// SAFETY: NT_LIBRARY initialized above.
|
||||||
let handle = unsafe { NT_LIBRARY.assume_init() };
|
let handle = unsafe { NT_LIBRARY.assume_init() };
|
||||||
if handle.is_null() {
|
if handle.is_null() {
|
||||||
Err(Error::from(io::Error::new(
|
Err(Error::from(io::Error::new(
|
||||||
|
@ -56,6 +58,7 @@ fn init_ntdll() -> Result<HINSTANCE> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_symbol(handle: HMODULE, proc_name: &str) -> Result<*mut minwindef::__some_function> {
|
fn get_symbol(handle: HMODULE, proc_name: &str) -> Result<*mut minwindef::__some_function> {
|
||||||
|
// SAFETY: return value is checked.
|
||||||
let symbol = unsafe { libloaderapi::GetProcAddress(handle, win32_string(proc_name).as_ptr()) };
|
let symbol = unsafe { libloaderapi::GetProcAddress(handle, win32_string(proc_name).as_ptr()) };
|
||||||
if symbol.is_null() {
|
if symbol.is_null() {
|
||||||
Err(Error::last())
|
Err(Error::last())
|
||||||
|
@ -68,6 +71,7 @@ fn get_symbol(handle: HMODULE, proc_name: &str) -> Result<*mut minwindef::__some
|
||||||
pub fn nt_query_timer_resolution() -> Result<(Duration, Duration)> {
|
pub fn nt_query_timer_resolution() -> Result<(Duration, Duration)> {
|
||||||
let handle = init_ntdll()?;
|
let handle = init_ntdll()?;
|
||||||
|
|
||||||
|
// SAFETY: trivially safe
|
||||||
let func = unsafe {
|
let func = unsafe {
|
||||||
std::mem::transmute::<
|
std::mem::transmute::<
|
||||||
*mut minwindef::__some_function,
|
*mut minwindef::__some_function,
|
||||||
|
@ -99,6 +103,7 @@ pub fn nt_query_timer_resolution() -> Result<(Duration, Duration)> {
|
||||||
|
|
||||||
pub fn nt_set_timer_resolution(resolution: Duration) -> Result<()> {
|
pub fn nt_set_timer_resolution(resolution: Duration) -> Result<()> {
|
||||||
let handle = init_ntdll()?;
|
let handle = init_ntdll()?;
|
||||||
|
// SAFETY: trivially safe
|
||||||
let func = unsafe {
|
let func = unsafe {
|
||||||
std::mem::transmute::<
|
std::mem::transmute::<
|
||||||
*mut minwindef::__some_function,
|
*mut minwindef::__some_function,
|
||||||
|
@ -150,10 +155,11 @@ pub fn set_time_period(res: Duration, begin: bool) -> Result<()> {
|
||||||
panic!("time(Begin|End)Period does not support values above u32::MAX.",);
|
panic!("time(Begin|End)Period does not support values above u32::MAX.",);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trivially safe. Note that the casts are safe because we know res is within u32's range.
|
|
||||||
let ret = if begin {
|
let ret = if begin {
|
||||||
|
// SAFETY: Trivially safe. Note that the casts are safe because we know res is within u32's range.
|
||||||
unsafe { timeBeginPeriod(res.as_millis() as u32) }
|
unsafe { timeBeginPeriod(res.as_millis() as u32) }
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY: Trivially safe. Note that the casts are safe because we know res is within u32's range.
|
||||||
unsafe { timeEndPeriod(res.as_millis() as u32) }
|
unsafe { timeEndPeriod(res.as_millis() as u32) }
|
||||||
};
|
};
|
||||||
if ret != TIMERR_NOERROR {
|
if ret != TIMERR_NOERROR {
|
||||||
|
|
|
@ -16,6 +16,7 @@ use super::errno_result;
|
||||||
use super::Result;
|
use super::Result;
|
||||||
|
|
||||||
pub fn set_audio_thread_priority() -> Result<SafeMultimediaHandle> {
|
pub fn set_audio_thread_priority() -> Result<SafeMultimediaHandle> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we know Pro Audio is part of windows and we down task_index.
|
// Safe because we know Pro Audio is part of windows and we down task_index.
|
||||||
let multimedia_handle = unsafe {
|
let multimedia_handle = unsafe {
|
||||||
let mut task_index: u32 = 0;
|
let mut task_index: u32 = 0;
|
||||||
|
@ -28,6 +29,7 @@ pub fn set_audio_thread_priority() -> Result<SafeMultimediaHandle> {
|
||||||
if multimedia_handle.is_null() {
|
if multimedia_handle.is_null() {
|
||||||
warn!(
|
warn!(
|
||||||
"Failed to set audio thread to Pro Audio. Error: {}",
|
"Failed to set audio thread to Pro Audio. Error: {}",
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe { GetLastError() }
|
unsafe { GetLastError() }
|
||||||
);
|
);
|
||||||
errno_result()
|
errno_result()
|
||||||
|
@ -38,6 +40,7 @@ pub fn set_audio_thread_priority() -> Result<SafeMultimediaHandle> {
|
||||||
|
|
||||||
pub fn set_thread_priority(thread_priority: i32) -> Result<()> {
|
pub fn set_thread_priority(thread_priority: i32) -> Result<()> {
|
||||||
let res =
|
let res =
|
||||||
|
// SAFETY:
|
||||||
// Safe because priority level value is valid and a valid thread handle will be passed in
|
// Safe because priority level value is valid and a valid thread handle will be passed in
|
||||||
unsafe { SetThreadPriority(GetCurrentThread(), thread_priority) };
|
unsafe { SetThreadPriority(GetCurrentThread(), thread_priority) };
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
|
@ -53,13 +56,16 @@ pub struct SafeMultimediaHandle {
|
||||||
|
|
||||||
impl Drop for SafeMultimediaHandle {
|
impl Drop for SafeMultimediaHandle {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we `multimedia_handle` is defined in the same thread and is created in the
|
// Safe because we `multimedia_handle` is defined in the same thread and is created in the
|
||||||
// function above. `multimedia_handle` needs be created from `AvSetMmThreadCharacteristicsA`.
|
// function above. `multimedia_handle` needs be created from `AvSetMmThreadCharacteristicsA`.
|
||||||
// This will also drop the `mulitmedia_handle`.
|
// This will also drop the `mulitmedia_handle`.
|
||||||
if unsafe { AvRevertMmThreadCharacteristics(self.multimedia_handle) } == FALSE {
|
if unsafe { AvRevertMmThreadCharacteristics(self.multimedia_handle) } == FALSE {
|
||||||
warn!("Failed to revert audio thread. Error: {}", unsafe {
|
warn!(
|
||||||
GetLastError()
|
"Failed to revert audio thread. Error: {}",
|
||||||
});
|
// SAFETY: trivially safe
|
||||||
|
unsafe { GetLastError() }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,6 +83,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn test_mm_handle_is_dropped() {
|
fn test_mm_handle_is_dropped() {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the only the only unsafe functions called are to get the thread
|
// Safe because the only the only unsafe functions called are to get the thread
|
||||||
// priority.
|
// priority.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub(crate) fn file_punch_hole(handle: &File, offset: u64, length: u64) -> io::Re
|
||||||
BeyondFinalZero: *end_offset,
|
BeyondFinalZero: *end_offset,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we check the return value and all values should be set
|
// Safe because we check the return value and all values should be set
|
||||||
let result = unsafe { super::ioctl::ioctl_with_ref(handle, FSCTL_SET_ZERO_DATA, &zero_data) };
|
let result = unsafe { super::ioctl::ioctl_with_ref(handle, FSCTL_SET_ZERO_DATA, &zero_data) };
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub fn set_cpu_affinity<I: IntoIterator<Item = usize>>(cpus: I) -> Result<usize>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cpu_affinity_mask(affinity_mask: usize) -> Result<usize> {
|
pub fn set_cpu_affinity_mask(affinity_mask: usize) -> Result<usize> {
|
||||||
|
// SAFETY: trivially safe as return value is checked.
|
||||||
let res: usize = unsafe {
|
let res: usize = unsafe {
|
||||||
let thread_handle = GetCurrentThread();
|
let thread_handle = GetCurrentThread();
|
||||||
SetThreadAffinityMask(thread_handle, affinity_mask)
|
SetThreadAffinityMask(thread_handle, affinity_mask)
|
||||||
|
@ -63,6 +64,7 @@ mod tests {
|
||||||
fn cpu_affinity() {
|
fn cpu_affinity() {
|
||||||
let mut process_affinity_mask: usize = 0;
|
let mut process_affinity_mask: usize = 0;
|
||||||
let mut system_affinity_mask: usize = 0;
|
let mut system_affinity_mask: usize = 0;
|
||||||
|
// SAFETY: trivially safe as return value is checked.
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
GetProcessAffinityMask(
|
GetProcessAffinityMask(
|
||||||
GetCurrentProcess(),
|
GetCurrentProcess(),
|
||||||
|
|
|
@ -15,13 +15,15 @@ use crate::SharedMemory;
|
||||||
|
|
||||||
impl PlatformSharedMemory for SharedMemory {
|
impl PlatformSharedMemory for SharedMemory {
|
||||||
fn new(_debug_name: &CStr, size: u64) -> Result<SharedMemory> {
|
fn new(_debug_name: &CStr, size: u64) -> Result<SharedMemory> {
|
||||||
// Safe because we do not provide a handle.
|
|
||||||
let mapping_handle =
|
let mapping_handle =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we do not provide a handle.
|
||||||
unsafe { create_file_mapping(None, size, PAGE_EXECUTE_READWRITE, None) }
|
unsafe { create_file_mapping(None, size, PAGE_EXECUTE_READWRITE, None) }
|
||||||
.map_err(super::Error::from)?;
|
.map_err(super::Error::from)?;
|
||||||
|
|
||||||
// Safe because we have exclusive ownership of mapping_handle & it is valid.
|
|
||||||
Self::from_safe_descriptor(
|
Self::from_safe_descriptor(
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we have exclusive ownership of mapping_handle & it is valid.
|
||||||
unsafe { SafeDescriptor::from_raw_descriptor(mapping_handle) },
|
unsafe { SafeDescriptor::from_raw_descriptor(mapping_handle) },
|
||||||
size,
|
size,
|
||||||
)
|
)
|
||||||
|
|
|
@ -203,11 +203,10 @@ impl StreamChannel {
|
||||||
// could stall readers.)
|
// could stall readers.)
|
||||||
let _read_lock = self.read_lock.lock();
|
let _read_lock = self.read_lock.lock();
|
||||||
|
|
||||||
let res = unsafe {
|
// SAFETY:
|
||||||
// Safe because no partial reads are possible, and the underlying code bounds the
|
// Safe because no partial reads are possible, and the underlying code bounds the
|
||||||
// read by buf's size.
|
// read by buf's size.
|
||||||
self.pipe_conn.read(buf)
|
let res = unsafe { self.pipe_conn.read(buf) };
|
||||||
};
|
|
||||||
|
|
||||||
// The entire goal of this complex section is to avoid the need for shared memory between
|
// The entire goal of this complex section is to avoid the need for shared memory between
|
||||||
// each channel end to synchronize the notification state. It is very subtle, modify with
|
// each channel end to synchronize the notification state. It is very subtle, modify with
|
||||||
|
|
|
@ -26,8 +26,10 @@ use crate::syslog::Log;
|
||||||
use crate::syslog::Syslog;
|
use crate::syslog::Syslog;
|
||||||
use crate::RawDescriptor;
|
use crate::RawDescriptor;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// On windows RawDescriptor is !Sync + !Send, but also on windows we don't do anything with them
|
// On windows RawDescriptor is !Sync + !Send, but also on windows we don't do anything with them
|
||||||
unsafe impl Sync for crate::syslog::State {}
|
unsafe impl Sync for crate::syslog::State {}
|
||||||
|
// SAFETY: See comments for impl Sync
|
||||||
unsafe impl Send for crate::syslog::State {}
|
unsafe impl Send for crate::syslog::State {}
|
||||||
|
|
||||||
pub struct PlatformSyslog {}
|
pub struct PlatformSyslog {}
|
||||||
|
|
|
@ -19,6 +19,7 @@ struct SystemInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
static SYSTEM_INFO: Lazy<SystemInfo> = Lazy::new(|| {
|
static SYSTEM_INFO: Lazy<SystemInfo> = Lazy::new(|| {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this is a universally available call on modern Windows systems.
|
// Safe because this is a universally available call on modern Windows systems.
|
||||||
let sysinfo = unsafe {
|
let sysinfo = unsafe {
|
||||||
let mut sysinfo = MaybeUninit::<SYSTEM_INFO>::uninit();
|
let mut sysinfo = MaybeUninit::<SYSTEM_INFO>::uninit();
|
||||||
|
@ -51,6 +52,7 @@ pub fn allocation_granularity() -> u64 {
|
||||||
/// Cross-platform wrapper around getting the current process id.
|
/// Cross-platform wrapper around getting the current process id.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn getpid() -> Pid {
|
pub fn getpid() -> Pid {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we only use the return value.
|
// Safe because we only use the return value.
|
||||||
unsafe { GetCurrentProcessId() }
|
unsafe { GetCurrentProcessId() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ pub unsafe trait Terminal {
|
||||||
let descriptor = self.terminal_descriptor();
|
let descriptor = self.terminal_descriptor();
|
||||||
let mut orig_mode = 0;
|
let mut orig_mode = 0;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we provide a valid descriptor and pointer and we check the return result.
|
// Safe because we provide a valid descriptor and pointer and we check the return result.
|
||||||
if unsafe { GetConsoleMode(descriptor, &mut orig_mode) } == 0 {
|
if unsafe { GetConsoleMode(descriptor, &mut orig_mode) } == 0 {
|
||||||
return Err(Error::last());
|
return Err(Error::last());
|
||||||
|
@ -43,6 +44,7 @@ pub unsafe trait Terminal {
|
||||||
let new_mode = (orig_mode | ENABLE_VIRTUAL_TERMINAL_INPUT)
|
let new_mode = (orig_mode | ENABLE_VIRTUAL_TERMINAL_INPUT)
|
||||||
& !(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
& !(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the syscall will only read the extent of mode and we check the return result.
|
// Safe because the syscall will only read the extent of mode and we check the return result.
|
||||||
if unsafe { SetConsoleMode(descriptor, new_mode) } == 0 {
|
if unsafe { SetConsoleMode(descriptor, new_mode) } == 0 {
|
||||||
return Err(Error::last());
|
return Err(Error::last());
|
||||||
|
@ -53,6 +55,7 @@ pub unsafe trait Terminal {
|
||||||
|
|
||||||
/// Set this terminal's mode to a previous state returned by `set_raw_mode()`.
|
/// Set this terminal's mode to a previous state returned by `set_raw_mode()`.
|
||||||
fn restore_mode(&self, mode: DWORD) -> Result<()> {
|
fn restore_mode(&self, mode: DWORD) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the syscall will only read the extent of mode and we check the return result.
|
// Safe because the syscall will only read the extent of mode and we check the return result.
|
||||||
if unsafe { SetConsoleMode(self.terminal_descriptor(), mode) } == 0 {
|
if unsafe { SetConsoleMode(self.terminal_descriptor(), mode) } == 0 {
|
||||||
Err(Error::last())
|
Err(Error::last())
|
||||||
|
@ -62,6 +65,7 @@ pub unsafe trait Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we return a genuine terminal descriptor that never changes and shares our lifetime.
|
// Safe because we return a genuine terminal descriptor that never changes and shares our lifetime.
|
||||||
unsafe impl Terminal for Stdin {
|
unsafe impl Terminal for Stdin {
|
||||||
fn terminal_descriptor(&self) -> RawDescriptor {
|
fn terminal_descriptor(&self) -> RawDescriptor {
|
||||||
|
|
|
@ -38,6 +38,7 @@ impl Timer {
|
||||||
/// `reset`. Note that this timer MAY wake/trigger early due to limitations on
|
/// `reset`. Note that this timer MAY wake/trigger early due to limitations on
|
||||||
/// SetWaitableTimer (see <https://github.com/rust-lang/rust/issues/43376>).
|
/// SetWaitableTimer (see <https://github.com/rust-lang/rust/issues/43376>).
|
||||||
pub fn new() -> Result<Timer> {
|
pub fn new() -> Result<Timer> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let handle = unsafe {
|
let handle = unsafe {
|
||||||
CreateWaitableTimerA(
|
CreateWaitableTimerA(
|
||||||
|
@ -59,8 +60,9 @@ impl Timer {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe because we uniquely own the file descriptor.
|
|
||||||
Ok(Timer {
|
Ok(Timer {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we uniquely own the file descriptor.
|
||||||
handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
|
handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
|
||||||
interval: None,
|
interval: None,
|
||||||
})
|
})
|
||||||
|
@ -100,6 +102,7 @@ impl TimerTrait for Timer {
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
SetWaitableTimer(
|
SetWaitableTimer(
|
||||||
|
@ -119,6 +122,7 @@ impl TimerTrait for Timer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait(&mut self) -> Result<()> {
|
fn wait(&mut self) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe { WaitForSingleObject(self.as_raw_descriptor(), INFINITE) };
|
let ret = unsafe { WaitForSingleObject(self.as_raw_descriptor(), INFINITE) };
|
||||||
|
|
||||||
|
@ -137,6 +141,7 @@ impl TimerTrait for Timer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) -> Result<()> {
|
fn clear(&mut self) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let ret = unsafe { CancelWaitableTimer(self.as_raw_descriptor()) };
|
let ret = unsafe { CancelWaitableTimer(self.as_raw_descriptor()) };
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,8 @@ impl<T: EventToken> EventContext<T> {
|
||||||
// which always populates the list.
|
// which always populates the list.
|
||||||
return Err(Error::new(ERROR_INVALID_PARAMETER));
|
return Err(Error::new(ERROR_INVALID_PARAMETER));
|
||||||
}
|
}
|
||||||
|
// SAFETY: raw handles array is expected to contain valid handles and the return value of
|
||||||
|
// the function is checked.
|
||||||
let result = unsafe {
|
let result = unsafe {
|
||||||
WaitForMultipleObjects(
|
WaitForMultipleObjects(
|
||||||
raw_handles_list.len() as DWORD,
|
raw_handles_list.len() as DWORD,
|
||||||
|
@ -254,14 +256,18 @@ impl<T: EventToken> EventContext<T> {
|
||||||
if handles_offset >= handles_len {
|
if handles_offset >= handles_len {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
event_index = (unsafe {
|
event_index = (
|
||||||
WaitForMultipleObjects(
|
// SAFETY: raw handles array is expected to contain valid handles and the
|
||||||
(raw_handles_list.len() - handles_offset) as DWORD,
|
// return value of the function is checked.
|
||||||
raw_handles_list[handles_offset..].as_ptr(),
|
unsafe {
|
||||||
FALSE, // return when one event is signaled
|
WaitForMultipleObjects(
|
||||||
0, /* instantaneous timeout */
|
(raw_handles_list.len() - handles_offset) as DWORD,
|
||||||
)
|
raw_handles_list[handles_offset..].as_ptr(),
|
||||||
} - WAIT_OBJECT_0) as usize;
|
FALSE, // return when one event is signaled
|
||||||
|
0, /* instantaneous timeout */
|
||||||
|
)
|
||||||
|
} - WAIT_OBJECT_0
|
||||||
|
) as usize;
|
||||||
|
|
||||||
if event_index >= (handles_len - handles_offset) {
|
if event_index >= (handles_len - handles_offset) {
|
||||||
// This indicates a failure condition, as return values greater than the length
|
// This indicates a failure condition, as return values greater than the length
|
||||||
|
|
|
@ -122,6 +122,7 @@ impl<'a> VolatileSlice<'a> {
|
||||||
pub fn as_iobufs<'mem, 'slice>(
|
pub fn as_iobufs<'mem, 'slice>(
|
||||||
iovs: &'slice [VolatileSlice<'mem>],
|
iovs: &'slice [VolatileSlice<'mem>],
|
||||||
) -> &'slice [IoBufMut<'mem>] {
|
) -> &'slice [IoBufMut<'mem>] {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `VolatileSlice` is ABI-compatible with `IoBufMut`.
|
// Safe because `VolatileSlice` is ABI-compatible with `IoBufMut`.
|
||||||
unsafe { slice::from_raw_parts(iovs.as_ptr() as *const IoBufMut, iovs.len()) }
|
unsafe { slice::from_raw_parts(iovs.as_ptr() as *const IoBufMut, iovs.len()) }
|
||||||
}
|
}
|
||||||
|
@ -131,6 +132,7 @@ impl<'a> VolatileSlice<'a> {
|
||||||
pub fn as_iobufs_mut<'mem, 'slice>(
|
pub fn as_iobufs_mut<'mem, 'slice>(
|
||||||
iovs: &'slice mut [VolatileSlice<'mem>],
|
iovs: &'slice mut [VolatileSlice<'mem>],
|
||||||
) -> &'slice mut [IoBufMut<'mem>] {
|
) -> &'slice mut [IoBufMut<'mem>] {
|
||||||
|
// SAFETY:
|
||||||
// Safe because `VolatileSlice` is ABI-compatible with `IoBufMut`.
|
// Safe because `VolatileSlice` is ABI-compatible with `IoBufMut`.
|
||||||
unsafe { slice::from_raw_parts_mut(iovs.as_mut_ptr() as *mut IoBufMut, iovs.len()) }
|
unsafe { slice::from_raw_parts_mut(iovs.as_mut_ptr() as *mut IoBufMut, iovs.len()) }
|
||||||
}
|
}
|
||||||
|
@ -149,6 +151,7 @@ impl<'a> VolatileSlice<'a> {
|
||||||
.checked_sub(count)
|
.checked_sub(count)
|
||||||
.ok_or(VolatileMemoryError::OutOfBounds { addr: new_addr })?;
|
.ok_or(VolatileMemoryError::OutOfBounds { addr: new_addr })?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the memory has the same lifetime and points to a subset of the memory of the
|
// Safe because the memory has the same lifetime and points to a subset of the memory of the
|
||||||
// original slice.
|
// original slice.
|
||||||
unsafe { Ok(VolatileSlice::from_raw_parts(new_addr as *mut u8, new_size)) }
|
unsafe { Ok(VolatileSlice::from_raw_parts(new_addr as *mut u8, new_size)) }
|
||||||
|
@ -174,6 +177,7 @@ impl<'a> VolatileSlice<'a> {
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we have verified that the new memory is a subset of the original slice.
|
// Safe because we have verified that the new memory is a subset of the original slice.
|
||||||
Ok(unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) })
|
Ok(unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) })
|
||||||
}
|
}
|
||||||
|
@ -196,6 +200,7 @@ impl<'a> VolatileSlice<'a> {
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
pub fn write_bytes(&self, value: u8) {
|
pub fn write_bytes(&self, value: u8) {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the memory is valid and needs only byte alignment.
|
// Safe because the memory is valid and needs only byte alignment.
|
||||||
unsafe {
|
unsafe {
|
||||||
write_bytes(self.as_mut_ptr(), value, self.size());
|
write_bytes(self.as_mut_ptr(), value, self.size());
|
||||||
|
@ -230,6 +235,7 @@ impl<'a> VolatileSlice<'a> {
|
||||||
{
|
{
|
||||||
let mut addr = self.as_mut_ptr() as *const u8;
|
let mut addr = self.as_mut_ptr() as *const u8;
|
||||||
for v in buf.iter_mut().take(self.size() / size_of::<T>()) {
|
for v in buf.iter_mut().take(self.size() / size_of::<T>()) {
|
||||||
|
// SAFETY: Safe because buf is valid, aligned to type `T` and is initialized.
|
||||||
unsafe {
|
unsafe {
|
||||||
*v = read_volatile(addr as *const T);
|
*v = read_volatile(addr as *const T);
|
||||||
addr = addr.add(size_of::<T>());
|
addr = addr.add(size_of::<T>());
|
||||||
|
@ -253,6 +259,7 @@ impl<'a> VolatileSlice<'a> {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn copy_to_volatile_slice(&self, slice: VolatileSlice) {
|
pub fn copy_to_volatile_slice(&self, slice: VolatileSlice) {
|
||||||
|
// SAFETY: Safe because slice is valid and is byte aligned.
|
||||||
unsafe {
|
unsafe {
|
||||||
copy(
|
copy(
|
||||||
self.as_mut_ptr() as *const u8,
|
self.as_mut_ptr() as *const u8,
|
||||||
|
@ -293,6 +300,7 @@ impl<'a> VolatileSlice<'a> {
|
||||||
{
|
{
|
||||||
let mut addr = self.as_mut_ptr();
|
let mut addr = self.as_mut_ptr();
|
||||||
for v in buf.iter().take(self.size() / size_of::<T>()) {
|
for v in buf.iter().take(self.size() / size_of::<T>()) {
|
||||||
|
// SAFETY: Safe because buf is valid, aligned to type `T` and is mutable.
|
||||||
unsafe {
|
unsafe {
|
||||||
write_volatile(
|
write_volatile(
|
||||||
addr as *mut T,
|
addr as *mut T,
|
||||||
|
@ -318,11 +326,11 @@ impl<'a> VolatileSlice<'a> {
|
||||||
let aligned_tail_addr = tail_addr & !MASK_4BIT;
|
let aligned_tail_addr = tail_addr & !MASK_4BIT;
|
||||||
|
|
||||||
// Check 16 bytes at once. The addresses should be 16 bytes aligned for better performance.
|
// Check 16 bytes at once. The addresses should be 16 bytes aligned for better performance.
|
||||||
// SAFETY: Each aligned_addr is within VolatileSlice
|
if (aligned_head_addr..aligned_tail_addr).step_by(16).any(
|
||||||
if (aligned_head_addr..aligned_tail_addr)
|
|aligned_addr|
|
||||||
.step_by(16)
|
// SAFETY: Each aligned_addr is within VolatileSlice
|
||||||
.any(|aligned_addr| unsafe { *(aligned_addr as *const u128) } != 0)
|
unsafe { *(aligned_addr as *const u128) } != 0,
|
||||||
{
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +354,7 @@ impl<'a> VolatileSlice<'a> {
|
||||||
///
|
///
|
||||||
/// This checks byte by byte.
|
/// This checks byte by byte.
|
||||||
///
|
///
|
||||||
/// ## Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// * `head_addr` <= `tail_addr`
|
/// * `head_addr` <= `tail_addr`
|
||||||
/// * Bytes between `head_addr` and `tail_addr` is valid to access.
|
/// * Bytes between `head_addr` and `tail_addr` is valid to access.
|
||||||
|
@ -417,7 +425,10 @@ mod tests {
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) })
|
Ok(
|
||||||
|
// SAFETY: trivially safe
|
||||||
|
unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,10 +52,12 @@ fn safe_descriptor_from_path_none() {
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::eq_op)]
|
#[allow(clippy::eq_op)]
|
||||||
fn clone_equality() {
|
fn clone_equality() {
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let ret = unsafe { libc::eventfd(0, 0) };
|
let ret = unsafe { libc::eventfd(0, 0) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
panic!("failed to create eventfd");
|
panic!("failed to create eventfd");
|
||||||
}
|
}
|
||||||
|
// SAFETY: Safe because ret is valid and return value is checked.
|
||||||
let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
|
let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
|
||||||
|
|
||||||
assert_eq!(descriptor, descriptor);
|
assert_eq!(descriptor, descriptor);
|
||||||
|
@ -65,10 +67,13 @@ fn clone_equality() {
|
||||||
descriptor.try_clone().expect("failed to clone eventfd")
|
descriptor.try_clone().expect("failed to clone eventfd")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// SAFETY: Safe because return value is checked.
|
||||||
let ret = unsafe { libc::eventfd(0, 0) };
|
let ret = unsafe { libc::eventfd(0, 0) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
panic!("failed to create eventfd");
|
panic!("failed to create eventfd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: Safe because ret is valid and return value is checked.
|
||||||
let another = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
|
let another = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
|
||||||
|
|
||||||
assert_ne!(descriptor, another);
|
assert_ne!(descriptor, another);
|
||||||
|
|
|
@ -36,6 +36,7 @@ fn unix_seqpacket_listener_from_fd() {
|
||||||
UnixSeqpacketListener::bind(&socket_path).expect("failed to create UnixSeqpacketListener"),
|
UnixSeqpacketListener::bind(&socket_path).expect("failed to create UnixSeqpacketListener"),
|
||||||
);
|
);
|
||||||
// UnixSeqpacketListener should succeed on a valid listening descriptor.
|
// UnixSeqpacketListener should succeed on a valid listening descriptor.
|
||||||
|
// SAFETY: Safe because `listener` is valid and the return value is checked.
|
||||||
let good_dup = UnixSeqpacketListener::bind(format!("/proc/self/fd/{}", unsafe {
|
let good_dup = UnixSeqpacketListener::bind(format!("/proc/self/fd/{}", unsafe {
|
||||||
libc::dup(listener.as_raw_descriptor())
|
libc::dup(listener.as_raw_descriptor())
|
||||||
}));
|
}));
|
||||||
|
@ -46,6 +47,7 @@ fn unix_seqpacket_listener_from_fd() {
|
||||||
assert!(good_dup_path.is_err());
|
assert!(good_dup_path.is_err());
|
||||||
// UnixSeqpacketListener must fail on an existing non-listener socket.
|
// UnixSeqpacketListener must fail on an existing non-listener socket.
|
||||||
let s1 = UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
|
let s1 = UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
|
||||||
|
// SAFETY: Safe because `s1` is valid and the return value is checked.
|
||||||
let bad_dup = UnixSeqpacketListener::bind(format!("/proc/self/fd/{}", unsafe {
|
let bad_dup = UnixSeqpacketListener::bind(format!("/proc/self/fd/{}", unsafe {
|
||||||
libc::dup(s1.as_raw_descriptor())
|
libc::dup(s1.as_raw_descriptor())
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -37,9 +37,9 @@ fn test_serialize_tube_new() {
|
||||||
let msg_descriptors = msg_serialize.into_descriptors();
|
let msg_descriptors = msg_serialize.into_descriptors();
|
||||||
|
|
||||||
// Deserialize the Tube
|
// Deserialize the Tube
|
||||||
let msg_descriptors_safe = msg_descriptors
|
let msg_descriptors_safe = msg_descriptors.into_iter().map(|v|
|
||||||
.into_iter()
|
// SAFETY: Safe because `v` is a valid descriptor
|
||||||
.map(|v| unsafe { SafeDescriptor::from_raw_descriptor(v) });
|
unsafe { SafeDescriptor::from_raw_descriptor(v) });
|
||||||
let tube_deserialized: Tube =
|
let tube_deserialized: Tube =
|
||||||
deserialize_with_descriptors(|| serde_json::from_slice(&serialized), msg_descriptors_safe)
|
deserialize_with_descriptors(|| serde_json::from_slice(&serialized), msg_descriptors_safe)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -52,6 +52,7 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(thread_comm, thread_name + "\n");
|
assert_eq!(thread_comm, thread_name + "\n");
|
||||||
|
|
||||||
|
// SAFETY: child pid is expected to be valid and we wait on the child
|
||||||
unsafe { libc::kill(child.pid, libc::SIGKILL) };
|
unsafe { libc::kill(child.pid, libc::SIGKILL) };
|
||||||
child.wait().unwrap();
|
child.wait().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -75,6 +76,7 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(thread_comm, "123456789012345\n");
|
assert_eq!(thread_comm, "123456789012345\n");
|
||||||
|
|
||||||
|
// SAFETY: child pid is expected to be valid and we wait on the child
|
||||||
unsafe { libc::kill(child.pid, libc::SIGKILL) };
|
unsafe { libc::kill(child.pid, libc::SIGKILL) };
|
||||||
child.wait().unwrap();
|
child.wait().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,9 +135,9 @@ fn test_serialize_tube_pair() {
|
||||||
let msg_descriptors = msg_serialize.into_descriptors();
|
let msg_descriptors = msg_serialize.into_descriptors();
|
||||||
|
|
||||||
// Deserialize the Tube
|
// Deserialize the Tube
|
||||||
let msg_descriptors_safe = msg_descriptors
|
let msg_descriptors_safe = msg_descriptors.into_iter().map(|v|
|
||||||
.into_iter()
|
// SAFETY: `v` is expected to be valid
|
||||||
.map(|v| unsafe { SafeDescriptor::from_raw_descriptor(v) });
|
unsafe { SafeDescriptor::from_raw_descriptor(v) });
|
||||||
let tube_deserialized: Tube =
|
let tube_deserialized: Tube =
|
||||||
deserialize_with_descriptors(|| serde_json::from_slice(&serialized), msg_descriptors_safe)
|
deserialize_with_descriptors(|| serde_json::from_slice(&serialized), msg_descriptors_safe)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -70,8 +70,9 @@ pub fn common_child_setup(args: CommonChildStartupArgs) -> anyhow::Result<ChildL
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if let Some(log_file_descriptor) = args.syslog_file {
|
if let Some(log_file_descriptor) = args.syslog_file {
|
||||||
// Safe because we are taking ownership of a SafeDescriptor.
|
|
||||||
let log_file =
|
let log_file =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we are taking ownership of a SafeDescriptor.
|
||||||
unsafe { File::from_raw_descriptor(log_file_descriptor.into_raw_descriptor()) };
|
unsafe { File::from_raw_descriptor(log_file_descriptor.into_raw_descriptor()) };
|
||||||
cfg.pipe = Some(Box::new(log_file));
|
cfg.pipe = Some(Box::new(log_file));
|
||||||
cfg.log_args.stderr = false;
|
cfg.log_args.stderr = false;
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
//! assert!(b == 3);
|
//! assert!(b == 3);
|
||||||
//! assert!(l == 3);
|
//! assert!(l == 3);
|
||||||
//!
|
//!
|
||||||
|
//! // SAFETY: trivially safe
|
||||||
//! let b_trans: u32 = unsafe { std::mem::transmute(b) };
|
//! let b_trans: u32 = unsafe { std::mem::transmute(b) };
|
||||||
|
//! // SAFETY: trivially safe
|
||||||
//! let l_trans: u32 = unsafe { std::mem::transmute(l) };
|
//! let l_trans: u32 = unsafe { std::mem::transmute(l) };
|
||||||
//!
|
//!
|
||||||
//! #[cfg(target_endian = "little")]
|
//! #[cfg(target_endian = "little")]
|
||||||
|
@ -153,6 +155,7 @@ mod tests {
|
||||||
let v = 0x0123456789ABCDEF as $old_type;
|
let v = 0x0123456789ABCDEF as $old_type;
|
||||||
let endian_v: $new_type = From::from(v);
|
let endian_v: $new_type = From::from(v);
|
||||||
let endian_into: $old_type = endian_v.into();
|
let endian_into: $old_type = endian_v.into();
|
||||||
|
// SAFETY: trivially safe
|
||||||
let endian_transmute: $old_type = unsafe { transmute(endian_v) };
|
let endian_transmute: $old_type = unsafe { transmute(endian_v) };
|
||||||
|
|
||||||
if $native {
|
if $native {
|
||||||
|
|
|
@ -149,6 +149,7 @@ where
|
||||||
/// mut_entries_slice instead.
|
/// mut_entries_slice instead.
|
||||||
pub fn entries_slice(&self) -> &[S] {
|
pub fn entries_slice(&self) -> &[S] {
|
||||||
let valid_length = self.get_valid_len();
|
let valid_length = self.get_valid_len();
|
||||||
|
// SAFETY:
|
||||||
// Safe because the length has been validated.
|
// Safe because the length has been validated.
|
||||||
unsafe { self.entries[0].get_slice(valid_length) }
|
unsafe { self.entries[0].get_slice(valid_length) }
|
||||||
}
|
}
|
||||||
|
@ -157,6 +158,7 @@ where
|
||||||
pub fn mut_entries_slice(&mut self) -> &mut [S] {
|
pub fn mut_entries_slice(&mut self) -> &mut [S] {
|
||||||
let valid_length = self.get_valid_len();
|
let valid_length = self.get_valid_len();
|
||||||
self.entries[0].set_len(valid_length);
|
self.entries[0].set_len(valid_length);
|
||||||
|
// SAFETY:
|
||||||
// Safe because the length has been validated.
|
// Safe because the length has been validated.
|
||||||
unsafe { self.entries[0].get_mut_slice(valid_length) }
|
unsafe { self.entries[0].get_mut_slice(valid_length) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ impl ArcWake for Waker {
|
||||||
fn wake_by_ref(arc_self: &Arc<Self>) {
|
fn wake_by_ref(arc_self: &Arc<Self>) {
|
||||||
let state = arc_self.0.swap(WOKEN, Ordering::Release);
|
let state = arc_self.0.swap(WOKEN, Ordering::Release);
|
||||||
if state == WAITING {
|
if state == WAITING {
|
||||||
|
// SAFETY:
|
||||||
// The thread hasn't already been woken up so wake it up now. Safe because this doesn't
|
// The thread hasn't already been woken up so wake it up now. Safe because this doesn't
|
||||||
// modify any memory and we check the return value.
|
// modify any memory and we check the return value.
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
|
@ -71,6 +72,7 @@ pub fn block_on<F: Future>(f: F) -> F::Output {
|
||||||
|
|
||||||
let state = thread_waker.0.swap(WAITING, Ordering::Acquire);
|
let state = thread_waker.0.swap(WAITING, Ordering::Acquire);
|
||||||
if state == WAITING {
|
if state == WAITING {
|
||||||
|
// SAFETY:
|
||||||
// If we weren't already woken up then wait until we are. Safe because this doesn't
|
// If we weren't already woken up then wait until we are. Safe because this doesn't
|
||||||
// modify any memory and we check the return value.
|
// modify any memory and we check the return value.
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
|
|
|
@ -201,6 +201,7 @@ impl VecIoWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe to implement BackingMemory as the vec is only accessible inside the wrapper and these iovecs
|
// Safe to implement BackingMemory as the vec is only accessible inside the wrapper and these iovecs
|
||||||
// are the only thing allowed to modify it. Nothing else can get a reference to the vec until all
|
// are the only thing allowed to modify it. Nothing else can get a reference to the vec until all
|
||||||
// iovecs are dropped because they borrow Self. Nothing can borrow the owned inner vec until self
|
// iovecs are dropped because they borrow Self. Nothing can borrow the owned inner vec until self
|
||||||
|
@ -208,6 +209,7 @@ impl VecIoWrapper {
|
||||||
unsafe impl BackingMemory for VecIoWrapper {
|
unsafe impl BackingMemory for VecIoWrapper {
|
||||||
fn get_volatile_slice(&self, mem_range: MemRegion) -> Result<VolatileSlice<'_>> {
|
fn get_volatile_slice(&self, mem_range: MemRegion) -> Result<VolatileSlice<'_>> {
|
||||||
self.check_addrs(&mem_range)?;
|
self.check_addrs(&mem_range)?;
|
||||||
|
// SAFETY:
|
||||||
// Safe because the mem_range range is valid in the backing memory as checked above.
|
// Safe because the mem_range range is valid in the backing memory as checked above.
|
||||||
unsafe {
|
unsafe {
|
||||||
Ok(VolatileSlice::from_raw_parts(
|
Ok(VolatileSlice::from_raw_parts(
|
||||||
|
|
|
@ -189,6 +189,7 @@ impl Condvar {
|
||||||
oldstate = self.state.load(Ordering::Relaxed);
|
oldstate = self.state.load(Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the spin lock guarantees exclusive access and the reference does not escape
|
// Safe because the spin lock guarantees exclusive access and the reference does not escape
|
||||||
// this function.
|
// this function.
|
||||||
let mu = unsafe { &mut *self.mu.get() };
|
let mu = unsafe { &mut *self.mu.get() };
|
||||||
|
@ -200,6 +201,7 @@ impl Condvar {
|
||||||
_ => panic!("Attempting to use Condvar with more than one RwLock at the same time"),
|
_ => panic!("Attempting to use Condvar with more than one RwLock at the same time"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the spin lock guarantees exclusive access.
|
// Safe because the spin lock guarantees exclusive access.
|
||||||
unsafe { (*self.waiters.get()).push_back(waiter) };
|
unsafe { (*self.waiters.get()).push_back(waiter) };
|
||||||
|
|
||||||
|
@ -241,12 +243,14 @@ impl Condvar {
|
||||||
oldstate = self.state.load(Ordering::Relaxed);
|
oldstate = self.state.load(Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the spin lock guarantees exclusive access and the reference does not escape
|
// Safe because the spin lock guarantees exclusive access and the reference does not escape
|
||||||
// this function.
|
// this function.
|
||||||
let waiters = unsafe { &mut *self.waiters.get() };
|
let waiters = unsafe { &mut *self.waiters.get() };
|
||||||
let wake_list = get_wake_list(waiters);
|
let wake_list = get_wake_list(waiters);
|
||||||
|
|
||||||
let newstate = if waiters.is_empty() {
|
let newstate = if waiters.is_empty() {
|
||||||
|
// SAFETY:
|
||||||
// Also clear the rwlock associated with this Condvar since there are no longer any
|
// Also clear the rwlock associated with this Condvar since there are no longer any
|
||||||
// waiters. Safe because the spin lock guarantees exclusive access.
|
// waiters. Safe because the spin lock guarantees exclusive access.
|
||||||
unsafe { *self.mu.get() = 0 };
|
unsafe { *self.mu.get() = 0 };
|
||||||
|
@ -299,9 +303,11 @@ impl Condvar {
|
||||||
oldstate = self.state.load(Ordering::Relaxed);
|
oldstate = self.state.load(Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the spin lock guarantees exclusive access to `self.waiters`.
|
// Safe because the spin lock guarantees exclusive access to `self.waiters`.
|
||||||
let wake_list = unsafe { (*self.waiters.get()).take() };
|
let wake_list = unsafe { (*self.waiters.get()).take() };
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Clear the rwlock associated with this Condvar since there are no longer any waiters. Safe
|
// Clear the rwlock associated with this Condvar since there are no longer any waiters. Safe
|
||||||
// because we the spin lock guarantees exclusive access.
|
// because we the spin lock guarantees exclusive access.
|
||||||
unsafe { *self.mu.get() = 0 };
|
unsafe { *self.mu.get() = 0 };
|
||||||
|
@ -337,6 +343,7 @@ impl Condvar {
|
||||||
oldstate = self.state.load(Ordering::Relaxed);
|
oldstate = self.state.load(Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the spin lock provides exclusive access and the reference does not escape
|
// Safe because the spin lock provides exclusive access and the reference does not escape
|
||||||
// this function.
|
// this function.
|
||||||
let waiters = unsafe { &mut *self.waiters.get() };
|
let waiters = unsafe { &mut *self.waiters.get() };
|
||||||
|
@ -344,6 +351,7 @@ impl Condvar {
|
||||||
let waiting_for = waiter.is_waiting_for();
|
let waiting_for = waiter.is_waiting_for();
|
||||||
// Don't drop the old waiter now as we're still holding the spin lock.
|
// Don't drop the old waiter now as we're still holding the spin lock.
|
||||||
let old_waiter = if waiter.is_linked() && waiting_for == WaitingFor::Condvar {
|
let old_waiter = if waiter.is_linked() && waiting_for == WaitingFor::Condvar {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we know that the waiter is still linked and is waiting for the Condvar,
|
// Safe because we know that the waiter is still linked and is waiting for the Condvar,
|
||||||
// which guarantees that it is still in `self.waiters`.
|
// which guarantees that it is still in `self.waiters`.
|
||||||
let mut cursor = unsafe { waiters.cursor_mut_from_ptr(waiter as *const Waiter) };
|
let mut cursor = unsafe { waiters.cursor_mut_from_ptr(waiter as *const Waiter) };
|
||||||
|
@ -361,6 +369,7 @@ impl Condvar {
|
||||||
};
|
};
|
||||||
|
|
||||||
let set_on_release = if waiters.is_empty() {
|
let set_on_release = if waiters.is_empty() {
|
||||||
|
// SAFETY:
|
||||||
// Clear the rwlock associated with this Condvar since there are no longer any waiters. Safe
|
// Clear the rwlock associated with this Condvar since there are no longer any waiters. Safe
|
||||||
// because we the spin lock guarantees exclusive access.
|
// because we the spin lock guarantees exclusive access.
|
||||||
unsafe { *self.mu.get() = 0 };
|
unsafe { *self.mu.get() = 0 };
|
||||||
|
@ -381,7 +390,11 @@ impl Condvar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl Send for Condvar {}
|
unsafe impl Send for Condvar {}
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl Sync for Condvar {}
|
unsafe impl Sync for Condvar {}
|
||||||
|
|
||||||
impl Default for Condvar {
|
impl Default for Condvar {
|
||||||
|
@ -446,6 +459,7 @@ fn get_wake_list(waiters: &mut WaiterList) -> WaiterList {
|
||||||
fn cancel_waiter(cv: usize, waiter: &Waiter, wake_next: bool) {
|
fn cancel_waiter(cv: usize, waiter: &Waiter, wake_next: bool) {
|
||||||
let condvar = cv as *const Condvar;
|
let condvar = cv as *const Condvar;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the thread that owns the waiter being canceled must also own a reference to the
|
// Safe because the thread that owns the waiter being canceled must also own a reference to the
|
||||||
// Condvar, which guarantees that this pointer is valid.
|
// Condvar, which guarantees that this pointer is valid.
|
||||||
unsafe { (*condvar).cancel_waiter(waiter, wake_next) }
|
unsafe { (*condvar).cancel_waiter(waiter, wake_next) }
|
||||||
|
@ -640,6 +654,7 @@ mod test {
|
||||||
while *count == 0 {
|
while *count == 0 {
|
||||||
count = cv.wait_read(count).await;
|
count = cv.wait_read(count).await;
|
||||||
}
|
}
|
||||||
|
// SAFETY: Safe because count is valid and is byte aligned.
|
||||||
let _ = unsafe { ptr::read_volatile(&*count as *const usize) };
|
let _ = unsafe { ptr::read_volatile(&*count as *const usize) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -305,15 +305,19 @@ impl RawRwLock {
|
||||||
{
|
{
|
||||||
let mut set_on_release = 0;
|
let mut set_on_release = 0;
|
||||||
|
|
||||||
// Safe because we have acquired the spin lock and it provides exclusive
|
|
||||||
// access to the waiter queue.
|
|
||||||
if wait_count < LONG_WAIT_THRESHOLD {
|
if wait_count < LONG_WAIT_THRESHOLD {
|
||||||
// Add the waiter to the back of the queue.
|
// Add the waiter to the back of the queue.
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we have acquired the spin lock and it provides exclusive
|
||||||
|
// access to the waiter queue.
|
||||||
unsafe { (*self.waiters.get()).push_back(w.clone()) };
|
unsafe { (*self.waiters.get()).push_back(w.clone()) };
|
||||||
} else {
|
} else {
|
||||||
// This waiter has gone through the queue too many times. Put it in the
|
// This waiter has gone through the queue too many times. Put it in the
|
||||||
// front of the queue and block all other threads from acquiring the lock
|
// front of the queue and block all other threads from acquiring the lock
|
||||||
// until this one has acquired it at least once.
|
// until this one has acquired it at least once.
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we have acquired the spin lock and it provides exclusive
|
||||||
|
// access to the waiter queue.
|
||||||
unsafe { (*self.waiters.get()).push_front(w.clone()) };
|
unsafe { (*self.waiters.get()).push_front(w.clone()) };
|
||||||
|
|
||||||
// Set the LONG_WAIT bit to prevent all other threads from acquiring the
|
// Set the LONG_WAIT bit to prevent all other threads from acquiring the
|
||||||
|
@ -459,6 +463,7 @@ impl RawRwLock {
|
||||||
// to be cleared.
|
// to be cleared.
|
||||||
let mut clear = SPINLOCK;
|
let mut clear = SPINLOCK;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the spinlock guarantees exclusive access to the waiter list and
|
// Safe because the spinlock guarantees exclusive access to the waiter list and
|
||||||
// the reference does not escape this function.
|
// the reference does not escape this function.
|
||||||
let waiters = unsafe { &mut *self.waiters.get() };
|
let waiters = unsafe { &mut *self.waiters.get() };
|
||||||
|
@ -530,6 +535,7 @@ impl RawRwLock {
|
||||||
oldstate = self.state.load(Ordering::Relaxed);
|
oldstate = self.state.load(Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the spin lock provides exclusive access and the reference does not escape
|
// Safe because the spin lock provides exclusive access and the reference does not escape
|
||||||
// this function.
|
// this function.
|
||||||
let waiters = unsafe { &mut *self.waiters.get() };
|
let waiters = unsafe { &mut *self.waiters.get() };
|
||||||
|
@ -557,6 +563,7 @@ impl RawRwLock {
|
||||||
|
|
||||||
// Don't drop the old waiter while holding the spin lock.
|
// Don't drop the old waiter while holding the spin lock.
|
||||||
let old_waiter = if waiter.is_linked() && waiting_for == WaitingFor::Mutex {
|
let old_waiter = if waiter.is_linked() && waiting_for == WaitingFor::Mutex {
|
||||||
|
// SAFETY:
|
||||||
// We know that the waiter is still linked and is waiting for the rwlock, which
|
// We know that the waiter is still linked and is waiting for the rwlock, which
|
||||||
// guarantees that it is still linked into `self.waiters`.
|
// guarantees that it is still linked into `self.waiters`.
|
||||||
let mut cursor = unsafe { waiters.cursor_mut_from_ptr(waiter as *const Waiter) };
|
let mut cursor = unsafe { waiters.cursor_mut_from_ptr(waiter as *const Waiter) };
|
||||||
|
@ -613,12 +620,17 @@ impl RawRwLock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl Send for RawRwLock {}
|
unsafe impl Send for RawRwLock {}
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl Sync for RawRwLock {}
|
unsafe impl Sync for RawRwLock {}
|
||||||
|
|
||||||
fn cancel_waiter(raw: usize, waiter: &Waiter, wake_next: bool) {
|
fn cancel_waiter(raw: usize, waiter: &Waiter, wake_next: bool) {
|
||||||
let raw_rwlock = raw as *const RawRwLock;
|
let raw_rwlock = raw as *const RawRwLock;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the thread that owns the waiter that is being canceled must also own a reference
|
// Safe because the thread that owns the waiter that is being canceled must also own a reference
|
||||||
// to the rwlock, which ensures that this pointer is valid.
|
// to the rwlock, which ensures that this pointer is valid.
|
||||||
unsafe { (*raw_rwlock).cancel_waiter(waiter, wake_next) }
|
unsafe { (*raw_rwlock).cancel_waiter(waiter, wake_next) }
|
||||||
|
@ -728,9 +740,10 @@ impl<T: ?Sized> RwLock<T> {
|
||||||
pub async fn lock(&self) -> RwLockWriteGuard<'_, T> {
|
pub async fn lock(&self) -> RwLockWriteGuard<'_, T> {
|
||||||
self.raw.lock().await;
|
self.raw.lock().await;
|
||||||
|
|
||||||
// Safe because we have exclusive access to `self.value`.
|
|
||||||
RwLockWriteGuard {
|
RwLockWriteGuard {
|
||||||
mu: self,
|
mu: self,
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we have exclusive access to `self.value`.
|
||||||
value: unsafe { &mut *self.value.get() },
|
value: unsafe { &mut *self.value.get() },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -750,9 +763,10 @@ impl<T: ?Sized> RwLock<T> {
|
||||||
pub async fn read_lock(&self) -> RwLockReadGuard<'_, T> {
|
pub async fn read_lock(&self) -> RwLockReadGuard<'_, T> {
|
||||||
self.raw.read_lock().await;
|
self.raw.read_lock().await;
|
||||||
|
|
||||||
// Safe because we have shared read-only access to `self.value`.
|
|
||||||
RwLockReadGuard {
|
RwLockReadGuard {
|
||||||
mu: self,
|
mu: self,
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we have shared read-only access to `self.value`.
|
||||||
value: unsafe { &*self.value.get() },
|
value: unsafe { &*self.value.get() },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -762,9 +776,10 @@ impl<T: ?Sized> RwLock<T> {
|
||||||
pub(crate) async fn lock_from_cv(&self) -> RwLockWriteGuard<'_, T> {
|
pub(crate) async fn lock_from_cv(&self) -> RwLockWriteGuard<'_, T> {
|
||||||
self.raw.lock_slow::<Exclusive>(DESIGNATED_WAKER, 0).await;
|
self.raw.lock_slow::<Exclusive>(DESIGNATED_WAKER, 0).await;
|
||||||
|
|
||||||
// Safe because we have exclusive access to `self.value`.
|
|
||||||
RwLockWriteGuard {
|
RwLockWriteGuard {
|
||||||
mu: self,
|
mu: self,
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we have exclusive access to `self.value`.
|
||||||
value: unsafe { &mut *self.value.get() },
|
value: unsafe { &mut *self.value.get() },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -778,9 +793,10 @@ impl<T: ?Sized> RwLock<T> {
|
||||||
.lock_slow::<Shared>(DESIGNATED_WAKER, WRITER_WAITING)
|
.lock_slow::<Shared>(DESIGNATED_WAKER, WRITER_WAITING)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Safe because we have exclusive access to `self.value`.
|
|
||||||
RwLockReadGuard {
|
RwLockReadGuard {
|
||||||
mu: self,
|
mu: self,
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we have exclusive access to `self.value`.
|
||||||
value: unsafe { &*self.value.get() },
|
value: unsafe { &*self.value.get() },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -796,13 +812,18 @@ impl<T: ?Sized> RwLock<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self) -> &mut T {
|
pub fn get_mut(&mut self) -> &mut T {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the compiler statically guarantees that are no other references to `self`.
|
// Safe because the compiler statically guarantees that are no other references to `self`.
|
||||||
// This is also why we don't need to acquire the lock first.
|
// This is also why we don't need to acquire the lock first.
|
||||||
unsafe { &mut *self.value.get() }
|
unsafe { &mut *self.value.get() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
|
unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl<T: ?Sized + Send> Sync for RwLock<T> {}
|
unsafe impl<T: ?Sized + Send> Sync for RwLock<T> {}
|
||||||
|
|
||||||
impl<T: ?Sized + Default> Default for RwLock<T> {
|
impl<T: ?Sized + Default> Default for RwLock<T> {
|
||||||
|
|
|
@ -70,6 +70,8 @@ impl<T: ?Sized> SpinLock<T> {
|
||||||
hint::spin_loop();
|
hint::spin_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
SpinLockGuard {
|
SpinLockGuard {
|
||||||
lock: self,
|
lock: self,
|
||||||
value: unsafe { &mut *self.value.get() },
|
value: unsafe { &mut *self.value.get() },
|
||||||
|
@ -84,13 +86,18 @@ impl<T: ?Sized> SpinLock<T> {
|
||||||
/// Returns a mutable reference to the contained value. This method doesn't perform any locking
|
/// Returns a mutable reference to the contained value. This method doesn't perform any locking
|
||||||
/// as the compiler will statically guarantee that there are no other references to `self`.
|
/// as the compiler will statically guarantee that there are no other references to `self`.
|
||||||
pub fn get_mut(&mut self) -> &mut T {
|
pub fn get_mut(&mut self) -> &mut T {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the compiler can statically guarantee that there are no other references to
|
// Safe because the compiler can statically guarantee that there are no other references to
|
||||||
// `self`. This is also why we don't need to acquire the lock.
|
// `self`. This is also why we don't need to acquire the lock.
|
||||||
unsafe { &mut *self.value.get() }
|
unsafe { &mut *self.value.get() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl<T: ?Sized + Send> Send for SpinLock<T> {}
|
unsafe impl<T: ?Sized + Send> Send for SpinLock<T> {}
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl<T: ?Sized + Send> Sync for SpinLock<T> {}
|
unsafe impl<T: ?Sized + Send> Sync for SpinLock<T> {}
|
||||||
|
|
||||||
impl<T: ?Sized + Default> Default for SpinLock<T> {
|
impl<T: ?Sized + Default> Default for SpinLock<T> {
|
||||||
|
|
|
@ -52,15 +52,19 @@ impl DefaultLinkOps for AtomicLink {
|
||||||
const NEW: Self::Ops = AtomicLinkOps;
|
const NEW: Self::Ops = AtomicLinkOps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the only way to mutate `AtomicLink` is via the `LinkedListOps` trait whose methods
|
// Safe because the only way to mutate `AtomicLink` is via the `LinkedListOps` trait whose methods
|
||||||
// are all unsafe and require that the caller has first called `acquire_link` (and had it return
|
// are all unsafe and require that the caller has first called `acquire_link` (and had it return
|
||||||
// true) to use them safely.
|
// true) to use them safely.
|
||||||
unsafe impl Send for AtomicLink {}
|
unsafe impl Send for AtomicLink {}
|
||||||
|
// SAFETY: See safety comment for impl Send
|
||||||
unsafe impl Sync for AtomicLink {}
|
unsafe impl Sync for AtomicLink {}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
pub struct AtomicLinkOps;
|
pub struct AtomicLinkOps;
|
||||||
|
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl LinkOps for AtomicLinkOps {
|
unsafe impl LinkOps for AtomicLinkOps {
|
||||||
type LinkPtr = NonNull<AtomicLink>;
|
type LinkPtr = NonNull<AtomicLink>;
|
||||||
|
|
||||||
|
@ -73,6 +77,8 @@ unsafe impl LinkOps for AtomicLinkOps {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe impl LinkedListOps for AtomicLinkOps {
|
unsafe impl LinkedListOps for AtomicLinkOps {
|
||||||
unsafe fn next(&self, ptr: Self::LinkPtr) -> Option<Self::LinkPtr> {
|
unsafe fn next(&self, ptr: Self::LinkPtr) -> Option<Self::LinkPtr> {
|
||||||
*ptr.as_ref().next.get()
|
*ptr.as_ref().next.get()
|
||||||
|
|
|
@ -108,8 +108,9 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
mut vec: Vec<u8>,
|
mut vec: Vec<u8>,
|
||||||
) -> AsyncResult<(usize, Vec<u8>)> {
|
) -> AsyncResult<(usize, Vec<u8>)> {
|
||||||
loop {
|
loop {
|
||||||
// Safe because this will only modify `vec` and we check the return value.
|
|
||||||
let res = if let Some(offset) = file_offset {
|
let res = if let Some(offset) = file_offset {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because this will only modify `vec` and we check the return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::pread64(
|
libc::pread64(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -119,6 +120,8 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because this will only modify `vec` and we check the return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::read(
|
libc::read(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -158,9 +161,10 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
.collect::<Vec<VolatileSlice>>();
|
.collect::<Vec<VolatileSlice>>();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Safe because we trust the kernel not to write path the length given and the length is
|
|
||||||
// guaranteed to be valid from the pointer by io_slice_mut.
|
|
||||||
let res = if let Some(offset) = file_offset {
|
let res = if let Some(offset) = file_offset {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we trust the kernel not to write path the length given and the length is
|
||||||
|
// guaranteed to be valid from the pointer by io_slice_mut.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::preadv64(
|
libc::preadv64(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -170,6 +174,9 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we trust the kernel not to write path the length given and the length is
|
||||||
|
// guaranteed to be valid from the pointer by io_slice_mut.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::readv(
|
libc::readv(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -213,8 +220,9 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
vec: Vec<u8>,
|
vec: Vec<u8>,
|
||||||
) -> AsyncResult<(usize, Vec<u8>)> {
|
) -> AsyncResult<(usize, Vec<u8>)> {
|
||||||
loop {
|
loop {
|
||||||
// Safe because this will not modify any memory and we check the return value.
|
|
||||||
let res = if let Some(offset) = file_offset {
|
let res = if let Some(offset) = file_offset {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because this will not modify any memory and we check the return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::pwrite64(
|
libc::pwrite64(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -224,6 +232,8 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because this will not modify any memory and we check the return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::write(
|
libc::write(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -264,9 +274,10 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
.collect::<Vec<VolatileSlice>>();
|
.collect::<Vec<VolatileSlice>>();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Safe because we trust the kernel not to write path the length given and the length is
|
|
||||||
// guaranteed to be valid from the pointer by io_slice_mut.
|
|
||||||
let res = if let Some(offset) = file_offset {
|
let res = if let Some(offset) = file_offset {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we trust the kernel not to write path the length given and the length is
|
||||||
|
// guaranteed to be valid from the pointer by io_slice_mut.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::pwritev64(
|
libc::pwritev64(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -276,6 +287,9 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we trust the kernel not to write path the length given and the length is
|
||||||
|
// guaranteed to be valid from the pointer by io_slice_mut.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::writev(
|
libc::writev(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -302,8 +316,11 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
/// Sync all completed write operations to the backing storage.
|
/// Sync all completed write operations to the backing storage.
|
||||||
pub async fn fsync(&self) -> AsyncResult<()> {
|
pub async fn fsync(&self) -> AsyncResult<()> {
|
||||||
|
// SAFETY: the duped_fd is valid and return value is checked.
|
||||||
let ret = unsafe { libc::fsync(self.registered_source.duped_fd.as_raw_fd()) };
|
let ret = unsafe { libc::fsync(self.registered_source.duped_fd.as_raw_fd()) };
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -344,6 +361,7 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
/// Sync all data of completed write operations to the backing storage, avoiding updating extra
|
/// Sync all data of completed write operations to the backing storage, avoiding updating extra
|
||||||
/// metadata.
|
/// metadata.
|
||||||
pub async fn fdatasync(&self) -> AsyncResult<()> {
|
pub async fn fdatasync(&self) -> AsyncResult<()> {
|
||||||
|
// SAFETY: the duped_fd is valid and return value is checked.
|
||||||
let ret = unsafe { libc::fdatasync(self.registered_source.duped_fd.as_raw_fd()) };
|
let ret = unsafe { libc::fdatasync(self.registered_source.duped_fd.as_raw_fd()) };
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -158,15 +158,18 @@ impl From<Error> for io::Error {
|
||||||
static IS_URING_STABLE: Lazy<bool> = Lazy::new(|| {
|
static IS_URING_STABLE: Lazy<bool> = Lazy::new(|| {
|
||||||
let mut utsname = MaybeUninit::zeroed();
|
let mut utsname = MaybeUninit::zeroed();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because this will only modify `utsname` and we check the return value.
|
// Safe because this will only modify `utsname` and we check the return value.
|
||||||
let res = unsafe { libc::uname(utsname.as_mut_ptr()) };
|
let res = unsafe { libc::uname(utsname.as_mut_ptr()) };
|
||||||
if res < 0 {
|
if res < 0 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the kernel has initialized `utsname`.
|
// Safe because the kernel has initialized `utsname`.
|
||||||
let utsname = unsafe { utsname.assume_init() };
|
let utsname = unsafe { utsname.assume_init() };
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the pointer is valid and the kernel guarantees that this is a valid C string.
|
// Safe because the pointer is valid and the kernel guarantees that this is a valid C string.
|
||||||
let release = unsafe { CStr::from_ptr(utsname.release.as_ptr()) };
|
let release = unsafe { CStr::from_ptr(utsname.release.as_ptr()) };
|
||||||
|
|
||||||
|
@ -423,11 +426,10 @@ impl UringReactor {
|
||||||
raw: &Arc<RawExecutor<UringReactor>>,
|
raw: &Arc<RawExecutor<UringReactor>>,
|
||||||
fd: &F,
|
fd: &F,
|
||||||
) -> Result<RegisteredSource> {
|
) -> Result<RegisteredSource> {
|
||||||
let duped_fd = unsafe {
|
// SAFETY:
|
||||||
// Safe because duplicating an FD doesn't affect memory safety, and the dup'd FD
|
// Safe because duplicating an FD doesn't affect memory safety, and the dup'd FD
|
||||||
// will only be added to the poll loop.
|
// will only be added to the poll loop.
|
||||||
File::from_raw_fd(dup_fd(fd.as_raw_descriptor())?)
|
let duped_fd = unsafe { File::from_raw_fd(dup_fd(fd.as_raw_descriptor())?) };
|
||||||
};
|
|
||||||
|
|
||||||
Ok(RegisteredSource {
|
Ok(RegisteredSource {
|
||||||
tag: self
|
tag: self
|
||||||
|
@ -555,6 +557,7 @@ impl UringReactor {
|
||||||
let vslice = mem
|
let vslice = mem
|
||||||
.get_volatile_slice(mem_range)
|
.get_volatile_slice(mem_range)
|
||||||
.map_err(|_| Error::InvalidOffset)?;
|
.map_err(|_| Error::InvalidOffset)?;
|
||||||
|
// SAFETY:
|
||||||
// Safe because we guarantee that the memory pointed to by `iovecs` lives until the
|
// Safe because we guarantee that the memory pointed to by `iovecs` lives until the
|
||||||
// transaction is complete and the completion has been returned from `wait()`.
|
// transaction is complete and the completion has been returned from `wait()`.
|
||||||
Ok(unsafe { IoBufMut::from_raw_parts(vslice.as_mut_ptr(), vslice.size()) })
|
Ok(unsafe { IoBufMut::from_raw_parts(vslice.as_mut_ptr(), vslice.size()) })
|
||||||
|
@ -572,10 +575,11 @@ impl UringReactor {
|
||||||
let entry = ring.ops.vacant_entry();
|
let entry = ring.ops.vacant_entry();
|
||||||
let next_op_token = entry.key();
|
let next_op_token = entry.key();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because all the addresses are within the Memory that an Arc is kept for the
|
||||||
|
// duration to ensure the memory is valid while the kernel accesses it.
|
||||||
|
// Tested by `dont_drop_backing_mem_read` unit test.
|
||||||
unsafe {
|
unsafe {
|
||||||
// Safe because all the addresses are within the Memory that an Arc is kept for the
|
|
||||||
// duration to ensure the memory is valid while the kernel accesses it.
|
|
||||||
// Tested by `dont_drop_backing_mem_read` unit test.
|
|
||||||
self.ctx
|
self.ctx
|
||||||
.add_readv(
|
.add_readv(
|
||||||
iovecs,
|
iovecs,
|
||||||
|
@ -609,6 +613,7 @@ impl UringReactor {
|
||||||
let vslice = mem
|
let vslice = mem
|
||||||
.get_volatile_slice(mem_range)
|
.get_volatile_slice(mem_range)
|
||||||
.map_err(|_| Error::InvalidOffset)?;
|
.map_err(|_| Error::InvalidOffset)?;
|
||||||
|
// SAFETY:
|
||||||
// Safe because we guarantee that the memory pointed to by `iovecs` lives until the
|
// Safe because we guarantee that the memory pointed to by `iovecs` lives until the
|
||||||
// transaction is complete and the completion has been returned from `wait()`.
|
// transaction is complete and the completion has been returned from `wait()`.
|
||||||
Ok(unsafe { IoBufMut::from_raw_parts(vslice.as_mut_ptr(), vslice.size()) })
|
Ok(unsafe { IoBufMut::from_raw_parts(vslice.as_mut_ptr(), vslice.size()) })
|
||||||
|
@ -626,10 +631,11 @@ impl UringReactor {
|
||||||
let entry = ring.ops.vacant_entry();
|
let entry = ring.ops.vacant_entry();
|
||||||
let next_op_token = entry.key();
|
let next_op_token = entry.key();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because all the addresses are within the Memory that an Arc is kept for the
|
||||||
|
// duration to ensure the memory is valid while the kernel accesses it.
|
||||||
|
// Tested by `dont_drop_backing_mem_write` unit test.
|
||||||
unsafe {
|
unsafe {
|
||||||
// Safe because all the addresses are within the Memory that an Arc is kept for the
|
|
||||||
// duration to ensure the memory is valid while the kernel accesses it.
|
|
||||||
// Tested by `dont_drop_backing_mem_write` unit test.
|
|
||||||
self.ctx
|
self.ctx
|
||||||
.add_writev(
|
.add_writev(
|
||||||
iovecs,
|
iovecs,
|
||||||
|
@ -800,6 +806,7 @@ impl Drop for UringReactor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Used to dup the FDs passed to the executor so there is a guarantee they aren't closed while
|
// Used to dup the FDs passed to the executor so there is a guarantee they aren't closed while
|
||||||
// waiting in TLS to be added to the main polling context.
|
// waiting in TLS to be added to the main polling context.
|
||||||
unsafe fn dup_fd(fd: RawFd) -> Result<RawFd> {
|
unsafe fn dup_fd(fd: RawFd) -> Result<RawFd> {
|
||||||
|
|
|
@ -36,13 +36,14 @@ impl EventAsync {
|
||||||
descriptor: &dyn AsRawDescriptor,
|
descriptor: &dyn AsRawDescriptor,
|
||||||
ex: &Executor,
|
ex: &Executor,
|
||||||
) -> AsyncResult<EventAsync> {
|
) -> AsyncResult<EventAsync> {
|
||||||
// Safe because:
|
|
||||||
// a) the underlying Event should be validated by the caller.
|
|
||||||
// b) we do NOT take ownership of the underlying Event. If we did that would cause an early
|
|
||||||
// free (and later a double free @ the end of this scope). This is why we have to wrap
|
|
||||||
// it in ManuallyDrop.
|
|
||||||
// c) we own the clone that is produced exclusively, so it is safe to take ownership of it.
|
|
||||||
Self::new_without_reset(
|
Self::new_without_reset(
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because:
|
||||||
|
// a) the underlying Event should be validated by the caller.
|
||||||
|
// b) we do NOT take ownership of the underlying Event. If we did that would cause an early
|
||||||
|
// free (and later a double free @ the end of this scope). This is why we have to wrap
|
||||||
|
// it in ManuallyDrop.
|
||||||
|
// c) we own the clone that is produced exclusively, so it is safe to take ownership of it.
|
||||||
unsafe {
|
unsafe {
|
||||||
ManuallyDrop::new(Event::from_raw_descriptor(descriptor.as_raw_descriptor()))
|
ManuallyDrop::new(Event::from_raw_descriptor(descriptor.as_raw_descriptor()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@ impl HandleWrapper {
|
||||||
pub fn cancel_sync_io<T>(&mut self, ret: T) -> T {
|
pub fn cancel_sync_io<T>(&mut self, ret: T) -> T {
|
||||||
for handle in &self.handles {
|
for handle in &self.handles {
|
||||||
// There isn't much we can do if cancel fails.
|
// There isn't much we can do if cancel fails.
|
||||||
|
// SAFETY: trivially safe
|
||||||
if unsafe { CancelIoEx(handle.as_raw_descriptor(), null_mut()) } == 0 {
|
if unsafe { CancelIoEx(handle.as_raw_descriptor(), null_mut()) } == 0 {
|
||||||
warn!(
|
warn!(
|
||||||
"Cancel IO for handle:{:?} failed with {}",
|
"Cancel IO for handle:{:?} failed with {}",
|
||||||
|
@ -188,6 +189,7 @@ impl<F: AsRawDescriptor> Drop for HandleSource<F> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_thread_file(descriptors: Vec<Descriptor>) -> ManuallyDrop<File> {
|
fn get_thread_file(descriptors: Vec<Descriptor>) -> ManuallyDrop<File> {
|
||||||
|
// SAFETY: trivially safe
|
||||||
// Safe because all callers must exit *before* these handles will be closed (guaranteed by
|
// Safe because all callers must exit *before* these handles will be closed (guaranteed by
|
||||||
// HandleSource's Drop impl.).
|
// HandleSource's Drop impl.).
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -57,7 +57,7 @@ struct Port {
|
||||||
inner: RawDescriptor,
|
inner: RawDescriptor,
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Safety
|
// SAFETY:
|
||||||
// Safe because the Port is dropped before IoCompletionPort goes out of scope
|
// Safe because the Port is dropped before IoCompletionPort goes out of scope
|
||||||
unsafe impl Send for Port {}
|
unsafe impl Send for Port {}
|
||||||
|
|
||||||
|
@ -90,8 +90,10 @@ unsafe fn get_completion_status(
|
||||||
) -> io::Result<CompletionPacket> {
|
) -> io::Result<CompletionPacket> {
|
||||||
let mut bytes_transferred = 0;
|
let mut bytes_transferred = 0;
|
||||||
let mut completion_key = 0;
|
let mut completion_key = 0;
|
||||||
|
// SAFETY: trivially safe
|
||||||
let mut overlapped: *mut OVERLAPPED = unsafe { std::mem::zeroed() };
|
let mut overlapped: *mut OVERLAPPED = unsafe { std::mem::zeroed() };
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because:
|
// Safe because:
|
||||||
// 1. Memory of pointers passed is stack allocated and lives as long as the syscall.
|
// 1. Memory of pointers passed is stack allocated and lives as long as the syscall.
|
||||||
// 2. We check the error so we don't use invalid output values (e.g. overlapped).
|
// 2. We check the error so we don't use invalid output values (e.g. overlapped).
|
||||||
|
@ -133,7 +135,7 @@ unsafe fn get_completion_status(
|
||||||
unsafe fn poll(port: RawDescriptor) -> Result<Vec<CompletionPacket>> {
|
unsafe fn poll(port: RawDescriptor) -> Result<Vec<CompletionPacket>> {
|
||||||
let mut completion_packets = vec![];
|
let mut completion_packets = vec![];
|
||||||
completion_packets.push(
|
completion_packets.push(
|
||||||
// Safety: caller has ensured that the handle is valid and is for io completion port
|
// SAFETY: caller has ensured that the handle is valid and is for io completion port
|
||||||
unsafe {
|
unsafe {
|
||||||
get_completion_status(port, INFINITE)
|
get_completion_status(port, INFINITE)
|
||||||
.map_err(|e| Error::IocpOperationFailed(SysError::from(e)))?
|
.map_err(|e| Error::IocpOperationFailed(SysError::from(e)))?
|
||||||
|
@ -146,8 +148,9 @@ unsafe fn poll(port: RawDescriptor) -> Result<Vec<CompletionPacket>> {
|
||||||
// get detailed error information for each of the returned overlapped IO operations without
|
// get detailed error information for each of the returned overlapped IO operations without
|
||||||
// calling GetOverlappedResult. If we have to do that, then it's cheaper to just get each
|
// calling GetOverlappedResult. If we have to do that, then it's cheaper to just get each
|
||||||
// completion packet individually.
|
// completion packet individually.
|
||||||
// Safety: caller has ensured that the handle is valid and is for io completion port
|
|
||||||
while completion_packets.len() < ENTRIES_PER_POLL {
|
while completion_packets.len() < ENTRIES_PER_POLL {
|
||||||
|
// SAFETY:
|
||||||
|
// Safety: caller has ensured that the handle is valid and is for io completion port
|
||||||
match unsafe { get_completion_status(port, 0) } {
|
match unsafe { get_completion_status(port, 0) } {
|
||||||
Ok(pkt) => {
|
Ok(pkt) => {
|
||||||
completion_packets.push(pkt);
|
completion_packets.push(pkt);
|
||||||
|
@ -168,7 +171,7 @@ fn iocp_waiter_thread(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let port = port.lock();
|
let port = port.lock();
|
||||||
loop {
|
loop {
|
||||||
// Safety: caller has ensured that the handle is valid and is for io completion port
|
// SAFETY: caller has ensured that the handle is valid and is for io completion port
|
||||||
let packets = unsafe { poll(port.inner)? };
|
let packets = unsafe { poll(port.inner)? };
|
||||||
if !packets.is_empty() {
|
if !packets.is_empty() {
|
||||||
{
|
{
|
||||||
|
@ -265,6 +268,7 @@ impl IoCompletionPort {
|
||||||
|
|
||||||
/// Posts a completion packet to the IO completion port.
|
/// Posts a completion packet to the IO completion port.
|
||||||
pub fn post_status(&self, bytes_transferred: u32, completion_key: usize) -> Result<()> {
|
pub fn post_status(&self, bytes_transferred: u32, completion_key: usize) -> Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the IOCP handle is valid.
|
// Safe because the IOCP handle is valid.
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
PostQueuedCompletionStatus(
|
PostQueuedCompletionStatus(
|
||||||
|
@ -296,11 +300,12 @@ impl IoCompletionPort {
|
||||||
let mut overlapped_entries: SmallVec<[OVERLAPPED_ENTRY; ENTRIES_PER_POLL]> =
|
let mut overlapped_entries: SmallVec<[OVERLAPPED_ENTRY; ENTRIES_PER_POLL]> =
|
||||||
smallvec!(OVERLAPPED_ENTRY::default(); ENTRIES_PER_POLL);
|
smallvec!(OVERLAPPED_ENTRY::default(); ENTRIES_PER_POLL);
|
||||||
|
|
||||||
|
let mut entries_removed: ULONG = 0;
|
||||||
|
// SAFETY:
|
||||||
// Safe because:
|
// Safe because:
|
||||||
// 1. IOCP is guaranteed to exist by self.
|
// 1. IOCP is guaranteed to exist by self.
|
||||||
// 2. Memory of pointers passed is stack allocated and lives as long as the syscall.
|
// 2. Memory of pointers passed is stack allocated and lives as long as the syscall.
|
||||||
// 3. We check the error so we don't use invalid output values (e.g. overlapped).
|
// 3. We check the error so we don't use invalid output values (e.g. overlapped).
|
||||||
let mut entries_removed: ULONG = 0;
|
|
||||||
let success = unsafe {
|
let success = unsafe {
|
||||||
GetQueuedCompletionStatusEx(
|
GetQueuedCompletionStatusEx(
|
||||||
self.port.as_raw_descriptor(),
|
self.port.as_raw_descriptor(),
|
||||||
|
@ -352,7 +357,7 @@ impl IoCompletionPort {
|
||||||
|
|
||||||
/// Waits for completion events to arrive & returns the completion keys.
|
/// Waits for completion events to arrive & returns the completion keys.
|
||||||
pub fn poll_unthreaded(&self) -> Result<SmallVec<[CompletionPacket; ENTRIES_PER_POLL]>> {
|
pub fn poll_unthreaded(&self) -> Result<SmallVec<[CompletionPacket; ENTRIES_PER_POLL]>> {
|
||||||
// Safety: safe because port is in scope for the duration of the call.
|
// SAFETY: safe because port is in scope for the duration of the call.
|
||||||
let packets = unsafe { poll(self.port.as_raw_descriptor())? };
|
let packets = unsafe { poll(self.port.as_raw_descriptor())? };
|
||||||
let mut completion_packets = SmallVec::with_capacity(ENTRIES_PER_POLL);
|
let mut completion_packets = SmallVec::with_capacity(ENTRIES_PER_POLL);
|
||||||
for pkt in packets {
|
for pkt in packets {
|
||||||
|
@ -398,6 +403,7 @@ impl IoCompletionPort {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bytes_transferred = 0;
|
let mut bytes_transferred = 0;
|
||||||
|
// SAFETY: trivially safe with return value checked
|
||||||
let success = unsafe {
|
let success = unsafe {
|
||||||
GetOverlappedResult(
|
GetOverlappedResult(
|
||||||
entry.lpCompletionKey as RawDescriptor,
|
entry.lpCompletionKey as RawDescriptor,
|
||||||
|
@ -442,10 +448,11 @@ fn create_iocp(
|
||||||
None => null_mut(),
|
None => null_mut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Safe because:
|
|
||||||
// 1. The file handle is open because we have a reference to it.
|
|
||||||
// 2. The existing IOCP (if applicable) is valid.
|
|
||||||
let port =
|
let port =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because:
|
||||||
|
// 1. The file handle is open because we have a reference to it.
|
||||||
|
// 2. The existing IOCP (if applicable) is valid.
|
||||||
unsafe { CreateIoCompletionPort(raw_file, raw_existing_iocp, completion_key, concurrency) };
|
unsafe { CreateIoCompletionPort(raw_file, raw_existing_iocp, completion_key, concurrency) };
|
||||||
|
|
||||||
if port.is_null() {
|
if port.is_null() {
|
||||||
|
@ -455,6 +462,7 @@ fn create_iocp(
|
||||||
if existing_iocp.is_some() {
|
if existing_iocp.is_some() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY:
|
||||||
// Safe because:
|
// Safe because:
|
||||||
// 1. We are creating a new IOCP.
|
// 1. We are creating a new IOCP.
|
||||||
// 2. We exclusively own the handle.
|
// 2. We exclusively own the handle.
|
||||||
|
@ -502,6 +510,8 @@ mod tests {
|
||||||
|
|
||||||
iocp.register_descriptor(&f).unwrap();
|
iocp.register_descriptor(&f).unwrap();
|
||||||
let buf = [0u8; 16];
|
let buf = [0u8; 16];
|
||||||
|
// SAFETY: Safe given file is valid, buffers are allocated and initialized and return value
|
||||||
|
// is checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
base::windows::write_file(&f, buf.as_ptr(), buf.len(), Some(&mut overlapped)).unwrap()
|
base::windows::write_file(&f, buf.as_ptr(), buf.len(), Some(&mut overlapped)).unwrap()
|
||||||
};
|
};
|
||||||
|
@ -526,6 +536,8 @@ mod tests {
|
||||||
|
|
||||||
iocp.register_descriptor(&f).unwrap();
|
iocp.register_descriptor(&f).unwrap();
|
||||||
let buf = [0u8; 16];
|
let buf = [0u8; 16];
|
||||||
|
// SAFETY: Safe given file is valid, buffers are allocated and initialized and return value
|
||||||
|
// is checked.
|
||||||
unsafe {
|
unsafe {
|
||||||
base::windows::write_file(&f, buf.as_ptr(), buf.len(), Some(&mut overlapped)).unwrap()
|
base::windows::write_file(&f, buf.as_ptr(), buf.len(), Some(&mut overlapped)).unwrap()
|
||||||
};
|
};
|
||||||
|
|
|
@ -106,6 +106,7 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SAFETY:
|
||||||
/// Safety requirements:
|
/// Safety requirements:
|
||||||
/// Same as base::windows::read_file.
|
/// Same as base::windows::read_file.
|
||||||
unsafe fn read(
|
unsafe fn read(
|
||||||
|
@ -119,6 +120,7 @@ unsafe fn read(
|
||||||
.map_err(|e| AsyncError::OverlappedSource(Error::StdIoReadError(e)))
|
.map_err(|e| AsyncError::OverlappedSource(Error::StdIoReadError(e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SAFETY:
|
||||||
/// Safety requirements:
|
/// Safety requirements:
|
||||||
/// Same as base::windows::write_file.
|
/// Same as base::windows::write_file.
|
||||||
unsafe fn write(
|
unsafe fn write(
|
||||||
|
@ -147,6 +149,7 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
let overlapped = create_overlapped(file_offset, None);
|
let overlapped = create_overlapped(file_offset, None);
|
||||||
let mut overlapped_op = self.reg_source.register_overlapped_operation(overlapped)?;
|
let mut overlapped_op = self.reg_source.register_overlapped_operation(overlapped)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass a pointer to a valid vec and that same vector's length.
|
// Safe because we pass a pointer to a valid vec and that same vector's length.
|
||||||
unsafe {
|
unsafe {
|
||||||
read(
|
read(
|
||||||
|
@ -192,6 +195,7 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
AsyncError::OverlappedSource(Error::BackingMemoryVolatileSliceFetchFailed(e))
|
AsyncError::OverlappedSource(Error::BackingMemoryVolatileSliceFetchFailed(e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we're passing a volatile slice (valid ptr), and the size of the memory region it refers to.
|
// Safe because we're passing a volatile slice (valid ptr), and the size of the memory region it refers to.
|
||||||
unsafe {
|
unsafe {
|
||||||
read(
|
read(
|
||||||
|
@ -235,6 +239,7 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
let overlapped = create_overlapped(file_offset, None);
|
let overlapped = create_overlapped(file_offset, None);
|
||||||
let mut overlapped_op = self.reg_source.register_overlapped_operation(overlapped)?;
|
let mut overlapped_op = self.reg_source.register_overlapped_operation(overlapped)?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass a pointer to a valid vec and that same vector's length.
|
// Safe because we pass a pointer to a valid vec and that same vector's length.
|
||||||
unsafe {
|
unsafe {
|
||||||
write(
|
write(
|
||||||
|
@ -281,6 +286,7 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
AsyncError::OverlappedSource(Error::BackingMemoryVolatileSliceFetchFailed(e))
|
AsyncError::OverlappedSource(Error::BackingMemoryVolatileSliceFetchFailed(e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we're passing a volatile slice (valid ptr), and the size of the memory region it refers to.
|
// Safe because we're passing a volatile slice (valid ptr), and the size of the memory region it refers to.
|
||||||
unsafe {
|
unsafe {
|
||||||
write(
|
write(
|
||||||
|
@ -313,6 +319,7 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
// SAFETY:
|
||||||
// Safe because self.source lives as long as file.
|
// Safe because self.source lives as long as file.
|
||||||
let file = ManuallyDrop::new(unsafe {
|
let file = ManuallyDrop::new(unsafe {
|
||||||
File::from_raw_descriptor(self.source.as_raw_descriptor())
|
File::from_raw_descriptor(self.source.as_raw_descriptor())
|
||||||
|
@ -335,6 +342,7 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
// SAFETY:
|
||||||
// Safe because self.source lives as long as file.
|
// Safe because self.source lives as long as file.
|
||||||
let mut file = ManuallyDrop::new(unsafe {
|
let mut file = ManuallyDrop::new(unsafe {
|
||||||
File::from_raw_descriptor(self.source.as_raw_descriptor())
|
File::from_raw_descriptor(self.source.as_raw_descriptor())
|
||||||
|
@ -348,6 +356,7 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
|
|
||||||
/// Sync all completed write operations to the backing storage.
|
/// Sync all completed write operations to the backing storage.
|
||||||
pub async fn fsync(&self) -> AsyncResult<()> {
|
pub async fn fsync(&self) -> AsyncResult<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because self.source lives at least as long as the blocking pool thread. Note that
|
// Safe because self.source lives at least as long as the blocking pool thread. Note that
|
||||||
// if the blocking pool stalls and shutdown fails, the thread could outlive the file;
|
// if the blocking pool stalls and shutdown fails, the thread could outlive the file;
|
||||||
// however, this would mean things are already badly broken and we have a similar risk in
|
// however, this would mean things are already badly broken and we have a similar risk in
|
||||||
|
|
|
@ -90,6 +90,7 @@ where
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
match inner.wait_state {
|
match inner.wait_state {
|
||||||
WaitState::New => {
|
WaitState::New => {
|
||||||
|
// SAFETY:
|
||||||
// Safe because:
|
// Safe because:
|
||||||
// a) the callback only runs when WaitForHandle is alive (we cancel it on
|
// a) the callback only runs when WaitForHandle is alive (we cancel it on
|
||||||
// drop).
|
// drop).
|
||||||
|
@ -128,6 +129,7 @@ where
|
||||||
WaitState::Woken => {
|
WaitState::Woken => {
|
||||||
inner.wait_state = WaitState::Finished;
|
inner.wait_state = WaitState::Finished;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because:
|
// Safe because:
|
||||||
// a) we know a wait was registered and hasn't been unregistered yet.
|
// a) we know a wait was registered and hasn't been unregistered yet.
|
||||||
// b) the callback is not queued because we set WT_EXECUTEONLYONCE, and we know
|
// b) the callback is not queued because we set WT_EXECUTEONLYONCE, and we know
|
||||||
|
@ -161,13 +163,14 @@ where
|
||||||
(inner.wait_state, inner.wait_object)
|
(inner.wait_state, inner.wait_object)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Safe because self.descriptor is valid in any state except New or Finished.
|
|
||||||
//
|
|
||||||
// Note: this method call is critical for supplying the safety guarantee relied upon by
|
|
||||||
// wait_for_handle_waker. Upon return, it ensures that wait_for_handle_waker is not running
|
|
||||||
// and won't be scheduled again, which makes it safe to drop self.inner_for_callback
|
|
||||||
// (wait_for_handle_waker has a non owning pointer to self.inner_for_callback).
|
|
||||||
if current_state != WaitState::New && current_state != WaitState::Finished {
|
if current_state != WaitState::New && current_state != WaitState::Finished {
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because self.descriptor is valid in any state except New or Finished.
|
||||||
|
//
|
||||||
|
// Note: this method call is critical for supplying the safety guarantee relied upon by
|
||||||
|
// wait_for_handle_waker. Upon return, it ensures that wait_for_handle_waker is not running
|
||||||
|
// and won't be scheduled again, which makes it safe to drop self.inner_for_callback
|
||||||
|
// (wait_for_handle_waker has a non owning pointer to self.inner_for_callback).
|
||||||
unsafe { unregister_wait(wait_object) }
|
unsafe { unregister_wait(wait_object) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,8 @@ unsafe fn drop_weak_raw<W: WeakWake>(data: *const ()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_waker<W: WeakWake>(w: Weak<W>) -> Waker {
|
pub(crate) fn new_waker<W: WeakWake>(w: Weak<W>) -> Waker {
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe {
|
unsafe {
|
||||||
Waker::from_raw(RawWaker::new(
|
Waker::from_raw(RawWaker::new(
|
||||||
w.into_raw() as *const (),
|
w.into_raw() as *const (),
|
||||||
|
|
|
@ -46,6 +46,7 @@ impl StaticString {
|
||||||
|
|
||||||
// Safety: pointers are safe to send between threads.
|
// Safety: pointers are safe to send between threads.
|
||||||
unsafe impl Send for StaticString {}
|
unsafe impl Send for StaticString {}
|
||||||
|
// SAFETY:
|
||||||
// Safe to share across threads, because `register` is protected by a lock and strings inserted
|
// Safe to share across threads, because `register` is protected by a lock and strings inserted
|
||||||
// are never removed.
|
// are never removed.
|
||||||
unsafe impl Sync for StaticString {}
|
unsafe impl Sync for StaticString {}
|
||||||
|
|
|
@ -460,6 +460,7 @@ pub unsafe extern "C" fn crosvm_client_usb_attach(
|
||||||
|
|
||||||
if let Ok(UsbControlResult::Ok { port }) = do_usb_attach(socket_path, dev_path) {
|
if let Ok(UsbControlResult::Ok { port }) = do_usb_attach(socket_path, dev_path) {
|
||||||
if !out_port.is_null() {
|
if !out_port.is_null() {
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe { *out_port = port };
|
unsafe { *out_port = port };
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -595,8 +596,11 @@ pub unsafe extern "C" fn crosvm_client_modify_battery(
|
||||||
if battery_type.is_null() || property.is_null() || target.is_null() {
|
if battery_type.is_null() || property.is_null() || target.is_null() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// SAFETY: trivially safe
|
||||||
let battery_type = unsafe { CStr::from_ptr(battery_type) };
|
let battery_type = unsafe { CStr::from_ptr(battery_type) };
|
||||||
|
// SAFETY: trivially safe
|
||||||
let property = unsafe { CStr::from_ptr(property) };
|
let property = unsafe { CStr::from_ptr(property) };
|
||||||
|
// SAFETY: trivially safe
|
||||||
let target = unsafe { CStr::from_ptr(target) };
|
let target = unsafe { CStr::from_ptr(target) };
|
||||||
|
|
||||||
do_modify_battery(
|
do_modify_battery(
|
||||||
|
|
|
@ -162,6 +162,7 @@ fn proto_error_to_int(e: protobuf::Error) -> c_int {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_cast<F: FromRawFd>(f: File) -> F {
|
fn fd_cast<F: FromRawFd>(f: File) -> F {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we are transferring unique ownership.
|
// Safe because we are transferring unique ownership.
|
||||||
unsafe { F::from_raw_fd(f.into_raw_fd()) }
|
unsafe { F::from_raw_fd(f.into_raw_fd()) }
|
||||||
}
|
}
|
||||||
|
@ -533,14 +534,20 @@ impl crosvm {
|
||||||
match route.kind {
|
match route.kind {
|
||||||
CROSVM_IRQ_ROUTE_IRQCHIP => {
|
CROSVM_IRQ_ROUTE_IRQCHIP => {
|
||||||
let irqchip = entry.mut_irqchip();
|
let irqchip = entry.mut_irqchip();
|
||||||
|
// SAFETY:
|
||||||
// Safe because route.kind indicates which union field is valid.
|
// Safe because route.kind indicates which union field is valid.
|
||||||
irqchip.irqchip = unsafe { route.route.irqchip }.irqchip;
|
irqchip.irqchip = unsafe { route.route.irqchip }.irqchip;
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because route.kind indicates which union field is valid.
|
||||||
irqchip.pin = unsafe { route.route.irqchip }.pin;
|
irqchip.pin = unsafe { route.route.irqchip }.pin;
|
||||||
}
|
}
|
||||||
CROSVM_IRQ_ROUTE_MSI => {
|
CROSVM_IRQ_ROUTE_MSI => {
|
||||||
let msi = entry.mut_msi();
|
let msi = entry.mut_msi();
|
||||||
|
// SAFETY:
|
||||||
// Safe because route.kind indicates which union field is valid.
|
// Safe because route.kind indicates which union field is valid.
|
||||||
msi.address = unsafe { route.route.msi }.address;
|
msi.address = unsafe { route.route.msi }.address;
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because route.kind indicates which union field is valid.
|
||||||
msi.data = unsafe { route.route.msi }.data;
|
msi.data = unsafe { route.route.msi }.data;
|
||||||
}
|
}
|
||||||
_ => return Err(EINVAL),
|
_ => return Err(EINVAL),
|
||||||
|
|
|
@ -105,12 +105,14 @@ impl KvmKernelIrqChip {
|
||||||
};
|
};
|
||||||
dist_attr.attr = dist_attr_attr;
|
dist_attr.attr = dist_attr_attr;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we allocated the struct that's being passed in
|
// Safe because we allocated the struct that's being passed in
|
||||||
let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR(), &cpu_redist_attr) };
|
let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR(), &cpu_redist_attr) };
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we allocated the struct that's being passed in
|
// Safe because we allocated the struct that's being passed in
|
||||||
let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR(), &dist_attr) };
|
let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR(), &dist_attr) };
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
|
@ -126,6 +128,7 @@ impl KvmKernelIrqChip {
|
||||||
addr: nr_irqs_ptr as u64,
|
addr: nr_irqs_ptr as u64,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
|
// SAFETY:
|
||||||
// Safe because we allocated the struct that's being passed in
|
// Safe because we allocated the struct that's being passed in
|
||||||
let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR(), &nr_irqs_attr) };
|
let ret = unsafe { ioctl_with_ref(&vgic, KVM_SET_DEVICE_ATTR(), &nr_irqs_attr) };
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
|
@ -178,6 +181,7 @@ impl IrqChipAArch64 for KvmKernelIrqChip {
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we allocated the struct that's being passed in
|
// Safe because we allocated the struct that's being passed in
|
||||||
let ret = unsafe { ioctl_with_ref(&self.vgic, KVM_SET_DEVICE_ATTR(), &init_gic_attr) };
|
let ret = unsafe { ioctl_with_ref(&self.vgic, KVM_SET_DEVICE_ATTR(), &init_gic_attr) };
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
|
|
|
@ -361,22 +361,25 @@ fn gfn_to_dtt_pte(
|
||||||
|
|
||||||
mem.get_host_address(GuestAddress(pt_gpa + index))
|
mem.get_host_address(GuestAddress(pt_gpa + index))
|
||||||
.context(Error::GetDTTEntry)?
|
.context(Error::GetDTTEntry)?
|
||||||
} else {
|
} else if gfn > dtt_iter.gfn {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we checked that dtt_iter.ptr is valid and that the dtt_pte
|
// Safe because we checked that dtt_iter.ptr is valid and that the dtt_pte
|
||||||
// for gfn lies on the same dtt page as the dtt_pte for dtt_iter.gfn, which
|
// for gfn lies on the same dtt page as the dtt_pte for dtt_iter.gfn, which
|
||||||
// means the calculated ptr will point to the same page as dtt_iter.ptr
|
// means the calculated ptr will point to the same page as dtt_iter.ptr
|
||||||
if gfn > dtt_iter.gfn {
|
unsafe {
|
||||||
unsafe {
|
dtt_iter
|
||||||
dtt_iter
|
.ptr
|
||||||
.ptr
|
.add(mem::size_of::<AtomicU32>() * (gfn - dtt_iter.gfn) as usize)
|
||||||
.add(mem::size_of::<AtomicU32>() * (gfn - dtt_iter.gfn) as usize)
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
// SAFETY:
|
||||||
unsafe {
|
// Safe because we checked that dtt_iter.ptr is valid and that the dtt_pte
|
||||||
dtt_iter
|
// for gfn lies on the same dtt page as the dtt_pte for dtt_iter.gfn, which
|
||||||
.ptr
|
// means the calculated ptr will point to the same page as dtt_iter.ptr
|
||||||
.sub(mem::size_of::<AtomicU32>() * (dtt_iter.gfn - gfn) as usize)
|
unsafe {
|
||||||
}
|
dtt_iter
|
||||||
|
.ptr
|
||||||
|
.sub(mem::size_of::<AtomicU32>() * (dtt_iter.gfn - gfn) as usize)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -403,6 +406,7 @@ fn pin_page(
|
||||||
.get_host_address_range(GuestAddress(gpa), PAGE_SIZE_4K as usize)
|
.get_host_address_range(GuestAddress(gpa), PAGE_SIZE_4K as usize)
|
||||||
.context("failed to get host address")? as u64;
|
.context("failed to get host address")? as u64;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because ptr is valid and guaranteed by the gfn_to_dtt_pte.
|
// Safe because ptr is valid and guaranteed by the gfn_to_dtt_pte.
|
||||||
// Test PINNED flag
|
// Test PINNED flag
|
||||||
if (unsafe { (*leaf_entry).load(Ordering::Relaxed) } & DTTE_PINNED_FLAG) != 0 {
|
if (unsafe { (*leaf_entry).load(Ordering::Relaxed) } & DTTE_PINNED_FLAG) != 0 {
|
||||||
|
@ -410,9 +414,11 @@ fn pin_page(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the gpa is valid from the gfn_to_dtt_pte and the host_addr
|
// Safe because the gpa is valid from the gfn_to_dtt_pte and the host_addr
|
||||||
// is guaranteed by MemoryMapping interface.
|
// is guaranteed by MemoryMapping interface.
|
||||||
if unsafe { vfio_map(vfio_container, gpa, PAGE_SIZE_4K, host_addr) } {
|
if unsafe { vfio_map(vfio_container, gpa, PAGE_SIZE_4K, host_addr) } {
|
||||||
|
// SAFETY:
|
||||||
// Safe because ptr is valid and guaranteed by the gfn_to_dtt_pte.
|
// Safe because ptr is valid and guaranteed by the gfn_to_dtt_pte.
|
||||||
// set PINNED flag
|
// set PINNED flag
|
||||||
unsafe { (*leaf_entry).fetch_or(DTTE_PINNED_FLAG, Ordering::SeqCst) };
|
unsafe { (*leaf_entry).fetch_or(DTTE_PINNED_FLAG, Ordering::SeqCst) };
|
||||||
|
@ -467,6 +473,7 @@ fn unpin_page(
|
||||||
};
|
};
|
||||||
|
|
||||||
if force {
|
if force {
|
||||||
|
// SAFETY:
|
||||||
// Safe because leaf_entry is valid and guaranteed by the gfn_to_dtt_pte.
|
// Safe because leaf_entry is valid and guaranteed by the gfn_to_dtt_pte.
|
||||||
// This case is for balloon to evict pages so these pages should
|
// This case is for balloon to evict pages so these pages should
|
||||||
// already been locked by balloon and no device driver in VM is
|
// already been locked by balloon and no device driver in VM is
|
||||||
|
@ -475,6 +482,7 @@ fn unpin_page(
|
||||||
unsafe { (*leaf_entry).fetch_and(!DTTE_ACCESSED_FLAG, Ordering::SeqCst) };
|
unsafe { (*leaf_entry).fetch_and(!DTTE_ACCESSED_FLAG, Ordering::SeqCst) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because leaf_entry is valid and guaranteed by the gfn_to_dtt_pte.
|
// Safe because leaf_entry is valid and guaranteed by the gfn_to_dtt_pte.
|
||||||
if let Err(entry) = unsafe {
|
if let Err(entry) = unsafe {
|
||||||
(*leaf_entry).compare_exchange(DTTE_PINNED_FLAG, 0, Ordering::SeqCst, Ordering::SeqCst)
|
(*leaf_entry).compare_exchange(DTTE_PINNED_FLAG, 0, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
|
@ -488,6 +496,7 @@ fn unpin_page(
|
||||||
UnpinResult::NotPinned
|
UnpinResult::NotPinned
|
||||||
} else {
|
} else {
|
||||||
if !force {
|
if !force {
|
||||||
|
// SAFETY:
|
||||||
// Safe because leaf_entry is valid and guaranteed by the gfn_to_dtt_pte.
|
// Safe because leaf_entry is valid and guaranteed by the gfn_to_dtt_pte.
|
||||||
// The ACCESSED_FLAG is set by the guest if guest requires DMA map for
|
// The ACCESSED_FLAG is set by the guest if guest requires DMA map for
|
||||||
// this page. It represents whether or not this page is touched by the
|
// this page. It represents whether or not this page is touched by the
|
||||||
|
@ -526,6 +535,7 @@ fn unpin_page(
|
||||||
if vfio_unmap(vfio_container, gpa, PAGE_SIZE_4K) {
|
if vfio_unmap(vfio_container, gpa, PAGE_SIZE_4K) {
|
||||||
UnpinResult::Unpinned
|
UnpinResult::Unpinned
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY:
|
||||||
// Safe because leaf_entry is valid and guaranteed by the gfn_to_dtt_pte.
|
// Safe because leaf_entry is valid and guaranteed by the gfn_to_dtt_pte.
|
||||||
// make sure the pinned flag is set
|
// make sure the pinned flag is set
|
||||||
unsafe { (*leaf_entry).fetch_or(DTTE_PINNED_FLAG, Ordering::SeqCst) };
|
unsafe { (*leaf_entry).fetch_or(DTTE_PINNED_FLAG, Ordering::SeqCst) };
|
||||||
|
|
|
@ -272,6 +272,7 @@ impl VfioPlatformDevice {
|
||||||
Err(_e) => break,
|
Err(_e) => break,
|
||||||
};
|
};
|
||||||
let host = mmap.as_ptr() as u64;
|
let host = mmap.as_ptr() as u64;
|
||||||
|
// SAFETY:
|
||||||
// Safe because the given guest_map_start is valid guest bar address. and
|
// Safe because the given guest_map_start is valid guest bar address. and
|
||||||
// the host pointer is correct and valid guaranteed by MemoryMapping interface.
|
// the host pointer is correct and valid guaranteed by MemoryMapping interface.
|
||||||
match unsafe {
|
match unsafe {
|
||||||
|
|
|
@ -291,6 +291,7 @@ impl ChildProcIntf {
|
||||||
if let Some(swap_device_uffd_sender) = swap_device_uffd_sender {
|
if let Some(swap_device_uffd_sender) = swap_device_uffd_sender {
|
||||||
if let Err(e) = swap_device_uffd_sender.on_process_forked() {
|
if let Err(e) = swap_device_uffd_sender.on_process_forked() {
|
||||||
error!("failed to SwapController::on_process_forked: {:?}", e);
|
error!("failed to SwapController::on_process_forked: {:?}", e);
|
||||||
|
// SAFETY:
|
||||||
// exit() is trivially safe.
|
// exit() is trivially safe.
|
||||||
unsafe { libc::exit(1) };
|
unsafe { libc::exit(1) };
|
||||||
}
|
}
|
||||||
|
@ -305,6 +306,7 @@ impl ChildProcIntf {
|
||||||
// TODO(crbug.com/992494): Remove this once device shutdown ordering is clearly
|
// TODO(crbug.com/992494): Remove this once device shutdown ordering is clearly
|
||||||
// defined.
|
// defined.
|
||||||
//
|
//
|
||||||
|
// SAFETY:
|
||||||
// exit() is trivially safe.
|
// exit() is trivially safe.
|
||||||
// ! Never returns
|
// ! Never returns
|
||||||
unsafe { libc::exit(0) };
|
unsafe { libc::exit(0) };
|
||||||
|
|
|
@ -256,6 +256,8 @@ mod tests {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// TODO(b/315998194): Add safety comment
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe {
|
unsafe {
|
||||||
// Check that serial output is sent to the pipe
|
// Check that serial output is sent to the pipe
|
||||||
device.write(serial_bus_address(DATA), &[b'T']);
|
device.write(serial_bus_address(DATA), &[b'T']);
|
||||||
|
|
|
@ -23,6 +23,7 @@ pub use calibrate::*;
|
||||||
pub use cpuid::*;
|
pub use cpuid::*;
|
||||||
|
|
||||||
fn rdtsc_safe() -> u64 {
|
fn rdtsc_safe() -> u64 {
|
||||||
|
// SAFETY:
|
||||||
// Safe because _rdtsc takes no arguments
|
// Safe because _rdtsc takes no arguments
|
||||||
unsafe { _rdtsc() }
|
unsafe { _rdtsc() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -450,6 +450,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rdtsc_frequency_higher_than_u32() -> u64 {
|
fn rdtsc_frequency_higher_than_u32() -> u64 {
|
||||||
|
// SAFETY: trivially safe
|
||||||
unsafe { _rdtsc() }.wrapping_mul(1000)
|
unsafe { _rdtsc() }.wrapping_mul(1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,6 +472,7 @@ mod tests {
|
||||||
fn test_offset_identification_core_0() {
|
fn test_offset_identification_core_0() {
|
||||||
fn rdtsc_with_core_0_offset_by_100_000() -> u64 {
|
fn rdtsc_with_core_0_offset_by_100_000() -> u64 {
|
||||||
let mut id = 0u32;
|
let mut id = 0u32;
|
||||||
|
// SAFETY: trivially safe
|
||||||
let mut value = unsafe { __rdtscp(&mut id as *mut u32) };
|
let mut value = unsafe { __rdtscp(&mut id as *mut u32) };
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
value += 100_000;
|
value += 100_000;
|
||||||
|
@ -513,6 +515,7 @@ mod tests {
|
||||||
fn test_offset_identification_core_1() {
|
fn test_offset_identification_core_1() {
|
||||||
fn rdtsc_with_core_1_offset_by_100_000() -> u64 {
|
fn rdtsc_with_core_1_offset_by_100_000() -> u64 {
|
||||||
let mut id = 0u32;
|
let mut id = 0u32;
|
||||||
|
// SAFETY: trivially safe
|
||||||
let mut value = unsafe { __rdtscp(&mut id as *mut u32) };
|
let mut value = unsafe { __rdtscp(&mut id as *mut u32) };
|
||||||
if id == 1 {
|
if id == 1 {
|
||||||
value += 100_000;
|
value += 100_000;
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub type CpuidCountFn = unsafe fn(u32, u32) -> CpuidResult;
|
||||||
/// combination. `std::arch::x86_64::__cpuid_count` may be used to provide the CPUID information
|
/// combination. `std::arch::x86_64::__cpuid_count` may be used to provide the CPUID information
|
||||||
/// from the host.
|
/// from the host.
|
||||||
pub fn tsc_frequency_cpuid(cpuid_count: CpuidCountFn) -> Option<hypervisor::CpuIdEntry> {
|
pub fn tsc_frequency_cpuid(cpuid_count: CpuidCountFn) -> Option<hypervisor::CpuIdEntry> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass 0 and 0 for this call and the host supports the `cpuid` instruction.
|
// Safe because we pass 0 and 0 for this call and the host supports the `cpuid` instruction.
|
||||||
let result = unsafe { cpuid_count(0, 0) };
|
let result = unsafe { cpuid_count(0, 0) };
|
||||||
if result.eax < 0x15 {
|
if result.eax < 0x15 {
|
||||||
|
@ -35,6 +36,7 @@ pub fn tsc_frequency_cpuid(cpuid_count: CpuidCountFn) -> Option<hypervisor::CpuI
|
||||||
edx: 0,
|
edx: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
// SAFETY:
|
||||||
// Safe because we pass 0 and 0 for this call and the host supports the `cpuid` instruction.
|
// Safe because we pass 0 and 0 for this call and the host supports the `cpuid` instruction.
|
||||||
tsc_freq.cpuid = unsafe { cpuid_count(tsc_freq.function, tsc_freq.index) };
|
tsc_freq.cpuid = unsafe { cpuid_count(tsc_freq.function, tsc_freq.index) };
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ pub fn tsc_frequency_cpuid(cpuid_count: CpuidCountFn) -> Option<hypervisor::CpuI
|
||||||
// The core crystal frequency is missing. Old kernels (<5.3) don't try to derive it from the
|
// The core crystal frequency is missing. Old kernels (<5.3) don't try to derive it from the
|
||||||
// CPU base clock speed. Here, we essentially implement
|
// CPU base clock speed. Here, we essentially implement
|
||||||
// https://lore.kernel.org/patchwork/patch/1064690/ so that old kernels can calibrate TSC.
|
// https://lore.kernel.org/patchwork/patch/1064690/ so that old kernels can calibrate TSC.
|
||||||
|
// SAFETY:
|
||||||
// Safe because the host supports `cpuid` instruction.
|
// Safe because the host supports `cpuid` instruction.
|
||||||
let cpu_clock = unsafe {
|
let cpu_clock = unsafe {
|
||||||
// 0x16 is the base clock frequency leaf.
|
// 0x16 is the base clock frequency leaf.
|
||||||
|
|
|
@ -724,25 +724,45 @@ pub unsafe trait TrbCast: FromBytes + AsBytes + TypedTrb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for Trb {}
|
unsafe impl TrbCast for Trb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for NormalTrb {}
|
unsafe impl TrbCast for NormalTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for SetupStageTrb {}
|
unsafe impl TrbCast for SetupStageTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for DataStageTrb {}
|
unsafe impl TrbCast for DataStageTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for StatusStageTrb {}
|
unsafe impl TrbCast for StatusStageTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for IsochTrb {}
|
unsafe impl TrbCast for IsochTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for LinkTrb {}
|
unsafe impl TrbCast for LinkTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for EventDataTrb {}
|
unsafe impl TrbCast for EventDataTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for NoopTrb {}
|
unsafe impl TrbCast for NoopTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for DisableSlotCommandTrb {}
|
unsafe impl TrbCast for DisableSlotCommandTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for AddressDeviceCommandTrb {}
|
unsafe impl TrbCast for AddressDeviceCommandTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for ConfigureEndpointCommandTrb {}
|
unsafe impl TrbCast for ConfigureEndpointCommandTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for EvaluateContextCommandTrb {}
|
unsafe impl TrbCast for EvaluateContextCommandTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for ResetEndpointCommandTrb {}
|
unsafe impl TrbCast for ResetEndpointCommandTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for StopEndpointCommandTrb {}
|
unsafe impl TrbCast for StopEndpointCommandTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for SetTRDequeuePointerCommandTrb {}
|
unsafe impl TrbCast for SetTRDequeuePointerCommandTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for ResetDeviceCommandTrb {}
|
unsafe impl TrbCast for ResetDeviceCommandTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for TransferEventTrb {}
|
unsafe impl TrbCast for TransferEventTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for CommandCompletionEventTrb {}
|
unsafe impl TrbCast for CommandCompletionEventTrb {}
|
||||||
|
// SAFETY: see safety comments for TrbCast
|
||||||
unsafe impl TrbCast for PortStatusChangeEventTrb {}
|
unsafe impl TrbCast for PortStatusChangeEventTrb {}
|
||||||
|
|
||||||
#[bitfield]
|
#[bitfield]
|
||||||
|
|
|
@ -218,6 +218,7 @@ impl KvmVfioPviommu {
|
||||||
addr: 0,
|
addr: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of vfio_dev_attr, which is valid.
|
// Safe as we are the owner of vfio_dev_attr, which is valid.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
ioctl_with_ref(
|
ioctl_with_ref(
|
||||||
|
@ -248,6 +249,7 @@ impl KvmVfioPviommu {
|
||||||
vsid,
|
vsid,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of device and config which are valid, and we verify the return
|
// Safe as we are the owner of device and config which are valid, and we verify the return
|
||||||
// value.
|
// value.
|
||||||
let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_PVIOMMU_SET_CONFIG, &config) };
|
let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_PVIOMMU_SET_CONFIG, &config) };
|
||||||
|
@ -280,6 +282,7 @@ impl KvmVfioPviommu {
|
||||||
addr: addr_of_mut!(info) as usize as u64,
|
addr: addr_of_mut!(info) as usize as u64,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of vfio_dev_attr, which is valid.
|
// Safe as we are the owner of vfio_dev_attr, which is valid.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
ioctl_with_ref(
|
ioctl_with_ref(
|
||||||
|
@ -344,6 +347,7 @@ impl VfioContainer {
|
||||||
|
|
||||||
// Construct a VfioContainer from an exist container file.
|
// Construct a VfioContainer from an exist container file.
|
||||||
pub fn new_from_container(container: File) -> Result<Self> {
|
pub fn new_from_container(container: File) -> Result<Self> {
|
||||||
|
// SAFETY:
|
||||||
// Safe as file is vfio container descriptor and ioctl is defined by kernel.
|
// Safe as file is vfio container descriptor and ioctl is defined by kernel.
|
||||||
let version = unsafe { ioctl(&container, VFIO_GET_API_VERSION()) };
|
let version = unsafe { ioctl(&container, VFIO_GET_API_VERSION()) };
|
||||||
if version as u8 != VFIO_API_VERSION {
|
if version as u8 != VFIO_API_VERSION {
|
||||||
|
@ -362,12 +366,14 @@ impl VfioContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_extension(&self, val: IommuType) -> bool {
|
fn check_extension(&self, val: IommuType) -> bool {
|
||||||
|
// SAFETY:
|
||||||
// Safe as file is vfio container and make sure val is valid.
|
// Safe as file is vfio container and make sure val is valid.
|
||||||
let ret = unsafe { ioctl_with_val(self, VFIO_CHECK_EXTENSION(), val as c_ulong) };
|
let ret = unsafe { ioctl_with_val(self, VFIO_CHECK_EXTENSION(), val as c_ulong) };
|
||||||
ret != 0
|
ret != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_iommu(&mut self, val: IommuType) -> i32 {
|
fn set_iommu(&mut self, val: IommuType) -> i32 {
|
||||||
|
// SAFETY:
|
||||||
// Safe as file is vfio container and make sure val is valid.
|
// Safe as file is vfio container and make sure val is valid.
|
||||||
unsafe { ioctl_with_val(self, VFIO_SET_IOMMU(), val as c_ulong) }
|
unsafe { ioctl_with_val(self, VFIO_SET_IOMMU(), val as c_ulong) }
|
||||||
}
|
}
|
||||||
|
@ -455,6 +461,7 @@ impl VfioContainer {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as file is vfio container, dma_unmap is constructed by us, and
|
// Safe as file is vfio container, dma_unmap is constructed by us, and
|
||||||
// we check the return value
|
// we check the return value
|
||||||
let ret = unsafe { ioctl_with_mut_ref(self, VFIO_IOMMU_UNMAP_DMA(), &mut dma_unmap) };
|
let ret = unsafe { ioctl_with_mut_ref(self, VFIO_IOMMU_UNMAP_DMA(), &mut dma_unmap) };
|
||||||
|
@ -485,6 +492,7 @@ impl VfioContainer {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as file is vfio container, iommu_info has valid values,
|
// Safe as file is vfio container, iommu_info has valid values,
|
||||||
// and we check the return value
|
// and we check the return value
|
||||||
let ret = unsafe { ioctl_with_mut_ref(self, VFIO_IOMMU_GET_INFO(), &mut iommu_info) };
|
let ret = unsafe { ioctl_with_mut_ref(self, VFIO_IOMMU_GET_INFO(), &mut iommu_info) };
|
||||||
|
@ -516,6 +524,7 @@ impl VfioContainer {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as file is vfio container, iommu_info_argsz has valid values,
|
// Safe as file is vfio container, iommu_info_argsz has valid values,
|
||||||
// and we check the return value
|
// and we check the return value
|
||||||
let ret = unsafe { ioctl_with_mut_ref(self, VFIO_IOMMU_GET_INFO(), &mut iommu_info_argsz) };
|
let ret = unsafe { ioctl_with_mut_ref(self, VFIO_IOMMU_GET_INFO(), &mut iommu_info_argsz) };
|
||||||
|
@ -531,14 +540,16 @@ impl VfioContainer {
|
||||||
iommu_info_argsz.argsz as usize - mem::size_of::<vfio_iommu_type1_info>(),
|
iommu_info_argsz.argsz as usize - mem::size_of::<vfio_iommu_type1_info>(),
|
||||||
);
|
);
|
||||||
iommu_info[0].argsz = iommu_info_argsz.argsz;
|
iommu_info[0].argsz = iommu_info_argsz.argsz;
|
||||||
// Safe as file is vfio container, iommu_info has valid values,
|
|
||||||
// and we check the return value
|
|
||||||
let ret =
|
let ret =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe as file is vfio container, iommu_info has valid values,
|
||||||
|
// and we check the return value
|
||||||
unsafe { ioctl_with_mut_ptr(self, VFIO_IOMMU_GET_INFO(), iommu_info.as_mut_ptr()) };
|
unsafe { ioctl_with_mut_ptr(self, VFIO_IOMMU_GET_INFO(), iommu_info.as_mut_ptr()) };
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
return Err(VfioError::IommuGetInfo(get_error()));
|
return Err(VfioError::IommuGetInfo(get_error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we initialized iommu_info with enough space, u8 has less strict
|
// Safe because we initialized iommu_info with enough space, u8 has less strict
|
||||||
// alignment, and since it will no longer be mutated.
|
// alignment, and since it will no longer be mutated.
|
||||||
let info_bytes = unsafe {
|
let info_bytes = unsafe {
|
||||||
|
@ -622,6 +633,7 @@ impl VfioContainer {
|
||||||
IommuDevType::CoIommu | IommuDevType::PkvmPviommu | IommuDevType::VirtioIommu => {}
|
IommuDevType::CoIommu | IommuDevType::PkvmPviommu | IommuDevType::VirtioIommu => {}
|
||||||
IommuDevType::NoIommu => {
|
IommuDevType::NoIommu => {
|
||||||
for region in vm.get_memory().regions() {
|
for region in vm.get_memory().regions() {
|
||||||
|
// SAFETY:
|
||||||
// Safe because the guest regions are guaranteed not to overlap
|
// Safe because the guest regions are guaranteed not to overlap
|
||||||
unsafe {
|
unsafe {
|
||||||
self.vfio_dma_map(
|
self.vfio_dma_map(
|
||||||
|
@ -691,6 +703,8 @@ impl VfioContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_as_raw_descriptor(&self) -> Result<RawDescriptor> {
|
pub fn clone_as_raw_descriptor(&self) -> Result<RawDescriptor> {
|
||||||
|
// SAFETY: this call is safe because it doesn't modify any memory and we
|
||||||
|
// check the return value.
|
||||||
let raw_descriptor = unsafe { libc::dup(self.container.as_raw_descriptor()) };
|
let raw_descriptor = unsafe { libc::dup(self.container.as_raw_descriptor()) };
|
||||||
if raw_descriptor < 0 {
|
if raw_descriptor < 0 {
|
||||||
Err(VfioError::ContainerDupError)
|
Err(VfioError::ContainerDupError)
|
||||||
|
@ -729,8 +743,9 @@ impl VfioGroup {
|
||||||
argsz: mem::size_of::<vfio_group_status>() as u32,
|
argsz: mem::size_of::<vfio_group_status>() as u32,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
// Safe as we are the owner of group_file and group_status which are valid value.
|
|
||||||
let mut ret =
|
let mut ret =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe as we are the owner of group_file and group_status which are valid value.
|
||||||
unsafe { ioctl_with_mut_ref(&group_file, VFIO_GROUP_GET_STATUS(), &mut group_status) };
|
unsafe { ioctl_with_mut_ref(&group_file, VFIO_GROUP_GET_STATUS(), &mut group_status) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return Err(VfioError::GetGroupStatus(get_error()));
|
return Err(VfioError::GetGroupStatus(get_error()));
|
||||||
|
@ -740,9 +755,10 @@ impl VfioGroup {
|
||||||
return Err(VfioError::GroupViable);
|
return Err(VfioError::GroupViable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let container_raw_descriptor = container.as_raw_descriptor();
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of group_file and container_raw_descriptor which are valid value,
|
// Safe as we are the owner of group_file and container_raw_descriptor which are valid value,
|
||||||
// and we verify the ret value
|
// and we verify the ret value
|
||||||
let container_raw_descriptor = container.as_raw_descriptor();
|
|
||||||
ret = unsafe {
|
ret = unsafe {
|
||||||
ioctl_with_ref(
|
ioctl_with_ref(
|
||||||
&group_file,
|
&group_file,
|
||||||
|
@ -796,6 +812,7 @@ impl VfioGroup {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of vfio_dev_descriptor and vfio_dev_attr which are valid value,
|
// Safe as we are the owner of vfio_dev_descriptor and vfio_dev_attr which are valid value,
|
||||||
// and we verify the return value.
|
// and we verify the return value.
|
||||||
if 0 != unsafe {
|
if 0 != unsafe {
|
||||||
|
@ -815,12 +832,14 @@ impl VfioGroup {
|
||||||
let path: CString = CString::new(name.as_bytes()).expect("CString::new() failed");
|
let path: CString = CString::new(name.as_bytes()).expect("CString::new() failed");
|
||||||
let path_ptr = path.as_ptr();
|
let path_ptr = path.as_ptr();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and path_ptr which are valid value.
|
// Safe as we are the owner of self and path_ptr which are valid value.
|
||||||
let ret = unsafe { ioctl_with_ptr(self, VFIO_GROUP_GET_DEVICE_FD(), path_ptr) };
|
let ret = unsafe { ioctl_with_ptr(self, VFIO_GROUP_GET_DEVICE_FD(), path_ptr) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return Err(VfioError::GroupGetDeviceFD(get_error()));
|
return Err(VfioError::GroupGetDeviceFD(get_error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as ret is valid descriptor
|
// Safe as ret is valid descriptor
|
||||||
Ok(unsafe { File::from_raw_descriptor(ret) })
|
Ok(unsafe { File::from_raw_descriptor(ret) })
|
||||||
}
|
}
|
||||||
|
@ -1177,6 +1196,7 @@ impl VfioDevice {
|
||||||
let mut device_feature = vec_with_array_field::<vfio_device_feature, u8>(0);
|
let mut device_feature = vec_with_array_field::<vfio_device_feature, u8>(0);
|
||||||
device_feature[0].argsz = mem::size_of::<vfio_device_feature>() as u32;
|
device_feature[0].argsz = mem::size_of::<vfio_device_feature>() as u32;
|
||||||
device_feature[0].flags = VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY;
|
device_feature[0].flags = VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY;
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and power_management which are valid value
|
// Safe as we are the owner of self and power_management which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_FEATURE(), &device_feature[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_FEATURE(), &device_feature[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1197,8 +1217,9 @@ impl VfioDevice {
|
||||||
device_feature[0].argsz = (mem::size_of::<vfio_device_feature>() + payload_size) as u32;
|
device_feature[0].argsz = (mem::size_of::<vfio_device_feature>() + payload_size) as u32;
|
||||||
device_feature[0].flags =
|
device_feature[0].flags =
|
||||||
VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY_WITH_WAKEUP;
|
VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY_WITH_WAKEUP;
|
||||||
|
// SAFETY:
|
||||||
|
// Safe as we know vfio_device_low_power_entry_with_wakeup has two 32-bit int fields
|
||||||
unsafe {
|
unsafe {
|
||||||
// Safe as we know vfio_device_low_power_entry_with_wakeup has two 32-bit int fields
|
|
||||||
device_feature[0]
|
device_feature[0]
|
||||||
.data
|
.data
|
||||||
.as_mut_slice(payload_size)
|
.as_mut_slice(payload_size)
|
||||||
|
@ -1207,6 +1228,7 @@ impl VfioDevice {
|
||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and power_management which are valid value
|
// Safe as we are the owner of self and power_management which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_FEATURE(), &device_feature[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_FEATURE(), &device_feature[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1221,6 +1243,7 @@ impl VfioDevice {
|
||||||
let mut device_feature = vec_with_array_field::<vfio_device_feature, u8>(0);
|
let mut device_feature = vec_with_array_field::<vfio_device_feature, u8>(0);
|
||||||
device_feature[0].argsz = mem::size_of::<vfio_device_feature>() as u32;
|
device_feature[0].argsz = mem::size_of::<vfio_device_feature>() as u32;
|
||||||
device_feature[0].flags = VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_LOW_POWER_EXIT;
|
device_feature[0].flags = VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_LOW_POWER_EXIT;
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and power_management which are valid value
|
// Safe as we are the owner of self and power_management which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_FEATURE(), &device_feature[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_FEATURE(), &device_feature[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1236,15 +1259,18 @@ impl VfioDevice {
|
||||||
let mut dsm = vec_with_array_field::<vfio_acpi_dsm, u8>(count);
|
let mut dsm = vec_with_array_field::<vfio_acpi_dsm, u8>(count);
|
||||||
dsm[0].argsz = (mem::size_of::<vfio_acpi_dsm>() + mem::size_of_val(args)) as u32;
|
dsm[0].argsz = (mem::size_of::<vfio_acpi_dsm>() + mem::size_of_val(args)) as u32;
|
||||||
dsm[0].padding = 0;
|
dsm[0].padding = 0;
|
||||||
|
// SAFETY:
|
||||||
// Safe as we allocated enough space to hold args
|
// Safe as we allocated enough space to hold args
|
||||||
unsafe {
|
unsafe {
|
||||||
dsm[0].args.as_mut_slice(count).clone_from_slice(args);
|
dsm[0].args.as_mut_slice(count).clone_from_slice(args);
|
||||||
}
|
}
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and dsm which are valid value
|
// Safe as we are the owner of self and dsm which are valid value
|
||||||
let ret = unsafe { ioctl_with_mut_ref(&self.dev, VFIO_DEVICE_ACPI_DSM(), &mut dsm[0]) };
|
let ret = unsafe { ioctl_with_mut_ref(&self.dev, VFIO_DEVICE_ACPI_DSM(), &mut dsm[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
Err(VfioError::VfioAcpiDsm(get_error()))
|
Err(VfioError::VfioAcpiDsm(get_error()))
|
||||||
} else {
|
} else {
|
||||||
|
// SAFETY:
|
||||||
// Safe as we allocated enough space to hold args
|
// Safe as we allocated enough space to hold args
|
||||||
let res = unsafe { dsm[0].args.as_slice(count) };
|
let res = unsafe { dsm[0].args.as_slice(count) };
|
||||||
Ok(res.to_vec())
|
Ok(res.to_vec())
|
||||||
|
@ -1267,10 +1293,12 @@ impl VfioDevice {
|
||||||
irq_set[0].start = 0;
|
irq_set[0].start = 0;
|
||||||
irq_set[0].count = count as u32;
|
irq_set[0].count = count as u32;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// It is safe as enough space is reserved through vec_with_array_field(u32)<count>.
|
// It is safe as enough space is reserved through vec_with_array_field(u32)<count>.
|
||||||
let data = unsafe { irq_set[0].data.as_mut_slice(count * u32_size) };
|
let data = unsafe { irq_set[0].data.as_mut_slice(count * u32_size) };
|
||||||
data.copy_from_slice(&acpi_notification_eventfd.as_raw_descriptor().to_ne_bytes()[..]);
|
data.copy_from_slice(&acpi_notification_eventfd.as_raw_descriptor().to_ne_bytes()[..]);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and irq_set which are valid value
|
// Safe as we are the owner of self and irq_set which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1289,6 +1317,7 @@ impl VfioDevice {
|
||||||
irq_set[0].start = 0;
|
irq_set[0].start = 0;
|
||||||
irq_set[0].count = 0;
|
irq_set[0].count = 0;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and irq_set which are valid value
|
// Safe as we are the owner of self and irq_set which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1310,10 +1339,12 @@ impl VfioDevice {
|
||||||
irq_set[0].start = 0;
|
irq_set[0].start = 0;
|
||||||
irq_set[0].count = 1;
|
irq_set[0].count = 1;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// It is safe as enough space is reserved through vec_with_array_field(u32)<count>.
|
// It is safe as enough space is reserved through vec_with_array_field(u32)<count>.
|
||||||
let data = unsafe { irq_set[0].data.as_mut_slice(u32_size) };
|
let data = unsafe { irq_set[0].data.as_mut_slice(u32_size) };
|
||||||
data.copy_from_slice(&val.to_ne_bytes()[..]);
|
data.copy_from_slice(&val.to_ne_bytes()[..]);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and irq_set which are valid value
|
// Safe as we are the owner of self and irq_set which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1345,6 +1376,7 @@ impl VfioDevice {
|
||||||
irq_set[0].start = subindex;
|
irq_set[0].start = subindex;
|
||||||
irq_set[0].count = count as u32;
|
irq_set[0].count = count as u32;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// irq_set.data could be none, bool or descriptor according to flags, so irq_set.data
|
// irq_set.data could be none, bool or descriptor according to flags, so irq_set.data
|
||||||
// is u8 default, here irq_set.data is descriptor as u32, so 4 default u8 are combined
|
// is u8 default, here irq_set.data is descriptor as u32, so 4 default u8 are combined
|
||||||
// together as u32. It is safe as enough space is reserved through
|
// together as u32. It is safe as enough space is reserved through
|
||||||
|
@ -1359,6 +1391,7 @@ impl VfioDevice {
|
||||||
data = right;
|
data = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and irq_set which are valid value
|
// Safe as we are the owner of self and irq_set which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1386,6 +1419,7 @@ impl VfioDevice {
|
||||||
irq_set[0].count = 1;
|
irq_set[0].count = 1;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// SAFETY:
|
||||||
// irq_set.data could be none, bool or descriptor according to flags, so irq_set.data is
|
// irq_set.data could be none, bool or descriptor according to flags, so irq_set.data is
|
||||||
// u8 default, here irq_set.data is descriptor as u32, so 4 default u8 are combined
|
// u8 default, here irq_set.data is descriptor as u32, so 4 default u8 are combined
|
||||||
// together as u32. It is safe as enough space is reserved through
|
// together as u32. It is safe as enough space is reserved through
|
||||||
|
@ -1394,6 +1428,7 @@ impl VfioDevice {
|
||||||
descriptors.copy_from_slice(&descriptor.as_raw_descriptor().to_le_bytes()[..]);
|
descriptors.copy_from_slice(&descriptor.as_raw_descriptor().to_le_bytes()[..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and irq_set which are valid value
|
// Safe as we are the owner of self and irq_set which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1412,6 +1447,7 @@ impl VfioDevice {
|
||||||
irq_set[0].start = 0;
|
irq_set[0].start = 0;
|
||||||
irq_set[0].count = 0;
|
irq_set[0].count = 0;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and irq_set which are valid value
|
// Safe as we are the owner of self and irq_set which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1430,6 +1466,7 @@ impl VfioDevice {
|
||||||
irq_set[0].start = 0;
|
irq_set[0].start = 0;
|
||||||
irq_set[0].count = 1;
|
irq_set[0].count = 1;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and irq_set which are valid value
|
// Safe as we are the owner of self and irq_set which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1448,6 +1485,7 @@ impl VfioDevice {
|
||||||
irq_set[0].start = 0;
|
irq_set[0].start = 0;
|
||||||
irq_set[0].count = 1;
|
irq_set[0].count = 1;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of self and irq_set which are valid value
|
// Safe as we are the owner of self and irq_set which are valid value
|
||||||
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
|
@ -1467,6 +1505,7 @@ impl VfioDevice {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of device_file and dev_info which are valid value,
|
// Safe as we are the owner of device_file and dev_info which are valid value,
|
||||||
// and we verify the return value.
|
// and we verify the return value.
|
||||||
let ret = unsafe { ioctl_with_mut_ref(device_file, VFIO_DEVICE_GET_INFO(), &mut dev_info) };
|
let ret = unsafe { ioctl_with_mut_ref(device_file, VFIO_DEVICE_GET_INFO(), &mut dev_info) };
|
||||||
|
@ -1504,6 +1543,7 @@ impl VfioDevice {
|
||||||
index: i,
|
index: i,
|
||||||
count: 0,
|
count: 0,
|
||||||
};
|
};
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of dev and irq_info which are valid value,
|
// Safe as we are the owner of dev and irq_info which are valid value,
|
||||||
// and we verify the return value.
|
// and we verify the return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -1539,9 +1579,10 @@ impl VfioDevice {
|
||||||
size: 0,
|
size: 0,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
};
|
};
|
||||||
// Safe as we are the owner of dev and reg_info which are valid value,
|
|
||||||
// and we verify the return value.
|
|
||||||
let ret =
|
let ret =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe as we are the owner of dev and reg_info which are valid value,
|
||||||
|
// and we verify the return value.
|
||||||
unsafe { ioctl_with_mut_ref(dev, VFIO_DEVICE_GET_REGION_INFO(), &mut reg_info) };
|
unsafe { ioctl_with_mut_ref(dev, VFIO_DEVICE_GET_REGION_INFO(), &mut reg_info) };
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1559,6 +1600,7 @@ impl VfioDevice {
|
||||||
region_with_cap[0].region_info.cap_offset = 0;
|
region_with_cap[0].region_info.cap_offset = 0;
|
||||||
region_with_cap[0].region_info.size = 0;
|
region_with_cap[0].region_info.size = 0;
|
||||||
region_with_cap[0].region_info.offset = 0;
|
region_with_cap[0].region_info.offset = 0;
|
||||||
|
// SAFETY:
|
||||||
// Safe as we are the owner of dev and region_info which are valid value,
|
// Safe as we are the owner of dev and region_info which are valid value,
|
||||||
// and we verify the return value.
|
// and we verify the return value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -1593,27 +1635,33 @@ impl VfioDevice {
|
||||||
if offset + cap_header_sz > region_info_sz {
|
if offset + cap_header_sz > region_info_sz {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// SAFETY:
|
||||||
// Safe, as cap_header struct is in this function allocated region_with_cap
|
// Safe, as cap_header struct is in this function allocated region_with_cap
|
||||||
// vec.
|
// vec.
|
||||||
let cap_ptr = unsafe { info_ptr.offset(offset as isize) };
|
let cap_ptr = unsafe { info_ptr.offset(offset as isize) };
|
||||||
|
// SAFETY:
|
||||||
|
// Safe, as cap_header struct is in this function allocated region_with_cap
|
||||||
|
// vec.
|
||||||
let cap_header = unsafe { &*(cap_ptr as *const vfio_info_cap_header) };
|
let cap_header = unsafe { &*(cap_ptr as *const vfio_info_cap_header) };
|
||||||
if cap_header.id as u32 == VFIO_REGION_INFO_CAP_SPARSE_MMAP {
|
if cap_header.id as u32 == VFIO_REGION_INFO_CAP_SPARSE_MMAP {
|
||||||
if offset + mmap_cap_sz > region_info_sz {
|
if offset + mmap_cap_sz > region_info_sz {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// cap_ptr is vfio_region_info_cap_sparse_mmap here
|
// cap_ptr is vfio_region_info_cap_sparse_mmap here
|
||||||
// Safe, this vfio_region_info_cap_sparse_mmap is in this function allocated
|
|
||||||
// region_with_cap vec.
|
|
||||||
let sparse_mmap =
|
let sparse_mmap =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe, this vfio_region_info_cap_sparse_mmap is in this function
|
||||||
|
// allocated region_with_cap vec.
|
||||||
unsafe { &*(cap_ptr as *const vfio_region_info_cap_sparse_mmap) };
|
unsafe { &*(cap_ptr as *const vfio_region_info_cap_sparse_mmap) };
|
||||||
|
|
||||||
let area_num = sparse_mmap.nr_areas;
|
let area_num = sparse_mmap.nr_areas;
|
||||||
if offset + mmap_cap_sz + area_num * mmap_area_sz > region_info_sz {
|
if offset + mmap_cap_sz + area_num * mmap_area_sz > region_info_sz {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Safe, these vfio_region_sparse_mmap_area are in this function allocated
|
|
||||||
// region_with_cap vec.
|
|
||||||
let areas =
|
let areas =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe, these vfio_region_sparse_mmap_area are in this function allocated
|
||||||
|
// region_with_cap vec.
|
||||||
unsafe { sparse_mmap.areas.as_slice(sparse_mmap.nr_areas as usize) };
|
unsafe { sparse_mmap.areas.as_slice(sparse_mmap.nr_areas as usize) };
|
||||||
for area in areas.iter() {
|
for area in areas.iter() {
|
||||||
mmaps.push(*area);
|
mmaps.push(*area);
|
||||||
|
@ -1623,9 +1671,10 @@ impl VfioDevice {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// cap_ptr is vfio_region_info_cap_type here
|
// cap_ptr is vfio_region_info_cap_type here
|
||||||
// Safe, this vfio_region_info_cap_type is in this function allocated
|
|
||||||
// region_with_cap vec
|
|
||||||
let cap_type_info =
|
let cap_type_info =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe, this vfio_region_info_cap_type is in this function allocated
|
||||||
|
// region_with_cap vec
|
||||||
unsafe { &*(cap_ptr as *const vfio_region_info_cap_type) };
|
unsafe { &*(cap_ptr as *const vfio_region_info_cap_type) };
|
||||||
|
|
||||||
cap_info = Some((cap_type_info.type_, cap_type_info.subtype));
|
cap_info = Some((cap_type_info.type_, cap_type_info.subtype));
|
||||||
|
@ -1776,10 +1825,12 @@ impl VfioDevice {
|
||||||
/// Reads a value from the specified `VfioRegionAddr.addr` + `offset`.
|
/// Reads a value from the specified `VfioRegionAddr.addr` + `offset`.
|
||||||
pub fn region_read_from_addr<T: FromBytes>(&self, addr: &VfioRegionAddr, offset: u64) -> T {
|
pub fn region_read_from_addr<T: FromBytes>(&self, addr: &VfioRegionAddr, offset: u64) -> T {
|
||||||
let mut val = mem::MaybeUninit::zeroed();
|
let mut val = mem::MaybeUninit::zeroed();
|
||||||
// Safe because we have zero-initialized `size_of::<T>()` bytes.
|
|
||||||
let buf =
|
let buf =
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because we have zero-initialized `size_of::<T>()` bytes.
|
||||||
unsafe { slice::from_raw_parts_mut(val.as_mut_ptr() as *mut u8, mem::size_of::<T>()) };
|
unsafe { slice::from_raw_parts_mut(val.as_mut_ptr() as *mut u8, mem::size_of::<T>()) };
|
||||||
self.region_read(addr.index, buf, addr.addr + offset);
|
self.region_read(addr.index, buf, addr.addr + offset);
|
||||||
|
// SAFETY:
|
||||||
// Safe because any bit pattern is valid for a type that implements FromBytes.
|
// Safe because any bit pattern is valid for a type that implements FromBytes.
|
||||||
unsafe { val.assume_init() }
|
unsafe { val.assume_init() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -738,6 +738,7 @@ impl io::Write for Writer {
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = cmp::min(rem.len(), b.size());
|
let count = cmp::min(rem.len(), b.size());
|
||||||
|
// SAFETY:
|
||||||
// Safe because we have already verified that `vs` points to valid memory.
|
// Safe because we have already verified that `vs` points to valid memory.
|
||||||
unsafe {
|
unsafe {
|
||||||
copy_nonoverlapping(rem.as_ptr(), b.as_mut_ptr(), count);
|
copy_nonoverlapping(rem.as_ptr(), b.as_mut_ptr(), count);
|
||||||
|
|
|
@ -113,6 +113,7 @@ pub struct Caps(cap_t);
|
||||||
impl Caps {
|
impl Caps {
|
||||||
/// Get the capabilities for the current thread.
|
/// Get the capabilities for the current thread.
|
||||||
pub fn for_current_thread() -> io::Result<Caps> {
|
pub fn for_current_thread() -> io::Result<Caps> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let caps = unsafe { cap_get_proc() };
|
let caps = unsafe { cap_get_proc() };
|
||||||
if caps.is_null() {
|
if caps.is_null() {
|
||||||
|
@ -124,6 +125,7 @@ impl Caps {
|
||||||
|
|
||||||
/// Update the capabilities described by `self` by setting or clearing `caps` in `set`.
|
/// Update the capabilities described by `self` by setting or clearing `caps` in `set`.
|
||||||
pub fn update(&mut self, caps: &[Capability], set: Set, value: Value) -> io::Result<()> {
|
pub fn update(&mut self, caps: &[Capability], set: Set, value: Value) -> io::Result<()> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this only modifies the memory pointed to by `self.0` and we check the return
|
// Safe because this only modifies the memory pointed to by `self.0` and we check the return
|
||||||
// value.
|
// value.
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
@ -146,6 +148,7 @@ impl Caps {
|
||||||
|
|
||||||
/// Apply the capabilities described by `self` to the current thread.
|
/// Apply the capabilities described by `self` to the current thread.
|
||||||
pub fn apply(&self) -> io::Result<()> {
|
pub fn apply(&self) -> io::Result<()> {
|
||||||
|
// SAFETY: trivially safe
|
||||||
if unsafe { cap_set_proc(self.0) } == 0 {
|
if unsafe { cap_set_proc(self.0) } == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -156,6 +159,7 @@ impl Caps {
|
||||||
|
|
||||||
impl Drop for Caps {
|
impl Drop for Caps {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY: cap_t is allocated from `Self`
|
||||||
unsafe {
|
unsafe {
|
||||||
cap_free(self.0);
|
cap_free(self.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,6 +301,7 @@ macro_rules! scoped_cred {
|
||||||
|
|
||||||
impl Drop for $name {
|
impl Drop for $name {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// SAFETY: trivially safe
|
||||||
let res = unsafe { libc::syscall($syscall_nr, -1, self.old, -1) };
|
let res = unsafe { libc::syscall($syscall_nr, -1, self.old, -1) };
|
||||||
if res < 0 {
|
if res < 0 {
|
||||||
error!(
|
error!(
|
||||||
|
@ -337,6 +338,8 @@ thread_local! {
|
||||||
// SAFETY: both calls take no parameters and only return an integer value. The kernel also
|
// SAFETY: both calls take no parameters and only return an integer value. The kernel also
|
||||||
// guarantees that they can never fail.
|
// guarantees that they can never fail.
|
||||||
static THREAD_EUID: libc::uid_t = unsafe { libc::syscall(SYS_GETEUID) as libc::uid_t };
|
static THREAD_EUID: libc::uid_t = unsafe { libc::syscall(SYS_GETEUID) as libc::uid_t };
|
||||||
|
// SAFETY: both calls take no parameters and only return an integer value. The kernel also
|
||||||
|
// guarantees that they can never fail.
|
||||||
static THREAD_EGID: libc::gid_t = unsafe { libc::syscall(SYS_GETEGID) as libc::gid_t };
|
static THREAD_EGID: libc::gid_t = unsafe { libc::syscall(SYS_GETEGID) as libc::gid_t };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1106,14 +1109,18 @@ impl PassthroughFs {
|
||||||
) -> io::Result<(Option<Handle>, OpenOptions)> {
|
) -> io::Result<(Option<Handle>, OpenOptions)> {
|
||||||
let open_flags = self.update_open_flags(flags as i32);
|
let open_flags = self.update_open_flags(flags as i32);
|
||||||
|
|
||||||
let fd_open = syscall!(unsafe {
|
let fd_open = syscall!(
|
||||||
libc::openat64(
|
// SAFETY: return value is checked.
|
||||||
parent_data.as_raw_descriptor(),
|
unsafe {
|
||||||
name.as_ptr(),
|
libc::openat64(
|
||||||
(open_flags | libc::O_CLOEXEC) & !(libc::O_NOFOLLOW | libc::O_DIRECT),
|
parent_data.as_raw_descriptor(),
|
||||||
)
|
name.as_ptr(),
|
||||||
})?;
|
(open_flags | libc::O_CLOEXEC) & !(libc::O_NOFOLLOW | libc::O_DIRECT),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// SAFETY: fd_open is valid
|
||||||
let file_open = unsafe { File::from_raw_descriptor(fd_open) };
|
let file_open = unsafe { File::from_raw_descriptor(fd_open) };
|
||||||
let handle = self.next_handle.fetch_add(1, Ordering::Relaxed);
|
let handle = self.next_handle.fetch_add(1, Ordering::Relaxed);
|
||||||
let data = HandleData {
|
let data = HandleData {
|
||||||
|
@ -1265,8 +1272,8 @@ impl PassthroughFs {
|
||||||
let policy_size = cmp::min(arg.policy_size, size_of::<fscrypt_policy>() as u64);
|
let policy_size = cmp::min(arg.policy_size, size_of::<fscrypt_policy>() as u64);
|
||||||
arg.policy_size = policy_size;
|
arg.policy_size = policy_size;
|
||||||
|
|
||||||
// SAFETY: the kernel will only write to `arg` and we check the return value.
|
|
||||||
let res =
|
let res =
|
||||||
|
// SAFETY: the kernel will only write to `arg` and we check the return value.
|
||||||
unsafe { ioctl_with_mut_ptr(&*data, FS_IOC_GET_ENCRYPTION_POLICY_EX(), &mut arg) };
|
unsafe { ioctl_with_mut_ptr(&*data, FS_IOC_GET_ENCRYPTION_POLICY_EX(), &mut arg) };
|
||||||
if res < 0 {
|
if res < 0 {
|
||||||
Ok(IoctlReply::Done(Err(io::Error::last_os_error())))
|
Ok(IoctlReply::Done(Err(io::Error::last_os_error())))
|
||||||
|
@ -1594,9 +1601,9 @@ impl PassthroughFs {
|
||||||
if res < 0 {
|
if res < 0 {
|
||||||
Ok(IoctlReply::Done(Err(io::Error::last_os_error())))
|
Ok(IoctlReply::Done(Err(io::Error::last_os_error())))
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: this value was initialized by us already and then overwritten by the kernel.
|
|
||||||
// TODO: Replace with `MaybeUninit::slice_as_ptr` once it is stabilized.
|
|
||||||
let digest_size =
|
let digest_size =
|
||||||
|
// SAFETY: this value was initialized by us already and then overwritten by the kernel.
|
||||||
|
// TODO: Replace with `MaybeUninit::slice_as_ptr` once it is stabilized.
|
||||||
unsafe { addr_of!((*(buf.as_ptr() as *const fsverity_digest)).digest_size).read() };
|
unsafe { addr_of!((*(buf.as_ptr() as *const fsverity_digest)).digest_size).read() };
|
||||||
let outlen = size_of::<fsverity_digest>() as u32 + u32::from(digest_size);
|
let outlen = size_of::<fsverity_digest>() as u32 + u32::from(digest_size);
|
||||||
|
|
||||||
|
@ -1608,16 +1615,16 @@ impl PassthroughFs {
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: any bit pattern is valid for `MaybeUninit<u8>` and `fsverity_digest` doesn't
|
|
||||||
// contain any references.
|
|
||||||
let buf: [MaybeUninit<u8>; ROUNDED_LEN * size_of::<fsverity_digest>()] =
|
let buf: [MaybeUninit<u8>; ROUNDED_LEN * size_of::<fsverity_digest>()] =
|
||||||
|
// SAFETY: any bit pattern is valid for `MaybeUninit<u8>` and `fsverity_digest`
|
||||||
|
// doesn't contain any references.
|
||||||
unsafe { mem::transmute(buf) };
|
unsafe { mem::transmute(buf) };
|
||||||
|
|
||||||
// SAFETY: Casting to `*const [u8]` is safe because the kernel guarantees that the first
|
|
||||||
// `outlen` bytes of `buf` are initialized and `MaybeUninit<u8>` is guaranteed to have
|
|
||||||
// the same layout as `u8`.
|
|
||||||
// TODO: Replace with `MaybeUninit::slice_assume_init_ref` once it is stabilized.
|
|
||||||
let buf =
|
let buf =
|
||||||
|
// SAFETY: Casting to `*const [u8]` is safe because the kernel guarantees that the
|
||||||
|
// first `outlen` bytes of `buf` are initialized and `MaybeUninit<u8>` is guaranteed
|
||||||
|
// to have the same layout as `u8`.
|
||||||
|
// TODO: Replace with `MaybeUninit::slice_assume_init_ref` once it is stabilized.
|
||||||
unsafe { &*(&buf[..outlen as usize] as *const [MaybeUninit<u8>] as *const [u8]) };
|
unsafe { &*(&buf[..outlen as usize] as *const [MaybeUninit<u8>] as *const [u8]) };
|
||||||
Ok(IoctlReply::Done(Ok(buf.to_vec())))
|
Ok(IoctlReply::Done(Ok(buf.to_vec())))
|
||||||
}
|
}
|
||||||
|
@ -2301,12 +2308,15 @@ impl FileSystem for PassthroughFs {
|
||||||
}
|
}
|
||||||
|
|
||||||
if valid.contains(SetattrValid::SIZE) {
|
if valid.contains(SetattrValid::SIZE) {
|
||||||
// SAFETY: this doesn't modify any memory and we check the return value.
|
|
||||||
syscall!(match data {
|
syscall!(match data {
|
||||||
Data::Handle(_, fd) => unsafe { libc::ftruncate64(fd, attr.st_size) },
|
Data::Handle(_, fd) => {
|
||||||
|
// SAFETY: this doesn't modify any memory and we check the return value.
|
||||||
|
unsafe { libc::ftruncate64(fd, attr.st_size) }
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// There is no `ftruncateat` so we need to get a new fd and truncate it.
|
// There is no `ftruncateat` so we need to get a new fd and truncate it.
|
||||||
let f = self.open_inode(&inode_data, libc::O_NONBLOCK | libc::O_RDWR)?;
|
let f = self.open_inode(&inode_data, libc::O_NONBLOCK | libc::O_RDWR)?;
|
||||||
|
// SAFETY: this doesn't modify any memory and we check the return value.
|
||||||
unsafe { libc::ftruncate64(f.as_raw_descriptor(), attr.st_size) }
|
unsafe { libc::ftruncate64(f.as_raw_descriptor(), attr.st_size) }
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
@ -2542,6 +2552,7 @@ impl FileSystem for PassthroughFs {
|
||||||
self.find_handle(handle, inode)?
|
self.find_handle(handle, inode)?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Since this method is called whenever an fd is closed in the client, we can emulate that
|
// Since this method is called whenever an fd is closed in the client, we can emulate that
|
||||||
// behavior by doing the same thing (dup-ing the fd and then immediately closing it). Safe
|
// behavior by doing the same thing (dup-ing the fd and then immediately closing it). Safe
|
||||||
// because this doesn't modify any memory and we check the return values.
|
// because this doesn't modify any memory and we check the return values.
|
||||||
|
@ -2664,8 +2675,8 @@ impl FileSystem for PassthroughFs {
|
||||||
let path = CString::new(format!("self/fd/{}", file.0.as_raw_descriptor()))
|
let path = CString::new(format!("self/fd/{}", file.0.as_raw_descriptor()))
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
// SAFETY: this doesn't modify any memory and we check the return value.
|
|
||||||
syscall!(self.with_proc_chdir(|| {
|
syscall!(self.with_proc_chdir(|| {
|
||||||
|
// SAFETY: this doesn't modify any memory and we check the return value.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::setxattr(
|
libc::setxattr(
|
||||||
path.as_ptr(),
|
path.as_ptr(),
|
||||||
|
@ -2677,17 +2688,19 @@ impl FileSystem for PassthroughFs {
|
||||||
}
|
}
|
||||||
}))?;
|
}))?;
|
||||||
} else {
|
} else {
|
||||||
// For regular files and directories, we can just use fsetxattr.
|
syscall!(
|
||||||
// SAFETY: this doesn't modify any memory and we check the return value.
|
// For regular files and directories, we can just use fsetxattr.
|
||||||
syscall!(unsafe {
|
// SAFETY: this doesn't modify any memory and we check the return value.
|
||||||
libc::fsetxattr(
|
unsafe {
|
||||||
file.0.as_raw_descriptor(),
|
libc::fsetxattr(
|
||||||
name.as_ptr(),
|
file.0.as_raw_descriptor(),
|
||||||
value.as_ptr() as *const libc::c_void,
|
name.as_ptr(),
|
||||||
value.len() as libc::size_t,
|
value.as_ptr() as *const libc::c_void,
|
||||||
flags as c_int,
|
value.len() as libc::size_t,
|
||||||
)
|
flags as c_int,
|
||||||
})?;
|
)
|
||||||
|
}
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2788,14 +2801,15 @@ impl FileSystem for PassthroughFs {
|
||||||
let path = CString::new(format!("self/fd/{}", file.0.as_raw_descriptor()))
|
let path = CString::new(format!("self/fd/{}", file.0.as_raw_descriptor()))
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
// SAFETY: this doesn't modify any memory and we check the return value.
|
syscall!(self.with_proc_chdir(||
|
||||||
syscall!(
|
// SAFETY: this doesn't modify any memory and we check the return value.
|
||||||
self.with_proc_chdir(|| unsafe { libc::removexattr(path.as_ptr(), name.as_ptr()) })
|
unsafe { libc::removexattr(path.as_ptr(), name.as_ptr()) }))?;
|
||||||
)?;
|
|
||||||
} else {
|
} else {
|
||||||
// For regular files and directories, we can just use fremovexattr.
|
// For regular files and directories, we can just use fremovexattr.
|
||||||
// SAFETY: this doesn't modify any memory and we check the return value.
|
syscall!(
|
||||||
syscall!(unsafe { libc::fremovexattr(file.0.as_raw_descriptor(), name.as_ptr()) })?;
|
// SAFETY: this doesn't modify any memory and we check the return value.
|
||||||
|
unsafe { libc::fremovexattr(file.0.as_raw_descriptor(), name.as_ptr()) }
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2967,17 +2981,21 @@ impl FileSystem for PassthroughFs {
|
||||||
let src = src_data.as_raw_descriptor();
|
let src = src_data.as_raw_descriptor();
|
||||||
let dst = dst_data.as_raw_descriptor();
|
let dst = dst_data.as_raw_descriptor();
|
||||||
|
|
||||||
Ok(syscall!(unsafe {
|
Ok(syscall!(
|
||||||
libc::syscall(
|
// SAFETY: this call is safe because it doesn't modify any memory and we
|
||||||
libc::SYS_copy_file_range,
|
// check the return value.
|
||||||
src,
|
unsafe {
|
||||||
&offset_src,
|
libc::syscall(
|
||||||
dst,
|
libc::SYS_copy_file_range,
|
||||||
&offset_dst,
|
src,
|
||||||
length,
|
&offset_src,
|
||||||
flags,
|
dst,
|
||||||
)
|
&offset_dst,
|
||||||
})? as usize)
|
length,
|
||||||
|
flags,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)? as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_up_mapping<M: Mapper>(
|
fn set_up_mapping<M: Mapper>(
|
||||||
|
@ -3137,6 +3155,8 @@ mod tests {
|
||||||
// SAFETY: both calls take no parameters and only return an integer value. The kernel also
|
// SAFETY: both calls take no parameters and only return an integer value. The kernel also
|
||||||
// guarantees that they can never fail.
|
// guarantees that they can never fail.
|
||||||
let uid = unsafe { libc::syscall(SYS_GETEUID) as libc::uid_t };
|
let uid = unsafe { libc::syscall(SYS_GETEUID) as libc::uid_t };
|
||||||
|
// SAFETY: both calls take no parameters and only return an integer value. The kernel also
|
||||||
|
// guarantees that they can never fail.
|
||||||
let gid = unsafe { libc::syscall(SYS_GETEGID) as libc::gid_t };
|
let gid = unsafe { libc::syscall(SYS_GETEGID) as libc::gid_t };
|
||||||
let pid = std::process::id() as libc::pid_t;
|
let pid = std::process::id() as libc::pid_t;
|
||||||
Context { uid, gid, pid }
|
Context { uid, gid, pid }
|
||||||
|
@ -3265,18 +3285,23 @@ mod tests {
|
||||||
let p = PassthroughFs::new("tag", cfg).expect("Failed to create PassthroughFs");
|
let p = PassthroughFs::new("tag", cfg).expect("Failed to create PassthroughFs");
|
||||||
|
|
||||||
// Selinux shouldn't get overwritten.
|
// Selinux shouldn't get overwritten.
|
||||||
|
// SAFETY: trivially safe
|
||||||
let selinux = unsafe { CStr::from_bytes_with_nul_unchecked(b"security.selinux\0") };
|
let selinux = unsafe { CStr::from_bytes_with_nul_unchecked(b"security.selinux\0") };
|
||||||
assert_eq!(p.rewrite_xattr_name(selinux).to_bytes(), selinux.to_bytes());
|
assert_eq!(p.rewrite_xattr_name(selinux).to_bytes(), selinux.to_bytes());
|
||||||
|
|
||||||
// user, trusted, and system should not be changed either.
|
// user, trusted, and system should not be changed either.
|
||||||
|
// SAFETY: trivially safe
|
||||||
let user = unsafe { CStr::from_bytes_with_nul_unchecked(b"user.foobar\0") };
|
let user = unsafe { CStr::from_bytes_with_nul_unchecked(b"user.foobar\0") };
|
||||||
assert_eq!(p.rewrite_xattr_name(user).to_bytes(), user.to_bytes());
|
assert_eq!(p.rewrite_xattr_name(user).to_bytes(), user.to_bytes());
|
||||||
|
// SAFETY: trivially safe
|
||||||
let trusted = unsafe { CStr::from_bytes_with_nul_unchecked(b"trusted.foobar\0") };
|
let trusted = unsafe { CStr::from_bytes_with_nul_unchecked(b"trusted.foobar\0") };
|
||||||
assert_eq!(p.rewrite_xattr_name(trusted).to_bytes(), trusted.to_bytes());
|
assert_eq!(p.rewrite_xattr_name(trusted).to_bytes(), trusted.to_bytes());
|
||||||
|
// SAFETY: trivially safe
|
||||||
let system = unsafe { CStr::from_bytes_with_nul_unchecked(b"system.foobar\0") };
|
let system = unsafe { CStr::from_bytes_with_nul_unchecked(b"system.foobar\0") };
|
||||||
assert_eq!(p.rewrite_xattr_name(system).to_bytes(), system.to_bytes());
|
assert_eq!(p.rewrite_xattr_name(system).to_bytes(), system.to_bytes());
|
||||||
|
|
||||||
// sehash should be re-written.
|
// sehash should be re-written.
|
||||||
|
// SAFETY: trivially safe
|
||||||
let sehash = unsafe { CStr::from_bytes_with_nul_unchecked(b"security.sehash\0") };
|
let sehash = unsafe { CStr::from_bytes_with_nul_unchecked(b"security.sehash\0") };
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p.rewrite_xattr_name(sehash).to_bytes(),
|
p.rewrite_xattr_name(sehash).to_bytes(),
|
||||||
|
|
|
@ -32,12 +32,14 @@ pub struct ReadDir<P> {
|
||||||
|
|
||||||
impl<P: DerefMut<Target = [u8]>> ReadDir<P> {
|
impl<P: DerefMut<Target = [u8]>> ReadDir<P> {
|
||||||
pub fn new<D: AsRawDescriptor>(dir: &D, offset: libc::off64_t, mut buf: P) -> io::Result<Self> {
|
pub fn new<D: AsRawDescriptor>(dir: &D, offset: libc::off64_t, mut buf: P) -> io::Result<Self> {
|
||||||
|
// SAFETY:
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
// Safe because this doesn't modify any memory and we check the return value.
|
||||||
let res = unsafe { libc::lseek64(dir.as_raw_descriptor(), offset, libc::SEEK_SET) };
|
let res = unsafe { libc::lseek64(dir.as_raw_descriptor(), offset, libc::SEEK_SET) };
|
||||||
if res < 0 {
|
if res < 0 {
|
||||||
return Err(io::Error::last_os_error());
|
return Err(io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because the kernel guarantees that it will only write to `buf` and we check the
|
// Safe because the kernel guarantees that it will only write to `buf` and we check the
|
||||||
// return value.
|
// return value.
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
|
@ -117,6 +119,7 @@ fn strip_padding(b: &[u8]) -> &CStr {
|
||||||
.position(|&c| c == 0)
|
.position(|&c| c == 0)
|
||||||
.expect("`b` doesn't contain any nul bytes");
|
.expect("`b` doesn't contain any nul bytes");
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
// Safe because we are creating this string with the first nul-byte we found so we can
|
// Safe because we are creating this string with the first nul-byte we found so we can
|
||||||
// guarantee that it is nul-terminated and doesn't contain any interior nuls.
|
// guarantee that it is nul-terminated and doesn't contain any interior nuls.
|
||||||
unsafe { CStr::from_bytes_with_nul_unchecked(&b[..pos + 1]) }
|
unsafe { CStr::from_bytes_with_nul_unchecked(&b[..pos + 1]) }
|
||||||
|
|
|
@ -188,20 +188,27 @@ impl<F: FileSystem + Sync> Worker<F> {
|
||||||
// cases.
|
// cases.
|
||||||
const SECBIT_NO_SETUID_FIXUP: i32 = 1 << 2;
|
const SECBIT_NO_SETUID_FIXUP: i32 = 1 << 2;
|
||||||
|
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
let mut securebits = syscall!(
|
||||||
let mut securebits = syscall!(unsafe { libc::prctl(libc::PR_GET_SECUREBITS) })
|
// SAFETY: Safe because this doesn't modify any memory and we check the return value.
|
||||||
.map_err(Error::GetSecurebits)?;
|
unsafe { libc::prctl(libc::PR_GET_SECUREBITS) }
|
||||||
|
)
|
||||||
|
.map_err(Error::GetSecurebits)?;
|
||||||
|
|
||||||
securebits |= SECBIT_NO_SETUID_FIXUP;
|
securebits |= SECBIT_NO_SETUID_FIXUP;
|
||||||
|
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
syscall!(
|
||||||
syscall!(unsafe { libc::prctl(libc::PR_SET_SECUREBITS, securebits) })
|
// SAFETY: Safe because this doesn't modify any memory and we check the return value.
|
||||||
.map_err(Error::SetSecurebits)?;
|
unsafe { libc::prctl(libc::PR_SET_SECUREBITS, securebits) }
|
||||||
|
)
|
||||||
|
.map_err(Error::SetSecurebits)?;
|
||||||
|
|
||||||
// To avoid extra locking, unshare filesystem attributes from parent. This includes the
|
// To avoid extra locking, unshare filesystem attributes from parent. This includes the
|
||||||
// current working directory and umask.
|
// current working directory and umask.
|
||||||
// Safe because this doesn't modify any memory and we check the return value.
|
syscall!(
|
||||||
syscall!(unsafe { libc::unshare(libc::CLONE_FS) }).map_err(Error::UnshareFromParent)?;
|
// SAFETY: Safe because this doesn't modify any memory and we check the return value.
|
||||||
|
unsafe { libc::unshare(libc::CLONE_FS) }
|
||||||
|
)
|
||||||
|
.map_err(Error::UnshareFromParent)?;
|
||||||
|
|
||||||
#[derive(EventToken)]
|
#[derive(EventToken)]
|
||||||
enum Token {
|
enum Token {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue