Skip to content

Commit

Permalink
Auto merge of #128504 - matthiaskrgr:rollup-pawylnk, r=matthiaskrgr
Browse files Browse the repository at this point in the history
Rollup of 6 pull requests

Successful merges:

 - #127490 (Add target page for riscv64gc-unknown-linux-gnu)
 - #128433 (fix(hermit): `deny(unsafe_op_in_unsafe_fn)`)
 - #128482 (interpret: on a signed deref check, mention the right pointer in the error)
 - #128496 (Fix removed `box_syntax` diagnostic if source isn't available)
 - #128497 (fix dropck documentation for `[T;0]` special-case)
 - #128499 (chore: refactor backtrace formatting)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Aug 1, 2024
2 parents 0ea782c + 4520d77 commit 0201f9d
Show file tree
Hide file tree
Showing 26 changed files with 93 additions and 101 deletions.
38 changes: 20 additions & 18 deletions src/alloc_addresses/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rand::Rng;

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_span::Span;
use rustc_target::abi::{Align, HasDataLayout, Size};
use rustc_target::abi::{Align, Size};

use crate::{concurrency::VClock, *};

Expand Down Expand Up @@ -105,15 +105,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Returns the exposed `AllocId` that corresponds to the specified addr,
// or `None` if the addr is out of bounds
fn alloc_id_from_addr(&self, addr: u64) -> Option<AllocId> {
fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option<AllocId> {
let ecx = self.eval_context_ref();
let global_state = ecx.machine.alloc_addresses.borrow();
assert!(global_state.provenance_mode != ProvenanceMode::Strict);

// We always search the allocation to the right of this address. So if the size is structly
// negative, we have to search for `addr-1` instead.
let addr = if size >= 0 { addr } else { addr.saturating_sub(1) };
let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr);

// Determine the in-bounds provenance for this pointer.
// (This is only called on an actual access, so in-bounds is the only possible kind of provenance.)
let alloc_id = match pos {
Ok(pos) => Some(global_state.int_to_ptr_map[pos].1),
Err(0) => None,
Expand Down Expand Up @@ -305,20 +307,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

let (prov, offset) = ptr.into_parts(); // offset is relative (AllocId provenance)
let alloc_id = prov.alloc_id();
let base_addr = ecx.addr_from_alloc_id(alloc_id, kind)?;

// Add offset with the right kind of pointer-overflowing arithmetic.
let dl = ecx.data_layout();
let absolute_addr = dl.overflowing_offset(base_addr, offset.bytes()).0;
Ok(interpret::Pointer::new(
// Get a pointer to the beginning of this allocation.
let base_addr = ecx.addr_from_alloc_id(alloc_id, kind)?;
let base_ptr = interpret::Pointer::new(
Provenance::Concrete { alloc_id, tag },
Size::from_bytes(absolute_addr),
))
Size::from_bytes(base_addr),
);
// Add offset with the right kind of pointer-overflowing arithmetic.
Ok(base_ptr.wrapping_offset(offset, ecx))
}

/// When a pointer is used for a memory access, this computes where in which allocation the
/// access is going.
fn ptr_get_alloc(&self, ptr: interpret::Pointer<Provenance>) -> Option<(AllocId, Size)> {
fn ptr_get_alloc(
&self,
ptr: interpret::Pointer<Provenance>,
size: i64,
) -> Option<(AllocId, Size)> {
let ecx = self.eval_context_ref();

let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance)
Expand All @@ -327,20 +333,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
alloc_id
} else {
// A wildcard pointer.
ecx.alloc_id_from_addr(addr.bytes())?
ecx.alloc_id_from_addr(addr.bytes(), size)?
};

// This cannot fail: since we already have a pointer with that provenance, adjust_alloc_root_pointer
// must have been called in the past, so we can just look up the address in the map.
let base_addr = *ecx.machine.alloc_addresses.borrow().base_addr.get(&alloc_id).unwrap();

// Wrapping "addr - base_addr"
#[allow(clippy::cast_possible_wrap)] // we want to wrap here
let neg_base_addr = (base_addr as i64).wrapping_neg();
Some((
alloc_id,
Size::from_bytes(ecx.overflowing_signed_offset(addr.bytes(), neg_base_addr).0),
))
let rel_offset = ecx.truncate_to_target_usize(addr.bytes().wrapping_sub(base_addr));
Some((alloc_id, Size::from_bytes(rel_offset)))
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/borrow_tracker/stacked_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
// attempt to use it for a non-zero-sized access.
// Dangling slices are a common case here; it's valid to get their length but with raw
// pointer tagging for example all calls to get_unchecked on them are invalid.
if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr()) {
if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr(), 0) {
log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
// Still give it the new provenance, it got retagged after all.
return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
Expand All @@ -685,7 +685,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
}
}

let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr())?;
let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr(), 0)?;
log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;

trace!(
Expand Down
2 changes: 1 addition & 1 deletion src/borrow_tracker/tree_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};

trace!("Reborrow of size {:?}", ptr_size);
let (alloc_id, base_offset, parent_prov) = match this.ptr_try_get_alloc_id(place.ptr()) {
let (alloc_id, base_offset, parent_prov) = match this.ptr_try_get_alloc_id(place.ptr(), 0) {
Ok(data) => {
// Unlike SB, we *do* a proper retag for size 0 if can identify the allocation.
// After all, the pointer may be lazily initialized outside this initial range.
Expand Down
4 changes: 2 additions & 2 deletions src/concurrency/data_race.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1180,7 +1180,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
// We avoid `get_ptr_alloc` since we do *not* want to run the access hooks -- the actual
// access will happen later.
let (alloc_id, _offset, _prov) = this
.ptr_try_get_alloc_id(place.ptr())
.ptr_try_get_alloc_id(place.ptr(), 0)
.expect("there are no zero-sized atomic accesses");
if this.get_alloc_mutability(alloc_id)? == Mutability::Not {
// See if this is fine.
Expand Down Expand Up @@ -1307,7 +1307,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
if let Some(data_race) = &this.machine.data_race {
if data_race.race_detecting() {
let size = place.layout.size;
let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr())?;
let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr(), 0)?;
// Load and log the atomic operation.
// Note that atomic loads are possible even from read-only allocations, so `get_alloc_extra_mut` is not an option.
let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap();
Expand Down
8 changes: 4 additions & 4 deletions src/concurrency/weak_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
init: Scalar,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?;
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr(), 0)?;
if let (
crate::AllocExtra { weak_memory: Some(alloc_buffers), .. },
crate::MiriMachine { data_race: Some(global), threads, .. },
Expand All @@ -495,7 +495,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_ref();
if let Some(global) = &this.machine.data_race {
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?;
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr(), 0)?;
if let Some(alloc_buffers) = this.get_alloc_extra(alloc_id)?.weak_memory.as_ref() {
if atomic == AtomicReadOrd::SeqCst {
global.sc_read(&this.machine.threads);
Expand Down Expand Up @@ -535,7 +535,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
init: Scalar,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr())?;
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr(), 0)?;
if let (
crate::AllocExtra { weak_memory: Some(alloc_buffers), .. },
crate::MiriMachine { data_race: Some(global), threads, .. },
Expand Down Expand Up @@ -585,7 +585,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
global.sc_read(&this.machine.threads);
}
let size = place.layout.size;
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?;
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr(), 0)?;
if let Some(alloc_buffers) = this.get_alloc_extra(alloc_id)?.weak_memory.as_ref() {
let buffer = alloc_buffers
.get_or_create_store_buffer(alloc_range(base_offset, size), init)?;
Expand Down
6 changes: 3 additions & 3 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
// The part between the end_ptr and the end of the place is also frozen.
// So pretend there is a 0-sized `UnsafeCell` at the end.
unsafe_cell_action(&place.ptr().offset(size, this)?, Size::ZERO)?;
unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?;
// Done!
return Ok(());

Expand Down Expand Up @@ -975,7 +975,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
loop {
// FIXME: We are re-getting the allocation each time around the loop.
// Would be nice if we could somehow "extend" an existing AllocRange.
let alloc = this.get_ptr_alloc(ptr.offset(len, this)?, size1)?.unwrap(); // not a ZST, so we will get a result
let alloc = this.get_ptr_alloc(ptr.wrapping_offset(len, this), size1)?.unwrap(); // not a ZST, so we will get a result
let byte = alloc.read_integer(alloc_range(Size::ZERO, size1))?.to_u8()?;
if byte == 0 {
break;
Expand Down Expand Up @@ -1039,7 +1039,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
break;
} else {
wchars.push(wchar_int.try_into().unwrap());
ptr = ptr.offset(size, this)?;
ptr = ptr.wrapping_offset(size, this);
}
}

Expand Down
14 changes: 9 additions & 5 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1198,19 +1198,23 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
}
}

/// Convert a pointer with provenance into an allocation-offset pair,
/// or a `None` with an absolute address if that conversion is not possible.
/// Convert a pointer with provenance into an allocation-offset pair and extra provenance info.
/// `size` says how many bytes of memory are expected at that pointer. The *sign* of `size` can
/// be used to disambiguate situations where a wildcard pointer sits right in between two
/// allocations.
///
/// This is called when a pointer is about to be used for memory access,
/// an in-bounds check, or anything else that requires knowing which allocation it points to.
/// If `ptr.provenance.get_alloc_id()` is `Some(p)`, the returned `AllocId` must be `p`.
/// The resulting `AllocId` will just be used for that one step and the forgotten again
/// (i.e., we'll never turn the data returned here back into a `Pointer` that might be
/// stored in machine state).
///
/// When this fails, that means the pointer does not point to a live allocation.
fn ptr_get_alloc(
ecx: &MiriInterpCx<'tcx>,
ptr: StrictPointer,
size: i64,
) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
let rel = ecx.ptr_get_alloc(ptr);
let rel = ecx.ptr_get_alloc(ptr, size);

rel.map(|(alloc_id, size)| {
let tag = match ptr.provenance {
Expand Down
2 changes: 1 addition & 1 deletion src/shims/backtrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

let ptr = this.read_pointer(ptr)?;
// Take apart the pointer, we need its pieces. The offset encodes the span.
let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(ptr)?;
let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(ptr, 0)?;

// This has to be an actual global fn ptr, not a dlsym function.
let fn_instance = if let Some(GlobalAlloc::Function { instance, .. }) =
Expand Down
22 changes: 11 additions & 11 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"miri_get_alloc_id" => {
let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr).map_err(|_e| {
let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err(|_e| {
err_machine_stop!(TerminationInfo::Abort(format!(
"pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
)))
Expand Down Expand Up @@ -311,7 +311,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"miri_static_root" => {
let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr)?;
let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
if offset != Size::ZERO {
throw_unsup_format!(
"pointer passed to `miri_static_root` must point to beginning of an allocated block"
Expand Down Expand Up @@ -392,7 +392,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"`miri_promise_symbolic_alignment`: pointer is not actually aligned"
);
}
if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr) {
if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
let (_size, alloc_align, _kind) = this.get_alloc_info(alloc_id);
// If the newly promised alignment is bigger than the native alignment of this
// allocation, and bigger than the previously promised alignment, then set it.
Expand Down Expand Up @@ -584,8 +584,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let n = Size::from_bytes(this.read_target_usize(n)?);

// C requires that this must always be a valid pointer (C18 §7.1.4).
this.ptr_get_alloc_id(left)?;
this.ptr_get_alloc_id(right)?;
this.ptr_get_alloc_id(left, 0)?;
this.ptr_get_alloc_id(right, 0)?;

let result = {
let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
Expand All @@ -612,7 +612,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let val = val as u8;

// C requires that this must always be a valid pointer (C18 §7.1.4).
this.ptr_get_alloc_id(ptr)?;
this.ptr_get_alloc_id(ptr, 0)?;

if let Some(idx) = this
.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
Expand All @@ -622,7 +622,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
{
let idx = u64::try_from(idx).unwrap();
#[allow(clippy::arithmetic_side_effects)] // idx < num, so this never wraps
let new_ptr = ptr.offset(Size::from_bytes(num - idx - 1), this)?;
let new_ptr = ptr.wrapping_offset(Size::from_bytes(num - idx - 1), this);
this.write_pointer(new_ptr, dest)?;
} else {
this.write_null(dest)?;
Expand All @@ -639,14 +639,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let val = val as u8;

// C requires that this must always be a valid pointer (C18 §7.1.4).
this.ptr_get_alloc_id(ptr)?;
this.ptr_get_alloc_id(ptr, 0)?;

let idx = this
.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
.iter()
.position(|&c| c == val);
if let Some(idx) = idx {
let new_ptr = ptr.offset(Size::from_bytes(idx as u64), this)?;
let new_ptr = ptr.wrapping_offset(Size::from_bytes(idx as u64), this);
this.write_pointer(new_ptr, dest)?;
} else {
this.write_null(dest)?;
Expand Down Expand Up @@ -681,8 +681,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {

// C requires that this must always be a valid pointer, even if `n` is zero, so we better check that.
// (This is more than Rust requires, so `mem_copy` is not sufficient.)
this.ptr_get_alloc_id(ptr_dest)?;
this.ptr_get_alloc_id(ptr_src)?;
this.ptr_get_alloc_id(ptr_dest, 0)?;
this.ptr_get_alloc_id(ptr_src, 0)?;

this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
this.write_pointer(ptr_dest, dest)?;
Expand Down
2 changes: 1 addition & 1 deletion src/shims/unix/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl<'tcx> UnixEnvVars<'tcx> {
};
// The offset is used to strip the "{name}=" part of the string.
let var_ptr = var_ptr
.offset(Size::from_bytes(u64::try_from(name.len()).unwrap().strict_add(1)), ecx)?;
.wrapping_offset(Size::from_bytes(u64::try_from(name.len()).unwrap().strict_add(1)), ecx);
Ok(Some(var_ptr))
}

Expand Down
2 changes: 1 addition & 1 deletion src/shims/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&this.ptr_to_mplace(entry, dirent64_layout),
)?;

let name_ptr = entry.offset(Size::from_bytes(d_name_offset), this)?;
let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this);
this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?;

Some(entry)
Expand Down
2 changes: 1 addition & 1 deletion src/shims/unix/linux/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// We just allocated this, the access is definitely in-bounds and fits into our address space.
// mmap guarantees new mappings are zero-init.
this.write_bytes_ptr(
ptr.offset(Size::from_bytes(old_size), this).unwrap().into(),
ptr.wrapping_offset(Size::from_bytes(old_size), this).into(),
std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()),
)
.unwrap();
Expand Down
4 changes: 2 additions & 2 deletions tests/fail-dep/libc/affinity.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC and there are only 128 bytes starting at that pointer
error: Undefined Behavior: memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC which is only 128 bytes from the end of the allocation
--> $DIR/affinity.rs:LL:CC
|
LL | let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>() + 1, &cpuset) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC and there are only 128 bytes starting at that pointer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC which is only 128 bytes from the end of the allocation
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
Expand Down
Loading

0 comments on commit 0201f9d

Please sign in to comment.