Skip to content

Commit ac8f847

Browse files
committed
fix(sallyport): fix UB by avoiding implicit reference from indexing with slice
This potential source of UB was discovered while upgrading the Rust toolchain, which upgrades us to a new version of Miri with stricter rules around raw pointers. Specifically, an expression like `addr_of_mut!((*(ptr))[offset..])` is deliberately attempting to operate only on raw pointers while avoiding any intermediate references, since references have invariants that raw pointers do not. However, there is in fact an implicit reference here that is created as a result of the indexing operation. This is both surprising and not surprising, for interesting reasons. First, it should not be surprising because indexing is governed by the Index traits, whose methods function return references, so their presence here is natural. On the other hand, it is surprising because Rust already special cases `(*ptr)[foo]` when `ptr` is a raw slice and `foo` is not a range to avoid the Index traits entirely, which allows it to avoid emitting an intermediate reference. The ideal solution here is for Rust to be smart enough to not introduce the intermediate reference here at all, which is tracked at rust-lang/rust#73987 . In addition, while investigating this issue I brought it up to the Unsafe Code Guidelines team, who saw fit to file rust-lang/rust#99437 as a more specific example of the potential perils of the current behavior.
1 parent e5ceb15 commit ac8f847

File tree

2 files changed

+22
-10
lines changed

2 files changed

+22
-10
lines changed

crates/sallyport/src/guest/alloc/phase_alloc.rs

+21-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use crate::Result;
77
use core::alloc::Layout;
88
use core::marker::PhantomData;
99
use core::mem::{align_of, size_of};
10-
use core::ptr::addr_of_mut;
1110
use core::ptr::NonNull;
1211

1312
pub(crate) mod phase {
@@ -76,29 +75,41 @@ impl<'a> Alloc<'a, phase::Init> {
7675
}
7776

7877
impl<'a> Alloc<'a, phase::Stage> {
79-
/// Allocates a memory region of `layout.size()` bytes with padding required to ensure alignment
80-
/// and returns a tuple of non-null pointer and byte offset of start of that aligned region on success.
78+
/// Allocates a memory region of `layout.size()` bytes with padding required to ensure
79+
/// alignment and returns a tuple of non-null pointer and byte offset of start of that aligned
80+
/// region on success.
8181
#[inline]
8282
fn allocate_layout(&mut self, layout: Layout) -> Result<(NonNull<[u8]>, usize)> {
8383
let free = self.ptr.len();
8484
let pad_size = self.ptr.cast::<u8>().as_ptr().align_offset(layout.align());
8585
let layout_size = layout.size();
86+
8687
if free < pad_size.checked_add(layout_size).ok_or(EOVERFLOW)? {
8788
return Err(ENOMEM);
8889
}
8990

90-
let ptr = unsafe { addr_of_mut!((*(self.ptr.as_ptr()))[pad_size..]) };
91+
let raw_slice = self.ptr.as_ptr();
92+
93+
// SAFETY: we know raw_slice is nonnull, and we know pad_size is in-bounds
94+
let padded_raw_slice = unsafe { raw_slice.get_unchecked_mut(pad_size..) };
95+
96+
// SAFETY: we know padded_raw_slice is nonnull, and we know layout_size is in-bounds
97+
let new_region = unsafe { padded_raw_slice.get_unchecked_mut(..layout_size) };
98+
99+
// SAFETY: we know padded_raw_slice is nonnull, and we know layout_size is in-bounds
100+
let remainder = unsafe { padded_raw_slice.get_unchecked_mut(layout_size..) };
101+
91102
let offset = self.offset + pad_size;
103+
92104
*self = Self {
93-
ptr: unsafe { NonNull::new_unchecked(addr_of_mut!((*(ptr))[layout_size..])) },
105+
// SAFETY: we know remainder is nonnull
106+
ptr: unsafe { NonNull::new_unchecked(remainder) },
94107
offset: offset + layout_size,
95-
96108
phase: PhantomData,
97109
};
98-
Ok((
99-
unsafe { NonNull::new_unchecked(addr_of_mut!((*(ptr))[..layout_size])) },
100-
offset,
101-
))
110+
111+
// SAFETY: we know new_region is nonnull
112+
Ok((unsafe { NonNull::new_unchecked(new_region) }, offset))
102113
}
103114

104115
#[inline]

crates/sallyport/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#![feature(core_ffi_c)]
1212
#![feature(c_size_t)]
1313
#![feature(nonnull_slice_from_raw_parts)]
14+
#![feature(slice_ptr_get)]
1415
#![feature(slice_ptr_len)]
1516

1617
pub mod elf;

0 commit comments

Comments
 (0)