Auto merge of #128334 - matthiaskrgr:rollup-nhxdt0c, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - #128182 (handle no_std targets on std builds)
 - #128277 (miri: fix offset_from behavior on wildcard pointers)
 - #128304 (Isolate the diagnostic code that expects `thir::Pat` to be printable)
 - #128307 (Clean and enable `rustdoc::unescaped_backticks` for `core/alloc/std/test/proc_macro`)
 - #128322 (CI: move RFL job forward to v6.11-rc1)
 - #128333 (Miri subtree update)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-07-29 10:05:42 +00:00
commit 56c698c711
145 changed files with 1241 additions and 553 deletions

View File

@ -45,9 +45,9 @@ const_eval_copy_nonoverlapping_overlapping =
`copy_nonoverlapping` called on overlapping ranges
const_eval_dangling_int_pointer =
{$bad_pointer_message}: {$pointer} is a dangling pointer (it has no provenance)
{$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} which is a dangling pointer (it has no provenance)
const_eval_dangling_null_pointer =
{$bad_pointer_message}: null pointer is a dangling pointer (it has no provenance)
{$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got a null pointer
const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind}
const_eval_dead_local =
@ -87,6 +87,13 @@ const_eval_error = {$error_kind ->
const_eval_exact_div_has_remainder =
exact_div: {$a} cannot be divided by {$b} without remainder
const_eval_expected_inbounds_pointer =
expected {$inbounds_size ->
[0] a pointer to some allocation
[1] a pointer to 1 byte of memory
*[x] a pointer to {$inbounds_size} bytes of memory
}
const_eval_extern_static =
cannot access extern static ({$did})
const_eval_extern_type_field = `extern type` field does not have a known offset
@ -233,8 +240,6 @@ const_eval_nullary_intrinsic_fail =
const_eval_offset_from_different_allocations =
`{$name}` called on pointers into different allocations
const_eval_offset_from_different_integers =
`{$name}` called on different pointers without provenance (i.e., without an associated allocation)
const_eval_offset_from_overflow =
`{$name}` called when first pointer is too far ahead of second
const_eval_offset_from_test =
@ -242,7 +247,10 @@ const_eval_offset_from_test =
const_eval_offset_from_underflow =
`{$name}` called when first pointer is too far before second
const_eval_offset_from_unsigned_overflow =
`ptr_offset_from_unsigned` called when first pointer has smaller offset than second: {$a_offset} < {$b_offset}
`ptr_offset_from_unsigned` called when first pointer has smaller {$is_addr ->
[true] address
*[false] offset
} than second: {$a_offset} < {$b_offset}
const_eval_operator_non_const =
cannot call non-const operator in {const_eval_const_context}s
@ -264,10 +272,16 @@ const_eval_pointer_arithmetic_overflow =
overflowing in-bounds pointer arithmetic
const_eval_pointer_arithmetic_test = out-of-bounds pointer arithmetic
const_eval_pointer_out_of_bounds =
{$bad_pointer_message}: {$alloc_id} has size {$alloc_size}, so pointer to {$ptr_size} {$ptr_size ->
[1] byte
*[many] bytes
} starting at offset {$ptr_offset} is out-of-bounds
{$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} {$ptr_offset_is_neg ->
[true] which points to before the beginning of the allocation
*[false] {$alloc_size_minus_ptr_offset ->
[0] which is at or beyond the end of the allocation of size {$alloc_size ->
[1] 1 byte
*[x] {$alloc_size} bytes
}
*[x] and there are only {$alloc_size_minus_ptr_offset} bytes starting at that pointer
}
}
const_eval_pointer_use_after_free =
{$bad_pointer_message}: {$alloc_id} has been freed, so this pointer is dangling
const_eval_ptr_as_bytes_1 =
@ -465,5 +479,3 @@ const_eval_write_through_immutable_pointer =
const_eval_write_to_read_only =
writing to {$allocation} which is read-only
const_eval_zst_pointer_out_of_bounds =
{$bad_pointer_message}: {$alloc_id} has size {$alloc_size}, so pointer at offset {$ptr_offset} is out-of-bounds

View File

@ -8,9 +8,9 @@ use rustc_errors::{
use rustc_hir::ConstContext;
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::mir::interpret::{
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, Misalignment,
PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo,
ValidationErrorInfo,
CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpError, InvalidMetaKind,
InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo,
UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
};
use rustc_middle::ty::{self, Mutability, Ty};
use rustc_span::Span;
@ -490,10 +490,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
InvalidMeta(InvalidMetaKind::TooBig) => const_eval_invalid_meta,
UnterminatedCString(_) => const_eval_unterminated_c_string,
PointerUseAfterFree(_, _) => const_eval_pointer_use_after_free,
PointerOutOfBounds { ptr_size: Size::ZERO, .. } => const_eval_zst_pointer_out_of_bounds,
PointerOutOfBounds { .. } => const_eval_pointer_out_of_bounds,
DanglingIntPointer(0, _) => const_eval_dangling_null_pointer,
DanglingIntPointer(_, _) => const_eval_dangling_int_pointer,
DanglingIntPointer { addr: 0, .. } => const_eval_dangling_null_pointer,
DanglingIntPointer { .. } => const_eval_dangling_int_pointer,
AlignmentCheckFailed { .. } => const_eval_alignment_check_failed,
WriteToReadOnly(_) => const_eval_write_to_read_only,
DerefFunctionPointer(_) => const_eval_deref_function_pointer,
@ -575,18 +574,33 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
diag.arg("alloc_id", alloc_id)
.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
}
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => {
diag.arg("alloc_id", alloc_id)
.arg("alloc_size", alloc_size.bytes())
.arg("ptr_offset", ptr_offset)
.arg("ptr_size", ptr_size.bytes())
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, inbounds_size, msg } => {
diag.arg("alloc_size", alloc_size.bytes())
.arg("inbounds_size", inbounds_size.bytes())
.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
diag.arg(
"pointer",
Pointer::new(
Some(CtfeProvenance::from(alloc_id)),
Size::from_bytes(ptr_offset as u64),
)
.to_string(),
);
diag.arg("ptr_offset_is_neg", ptr_offset < 0);
diag.arg(
"alloc_size_minus_ptr_offset",
alloc_size.bytes().saturating_sub(ptr_offset as u64),
);
}
DanglingIntPointer(ptr, msg) => {
if ptr != 0 {
diag.arg("pointer", format!("{ptr:#x}[noalloc]"));
DanglingIntPointer { addr, inbounds_size, msg } => {
if addr != 0 {
diag.arg(
"pointer",
Pointer::<Option<CtfeProvenance>>::from_addr_invalid(addr).to_string(),
);
}
diag.arg("inbounds_size", inbounds_size.bytes());
diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
}
AlignmentCheckFailed(Misalignment { required, has }, msg) => {

View File

@ -238,36 +238,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let isize_layout = self.layout_of(self.tcx.types.isize)?;
// Get offsets for both that are at least relative to the same base.
let (a_offset, b_offset) =
// With `OFFSET_IS_ADDR` this is trivial; without it we need either
// two integers or two pointers into the same allocation.
let (a_offset, b_offset, is_addr) = if M::Provenance::OFFSET_IS_ADDR {
(a.addr().bytes(), b.addr().bytes(), /*is_addr*/ true)
} else {
match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) {
(Err(a), Err(b)) => {
// Neither pointer points to an allocation.
// This is okay only if they are the same.
if a != b {
// We'd catch this below in the "dereferenceable" check, but
// show a nicer error for this particular case.
throw_ub_custom!(
fluent::const_eval_offset_from_different_integers,
name = intrinsic_name,
);
}
// This will always return 0.
(a, b)
// Neither pointer points to an allocation, so they are both absolute.
(a, b, /*is_addr*/ true)
}
_ if M::Provenance::OFFSET_IS_ADDR && a.addr() == b.addr() => {
// At least one of the pointers has provenance, but they also point to
// the same address so it doesn't matter; this is fine. `(0, 0)` means
// we pass all the checks below and return 0.
(0, 0)
}
// From here onwards, the pointers are definitely for different addresses
// (or we can't determine their absolute address).
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _)))
if a_alloc_id == b_alloc_id =>
{
// Found allocation for both, and it's the same.
// Use these offsets for distance calculation.
(a_offset.bytes(), b_offset.bytes())
(a_offset.bytes(), b_offset.bytes(), /*is_addr*/ false)
}
_ => {
// Not into the same allocation -- this is UB.
@ -276,9 +262,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
name = intrinsic_name,
);
}
};
}
};
// Compute distance.
// Compute distance: a - b.
let dist = {
// Addresses are unsigned, so this is a `usize` computation. We have to do the
// overflow check separately anyway.
@ -295,6 +282,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
fluent::const_eval_offset_from_unsigned_overflow,
a_offset = a_offset,
b_offset = b_offset,
is_addr = is_addr,
);
}
// The signed form of the intrinsic allows this. If we interpret the
@ -323,14 +311,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
};
// Check that the range between them is dereferenceable ("in-bounds or one past the
// end of the same allocation"). This is like the check in ptr_offset_inbounds.
let min_ptr = if dist >= 0 { b } else { a };
self.check_ptr_access(
min_ptr,
Size::from_bytes(dist.unsigned_abs()),
// Check that the memory between them is dereferenceable at all, starting from the
// base pointer: `dist` is `a - b`, so it is based on `b`.
self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest)?;
// Then check that this is also dereferenceable from `a`. This ensures that they are
// derived from the same allocation.
self.check_ptr_access_signed(
a,
dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large
CheckInAllocMsg::OffsetFromTest,
)?;
)
.map_err(|_| {
// Make the error more specific.
err_ub_custom!(
fluent::const_eval_offset_from_different_allocations,
name = intrinsic_name,
)
})?;
// Perform division by size to compute return value.
let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned {
@ -577,27 +574,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
/// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
/// allocation. For integer pointers, we consider each of them their own tiny allocation of size
/// 0, so offset-by-0 (and only 0) is okay -- except that null cannot be offset by _any_ value.
/// allocation.
pub fn ptr_offset_inbounds(
&self,
ptr: Pointer<Option<M::Provenance>>,
offset_bytes: i64,
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
// The offset being in bounds cannot rely on "wrapping around" the address space.
// So, first rule out overflows in the pointer arithmetic.
let offset_ptr = ptr.signed_offset(offset_bytes, self)?;
// ptr and offset_ptr must be in bounds of the same allocated object. This means all of the
// memory between these pointers must be accessible. Note that we do not require the
// pointers to be properly aligned (unlike a read/write operation).
let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr };
// This call handles checking for integer/null pointers.
self.check_ptr_access(
min_ptr,
Size::from_bytes(offset_bytes.unsigned_abs()),
CheckInAllocMsg::PointerArithmeticTest,
)?;
Ok(offset_ptr)
// We first compute the pointer with overflow checks, to get a specific error for when it
// overflows (though technically this is redundant with the following inbounds check).
let result = ptr.signed_offset(offset_bytes, self)?;
// The offset must be in bounds starting from `ptr`.
self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?;
// Done.
Ok(result)
}
/// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.

View File

@ -411,6 +411,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Ok(())
}
/// Check whether the given pointer points to live memory for a signed amount of bytes.
/// A negative amounts means that the given range of memory to the left of the pointer
/// needs to be dereferenceable.
pub fn check_ptr_access_signed(
&self,
ptr: Pointer<Option<M::Provenance>>,
size: i64,
msg: CheckInAllocMsg,
) -> InterpResult<'tcx> {
if let Ok(size) = u64::try_from(size) {
self.check_ptr_access(ptr, Size::from_bytes(size), msg)
} else {
// Compute the pointer at the beginning of the range, and do the standard
// dereferenceability check from there.
let begin_ptr = ptr.wrapping_signed_offset(size, self);
self.check_ptr_access(begin_ptr, Size::from_bytes(size.unsigned_abs()), msg)
}
}
/// Low-level helper function to check if a ptr is in-bounds and potentially return a reference
/// to the allocation it points to. Supports both shared and mutable references, as the actual
/// checking is offloaded to a helper closure.
@ -437,7 +456,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Ok(match self.ptr_try_get_alloc_id(ptr) {
Err(addr) => {
// We couldn't get a proper allocation.
throw_ub!(DanglingIntPointer(addr, msg));
throw_ub!(DanglingIntPointer { addr, inbounds_size: size, msg });
}
Ok((alloc_id, offset, prov)) => {
let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
@ -448,7 +467,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
alloc_id,
alloc_size,
ptr_offset: self.target_usize_to_isize(offset.bytes()),
ptr_size: size,
inbounds_size: size,
msg,
})
}
@ -1421,7 +1440,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
ptr: Pointer<Option<M::Provenance>>,
) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> {
self.ptr_try_get_alloc_id(ptr).map_err(|offset| {
err_ub!(DanglingIntPointer(offset, CheckInAllocMsg::InboundsTest)).into()
err_ub!(DanglingIntPointer {
addr: offset,
// We don't know the actually required size.
inbounds_size: Size::ZERO,
msg: CheckInAllocMsg::InboundsTest
})
.into()
})
}
}

View File

@ -348,7 +348,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
try_validation!(
self.ecx.get_ptr_vtable_ty(vtable, Some(data)),
self.path,
Ub(DanglingIntPointer(..) | InvalidVTablePointer(..)) =>
Ub(DanglingIntPointer{ .. } | InvalidVTablePointer(..)) =>
InvalidVTablePtr { value: format!("{vtable}") },
Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => {
InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait }
@ -405,8 +405,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
),
self.path,
Ub(DanglingIntPointer(0, _)) => NullPtr { ptr_kind },
Ub(DanglingIntPointer(i, _)) => DanglingPtrNoProvenance {
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind },
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
ptr_kind,
// FIXME this says "null pointer" when null but we need translate
pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(*i))
@ -605,7 +605,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
let _fn = try_validation!(
self.ecx.get_ptr_fn(ptr),
self.path,
Ub(DanglingIntPointer(..) | InvalidFunctionPointer(..)) =>
Ub(DanglingIntPointer{ .. } | InvalidFunctionPointer(..)) =>
InvalidFnPtr { value: format!("{ptr}") },
);
// FIXME: Check if the signature matches

View File

@ -329,16 +329,21 @@ pub enum UndefinedBehaviorInfo<'tcx> {
/// Using a pointer after it got freed.
PointerUseAfterFree(AllocId, CheckInAllocMsg),
/// Used a pointer outside the bounds it is valid for.
/// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.)
PointerOutOfBounds {
alloc_id: AllocId,
alloc_size: Size,
ptr_offset: i64,
ptr_size: Size,
/// The size of the memory range that was expected to be in-bounds.
inbounds_size: Size,
msg: CheckInAllocMsg,
},
/// Using an integer as a pointer in the wrong way.
DanglingIntPointer(u64, CheckInAllocMsg),
DanglingIntPointer {
addr: u64,
/// The size of the memory range that was expected to be in-bounds (or 0 if we don't know).
inbounds_size: Size,
msg: CheckInAllocMsg,
},
/// Used a pointer with bad alignment.
AlignmentCheckFailed(Misalignment, CheckAlignMsg),
/// Writing to read-only memory.

View File

@ -181,9 +181,12 @@ impl Provenance for CtfeProvenance {
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Print AllocId.
fmt::Debug::fmt(&ptr.provenance.alloc_id(), f)?; // propagates `alternate` flag
// Print offset only if it is non-zero.
if ptr.offset.bytes() > 0 {
write!(f, "+{:#x}", ptr.offset.bytes())?;
// Print offset only if it is non-zero. Print it signed.
let signed_offset = ptr.offset.bytes() as i64;
if signed_offset > 0 {
write!(f, "+{:#x}", signed_offset)?;
} else if signed_offset < 0 {
write!(f, "-{:#x}", signed_offset.unsigned_abs())?;
}
// Print immutable status.
if ptr.provenance.immutable() {

View File

@ -13,7 +13,6 @@ use std::fmt;
use std::ops::Index;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_errors::{DiagArgValue, IntoDiagArg};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd};
@ -702,12 +701,6 @@ impl<'tcx> Pat<'tcx> {
}
}
impl<'tcx> IntoDiagArg for Pat<'tcx> {
fn into_diag_arg(self) -> DiagArgValue {
format!("{self}").into_diag_arg()
}
}
#[derive(Clone, Debug, HashStable, TypeVisitable)]
pub struct Ascription<'tcx> {
pub annotation: CanonicalUserTypeAnnotation<'tcx>,
@ -1080,8 +1073,33 @@ impl<'tcx> PatRangeBoundary<'tcx> {
}
}
impl<'tcx> fmt::Display for Pat<'tcx> {
impl<'tcx> Pat<'tcx> {
/// Prints a [`Pat`] to an owned string, for user-facing diagnostics.
///
/// If we ever switch over to storing subpatterns as `PatId`, this will also
/// need to take a context that can resolve IDs to subpatterns.
pub fn to_string(&self) -> String {
format!("{}", self.display())
}
/// Used internally by [`fmt::Display`] for [`PatDisplay`].
fn display(&self) -> PatDisplay<'_, 'tcx> {
PatDisplay { pat: self }
}
}
/// Wrapper around [`&Pat<'tcx>`][`Pat`] that implements [`fmt::Display`].
///
/// If we ever switch over to storing subpatterns as `PatId`, this will also
/// need to hold a context that can resolve IDs to subpatterns.
struct PatDisplay<'pat, 'tcx> {
pat: &'pat Pat<'tcx>,
}
impl<'pat, 'tcx> fmt::Display for PatDisplay<'pat, 'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let &Self { pat } = self;
// Printing lists is a chore.
let mut first = true;
let mut start_or_continue = |s| {
@ -1094,20 +1112,22 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
};
let mut start_or_comma = || start_or_continue(", ");
match self.kind {
match pat.kind {
PatKind::Wild => write!(f, "_"),
PatKind::Never => write!(f, "!"),
PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"),
PatKind::AscribeUserType { ref subpattern, .. } => {
write!(f, "{}: _", subpattern.display())
}
PatKind::Binding { name, mode, ref subpattern, .. } => {
f.write_str(mode.prefix_str())?;
write!(f, "{name}")?;
if let Some(ref subpattern) = *subpattern {
write!(f, " @ {subpattern}")?;
write!(f, " @ {}", subpattern.display())?;
}
Ok(())
}
PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => {
let variant_and_name = match self.kind {
let variant_and_name = match pat.kind {
PatKind::Variant { adt_def, variant_index, .. } => ty::tls::with(|tcx| {
let variant = adt_def.variant(variant_index);
let adt_did = adt_def.did();
@ -1120,7 +1140,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
};
Some((variant, name))
}),
_ => self.ty.ty_adt_def().and_then(|adt_def| {
_ => pat.ty.ty_adt_def().and_then(|adt_def| {
if !adt_def.is_enum() {
ty::tls::with(|tcx| {
Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did())))
@ -1145,11 +1165,11 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
continue;
}
let name = variant.fields[p.field].name;
write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
write!(f, "{}{}: {}", start_or_comma(), name, p.pattern.display())?;
printed += 1;
}
let is_union = self.ty.ty_adt_def().is_some_and(|adt| adt.is_union());
let is_union = pat.ty.ty_adt_def().is_some_and(|adt| adt.is_union());
if printed < variant.fields.len() && (!is_union || printed == 0) {
write!(f, "{}..", start_or_comma())?;
}
@ -1168,14 +1188,14 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
// Common case: the field is where we expect it.
if let Some(p) = subpatterns.get(i) {
if p.field.index() == i {
write!(f, "{}", p.pattern)?;
write!(f, "{}", p.pattern.display())?;
continue;
}
}
// Otherwise, we have to go looking for it.
if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
write!(f, "{}", p.pattern)?;
write!(f, "{}", p.pattern.display())?;
} else {
write!(f, "_")?;
}
@ -1186,45 +1206,45 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
Ok(())
}
PatKind::Deref { ref subpattern } => {
match self.ty.kind() {
match pat.ty.kind() {
ty::Adt(def, _) if def.is_box() => write!(f, "box ")?,
ty::Ref(_, _, mutbl) => {
write!(f, "&{}", mutbl.prefix_str())?;
}
_ => bug!("{} is a bad Deref pattern type", self.ty),
_ => bug!("{} is a bad Deref pattern type", pat.ty),
}
write!(f, "{subpattern}")
write!(f, "{}", subpattern.display())
}
PatKind::DerefPattern { ref subpattern, .. } => {
write!(f, "deref!({subpattern})")
write!(f, "deref!({})", subpattern.display())
}
PatKind::Constant { value } => write!(f, "{value}"),
PatKind::InlineConstant { def: _, ref subpattern } => {
write!(f, "{} (from inline const)", subpattern)
write!(f, "{} (from inline const)", subpattern.display())
}
PatKind::Range(ref range) => write!(f, "{range}"),
PatKind::Slice { ref prefix, ref slice, ref suffix }
| PatKind::Array { ref prefix, ref slice, ref suffix } => {
write!(f, "[")?;
for p in prefix.iter() {
write!(f, "{}{}", start_or_comma(), p)?;
write!(f, "{}{}", start_or_comma(), p.display())?;
}
if let Some(ref slice) = *slice {
write!(f, "{}", start_or_comma())?;
match slice.kind {
PatKind::Wild => {}
_ => write!(f, "{slice}")?,
_ => write!(f, "{}", slice.display())?,
}
write!(f, "..")?;
}
for p in suffix.iter() {
write!(f, "{}{}", start_or_comma(), p)?;
write!(f, "{}{}", start_or_comma(), p.display())?;
}
write!(f, "]")
}
PatKind::Or { ref pats } => {
for pat in pats.iter() {
write!(f, "{}{}", start_or_continue(" | "), pat)?;
write!(f, "{}{}", start_or_continue(" | "), pat.display())?;
}
Ok(())
}

View File

@ -858,7 +858,7 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> {
pub(crate) span: Span,
pub(crate) origin: &'s str,
#[subdiagnostic]
pub(crate) uncovered: Uncovered<'tcx>,
pub(crate) uncovered: Uncovered,
#[subdiagnostic]
pub(crate) inform: Option<Inform>,
#[subdiagnostic]

View File

@ -1078,7 +1078,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
let suggested_arm = if suggest_the_witnesses {
let pattern = witnesses
.iter()
.map(|witness| cx.hoist_witness_pat(witness).to_string())
.map(|witness| cx.print_witness_pat(witness))
.collect::<Vec<String>>()
.join(" | ");
if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns {
@ -1196,13 +1196,13 @@ fn joined_uncovered_patterns<'p, 'tcx>(
witnesses: &[WitnessPat<'p, 'tcx>],
) -> String {
const LIMIT: usize = 3;
let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.hoist_witness_pat(pat).to_string();
let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.print_witness_pat(pat);
match witnesses {
[] => bug!(),
[witness] => format!("`{}`", cx.hoist_witness_pat(witness)),
[witness] => format!("`{}`", cx.print_witness_pat(witness)),
[head @ .., tail] if head.len() < LIMIT => {
let head: Vec<_> = head.iter().map(pat_to_str).collect();
format!("`{}` and `{}`", head.join("`, `"), cx.hoist_witness_pat(tail))
format!("`{}` and `{}`", head.join("`, `"), cx.print_witness_pat(tail))
}
_ => {
let (head, tail) = witnesses.split_at(LIMIT);

View File

@ -1,6 +1,5 @@
use rustc_errors::{Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic};
use rustc_macros::{LintDiagnostic, Subdiagnostic};
use rustc_middle::thir::Pat;
use rustc_middle::ty::Ty;
use rustc_span::Span;
@ -8,18 +7,18 @@ use crate::rustc::{RustcPatCtxt, WitnessPat};
#[derive(Subdiagnostic)]
#[label(pattern_analysis_uncovered)]
pub struct Uncovered<'tcx> {
pub struct Uncovered {
#[primary_span]
span: Span,
count: usize,
witness_1: Pat<'tcx>,
witness_2: Pat<'tcx>,
witness_3: Pat<'tcx>,
witness_1: String, // a printed pattern
witness_2: String, // a printed pattern
witness_3: String, // a printed pattern
remainder: usize,
}
impl<'tcx> Uncovered<'tcx> {
pub fn new<'p>(
impl Uncovered {
pub fn new<'p, 'tcx>(
span: Span,
cx: &RustcPatCtxt<'p, 'tcx>,
witnesses: Vec<WitnessPat<'p, 'tcx>>,
@ -27,19 +26,13 @@ impl<'tcx> Uncovered<'tcx> {
where
'tcx: 'p,
{
let witness_1 = cx.hoist_witness_pat(witnesses.get(0).unwrap());
let witness_1 = cx.print_witness_pat(witnesses.get(0).unwrap());
Self {
span,
count: witnesses.len(),
// Substitute dummy values if witnesses is smaller than 3. These will never be read.
witness_2: witnesses
.get(1)
.map(|w| cx.hoist_witness_pat(w))
.unwrap_or_else(|| witness_1.clone()),
witness_3: witnesses
.get(2)
.map(|w| cx.hoist_witness_pat(w))
.unwrap_or_else(|| witness_1.clone()),
witness_2: witnesses.get(1).map(|w| cx.print_witness_pat(w)).unwrap_or_default(),
witness_3: witnesses.get(2).map(|w| cx.print_witness_pat(w)).unwrap_or_default(),
witness_1,
remainder: witnesses.len().saturating_sub(3),
}
@ -49,19 +42,19 @@ impl<'tcx> Uncovered<'tcx> {
#[derive(LintDiagnostic)]
#[diag(pattern_analysis_overlapping_range_endpoints)]
#[note]
pub struct OverlappingRangeEndpoints<'tcx> {
pub struct OverlappingRangeEndpoints {
#[label]
pub range: Span,
#[subdiagnostic]
pub overlap: Vec<Overlap<'tcx>>,
pub overlap: Vec<Overlap>,
}
pub struct Overlap<'tcx> {
pub struct Overlap {
pub span: Span,
pub range: Pat<'tcx>,
pub range: String, // a printed pattern
}
impl<'tcx> Subdiagnostic for Overlap<'tcx> {
impl Subdiagnostic for Overlap {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self,
diag: &mut Diag<'_, G>,
@ -78,38 +71,38 @@ impl<'tcx> Subdiagnostic for Overlap<'tcx> {
#[derive(LintDiagnostic)]
#[diag(pattern_analysis_excluside_range_missing_max)]
pub struct ExclusiveRangeMissingMax<'tcx> {
pub struct ExclusiveRangeMissingMax {
#[label]
#[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")]
/// This is an exclusive range that looks like `lo..max` (i.e. doesn't match `max`).
pub first_range: Span,
/// Suggest `lo..=max` instead.
pub suggestion: String,
pub max: Pat<'tcx>,
pub max: String, // a printed pattern
}
#[derive(LintDiagnostic)]
#[diag(pattern_analysis_excluside_range_missing_gap)]
pub struct ExclusiveRangeMissingGap<'tcx> {
pub struct ExclusiveRangeMissingGap {
#[label]
#[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")]
/// This is an exclusive range that looks like `lo..gap` (i.e. doesn't match `gap`).
pub first_range: Span,
pub gap: Pat<'tcx>,
pub gap: String, // a printed pattern
/// Suggest `lo..=gap` instead.
pub suggestion: String,
#[subdiagnostic]
/// All these ranges skipped over `gap` which we think is probably a mistake.
pub gap_with: Vec<GappedRange<'tcx>>,
pub gap_with: Vec<GappedRange>,
}
pub struct GappedRange<'tcx> {
pub struct GappedRange {
pub span: Span,
pub gap: Pat<'tcx>,
pub first_range: Pat<'tcx>,
pub gap: String, // a printed pattern
pub first_range: String, // a printed pattern
}
impl<'tcx> Subdiagnostic for GappedRange<'tcx> {
impl Subdiagnostic for GappedRange {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self,
diag: &mut Diag<'_, G>,
@ -134,7 +127,7 @@ impl<'tcx> Subdiagnostic for GappedRange<'tcx> {
pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
pub scrut_ty: Ty<'tcx>,
#[subdiagnostic]
pub uncovered: Uncovered<'tcx>,
pub uncovered: Uncovered,
}
#[derive(LintDiagnostic)]

View File

@ -743,7 +743,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
/// Note: it is possible to get `isize/usize::MAX+1` here, as explained in the doc for
/// [`IntRange::split`]. This cannot be represented as a `Const`, so we represent it with
/// `PosInfinity`.
pub(crate) fn hoist_pat_range_bdy(
fn hoist_pat_range_bdy(
&self,
miint: MaybeInfiniteInt,
ty: RevealedTy<'tcx>,
@ -774,7 +774,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
}
/// Convert back to a `thir::Pat` for diagnostic purposes.
pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> Pat<'tcx> {
fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> Pat<'tcx> {
use MaybeInfiniteInt::*;
let cx = self;
let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
@ -810,9 +810,17 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
Pat { ty: ty.inner(), span: DUMMY_SP, kind }
}
/// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes.
pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
// This works by converting the witness pattern back to a `thir::Pat`
// and then printing that, but callers don't need to know that.
self.hoist_witness_pat(pat).to_string()
}
/// Convert back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't
/// appear in diagnostics, like float ranges.
pub fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> Pat<'tcx> {
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> Pat<'tcx> {
let cx = self;
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p)));
@ -965,7 +973,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
let overlaps: Vec<_> = overlaps_with
.iter()
.map(|pat| pat.data().span)
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
.map(|span| errors::Overlap { range: overlap_as_pat.to_string(), span })
.collect();
let pat_span = pat.data().span;
self.tcx.emit_node_span_lint(
@ -1013,7 +1021,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
// Point at this range.
first_range: thir_pat.span,
// That's the gap that isn't covered.
max: gap_as_pat.clone(),
max: gap_as_pat.to_string(),
// Suggest `lo..=max` instead.
suggestion: suggested_range.to_string(),
},
@ -1027,7 +1035,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
// Point at this range.
first_range: thir_pat.span,
// That's the gap that isn't covered.
gap: gap_as_pat.clone(),
gap: gap_as_pat.to_string(),
// Suggest `lo..=gap` instead.
suggestion: suggested_range.to_string(),
// All these ranges skipped over `gap` which we think is probably a
@ -1036,8 +1044,8 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
.iter()
.map(|pat| errors::GappedRange {
span: pat.data().span,
gap: gap_as_pat.clone(),
first_range: thir_pat.clone(),
gap: gap_as_pat.to_string(),
first_range: thir_pat.to_string(),
})
.collect(),
},

View File

@ -86,6 +86,7 @@
#![warn(multiple_supertrait_upcastable)]
#![allow(internal_features)]
#![allow(rustdoc::redundant_explicit_links)]
#![warn(rustdoc::unescaped_backticks)]
#![deny(ffi_unwind_calls)]
//
// Library features:

View File

@ -103,6 +103,7 @@
#![deny(ffi_unwind_calls)]
// Do not check link redundancy on bootstraping phase
#![allow(rustdoc::redundant_explicit_links)]
#![warn(rustdoc::unescaped_backticks)]
//
// Library features:
// tidy-alphabetical-start

View File

@ -282,7 +282,7 @@ impl<T: ?Sized> DerefMut for &mut T {
/// FIXME(deref_patterns): The precise semantics are undecided; the rough idea is that
/// successive calls to `deref`/`deref_mut` without intermediate mutation should be
/// idempotent, in the sense that they return the same value as far as pattern-matching
/// is concerned. Calls to `deref`/`deref_mut`` must leave the pointer itself likewise
/// is concerned. Calls to `deref`/`deref_mut` must leave the pointer itself likewise
/// unchanged.
#[unstable(feature = "deref_pure_trait", issue = "87121")]
#[lang = "deref_pure"]

View File

@ -780,7 +780,7 @@ where
///
/// The caller must also ensure that the memory the pointer (non-transitively) points to is never
/// written to (except inside an `UnsafeCell`) using this pointer or any pointer derived from it. If
/// you need to mutate the pointee, use [`from_mut`]`. Specifically, to turn a mutable reference `m:
/// you need to mutate the pointee, use [`from_mut`]. Specifically, to turn a mutable reference `m:
/// &mut T` into `*const T`, prefer `from_mut(m).cast_const()` to obtain a pointer that can later be
/// used for mutation.
///

View File

@ -3435,8 +3435,8 @@ impl<T> [T] {
/// elements of the slice move to the end while the last `k` elements move
/// to the front.
///
/// After calling `rotate_right`, the element previously at index `self.len()
/// - k` will become the first element in the slice.
/// After calling `rotate_right`, the element previously at index
/// `self.len() - k` will become the first element in the slice.
///
/// # Panics
///

View File

@ -37,6 +37,7 @@
#![recursion_limit = "256"]
#![allow(internal_features)]
#![deny(ffi_unwind_calls)]
#![warn(rustdoc::unescaped_backticks)]
#[unstable(feature = "proc_macro_internals", issue = "27812")]
#[doc(hidden)]

View File

@ -254,6 +254,7 @@
#![deny(fuzzy_provenance_casts)]
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(rustdoc::redundant_explicit_links)]
#![warn(rustdoc::unescaped_backticks)]
// Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind`
#![deny(ffi_unwind_calls)]
// std may use features in a platform-specific way

View File

@ -24,6 +24,7 @@
#![feature(panic_can_unwind)]
#![feature(test)]
#![allow(internal_features)]
#![warn(rustdoc::unescaped_backticks)]
pub use cli::TestOpts;

View File

@ -3,7 +3,7 @@
use std::path::PathBuf;
use crate::core::build_steps::compile::{
add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo,
add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make,
};
use crate::core::build_steps::tool::{prepare_tool_cargo, SourceType};
use crate::core::builder::{
@ -49,7 +49,7 @@ impl Step for Std {
}
fn make_run(run: RunConfig<'_>) {
let crates = run.make_run_crates(Alias::Library);
let crates = std_crates_for_run_make(&run);
run.builder.ensure(Std { target: run.target, crates, override_build_kind: None });
}

View File

@ -4,6 +4,7 @@ use super::compile::{librustc_stamp, libstd_stamp, run_cargo, rustc_cargo, std_c
use super::tool::{prepare_tool_cargo, SourceType};
use super::{check, compile};
use crate::builder::{Builder, ShouldRun};
use crate::core::build_steps::compile::std_crates_for_run_make;
use crate::core::builder;
use crate::core::builder::{crate_description, Alias, Kind, RunConfig, Step};
use crate::{Mode, Subcommand, TargetSelection};
@ -106,7 +107,7 @@ impl Step for Std {
}
fn make_run(run: RunConfig<'_>) {
let crates = run.make_run_crates(Alias::Library);
let crates = std_crates_for_run_make(&run);
run.builder.ensure(Std { target: run.target, crates });
}

View File

@ -123,11 +123,7 @@ impl Step for Std {
}
fn make_run(run: RunConfig<'_>) {
// If the paths include "library", build the entire standard library.
let has_alias =
run.paths.iter().any(|set| set.assert_single_path().path.ends_with("library"));
let crates = if has_alias { Default::default() } else { run.cargo_crates_in_set() };
let crates = std_crates_for_run_make(&run);
run.builder.ensure(Std {
compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
target: run.target,
@ -425,6 +421,28 @@ fn copy_self_contained_objects(
target_deps
}
/// Resolves standard library crates for `Std::run_make` for any build kind (like check, build, clippy, etc.).
pub fn std_crates_for_run_make(run: &RunConfig<'_>) -> Vec<String> {
// FIXME: Extend builder tests to cover the `crates` field of `Std` instances.
if cfg!(feature = "bootstrap-self-test") {
return vec![];
}
let has_alias = run.paths.iter().any(|set| set.assert_single_path().path.ends_with("library"));
let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false);
// For no_std targets, do not add any additional crates to the compilation other than what `compile::std_cargo` already adds for no_std targets.
if target_is_no_std {
vec![]
}
// If the paths include "library", build the entire standard library.
else if has_alias {
run.make_run_crates(builder::Alias::Library)
} else {
run.cargo_crates_in_set()
}
}
/// Configure cargo to compile the standard library, adding appropriate env vars
/// and such.
pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, cargo: &mut Cargo) {

View File

@ -106,7 +106,6 @@ impl Step for JsonDocs {
builder.ensure(crate::core::build_steps::doc::Std::new(
builder.top_stage,
host,
builder,
DocumentationFormat::Json,
));

View File

@ -564,18 +564,8 @@ pub struct Std {
}
impl Std {
pub(crate) fn new(
stage: u32,
target: TargetSelection,
builder: &Builder<'_>,
format: DocumentationFormat,
) -> Self {
let crates = builder
.in_tree_crates("sysroot", Some(target))
.into_iter()
.map(|krate| krate.name.to_string())
.collect();
Std { stage, target, format, crates }
pub(crate) fn new(stage: u32, target: TargetSelection, format: DocumentationFormat) -> Self {
Std { stage, target, format, crates: vec![] }
}
}
@ -589,6 +579,7 @@ impl Step for Std {
}
fn make_run(run: RunConfig<'_>) {
let crates = compile::std_crates_for_run_make(&run);
run.builder.ensure(Std {
stage: run.builder.top_stage,
target: run.target,
@ -597,7 +588,7 @@ impl Step for Std {
} else {
DocumentationFormat::Html
},
crates: run.make_run_crates(Alias::Library),
crates,
});
}
@ -695,13 +686,6 @@ fn doc_std(
extra_args: &[&str],
requested_crates: &[String],
) {
if builder.no_std(target) == Some(true) {
panic!(
"building std documentation for no_std target {target} is not supported\n\
Set `docs = false` in the config to disable documentation, or pass `--skip library`."
);
}
let compiler = builder.compiler(stage, builder.config.build);
let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" };

View File

@ -847,7 +847,6 @@ impl Step for RustdocJSStd {
builder.ensure(crate::core::build_steps::doc::Std::new(
builder.top_stage,
self.target,
builder,
DocumentationFormat::Html,
));
let _guard = builder.msg(

View File

@ -79,13 +79,9 @@ macro_rules! std {
macro_rules! doc_std {
($host:ident => $target:ident, stage = $stage:literal) => {{
let config = configure("doc", &["A-A"], &["A-A"]);
let build = Build::new(config);
let builder = Builder::new(&build);
doc::Std::new(
$stage,
TargetSelection::from_user(concat!(stringify!($target), "-", stringify!($target))),
&builder,
DocumentationFormat::Html,
)
}};

View File

@ -2,7 +2,7 @@
set -euo pipefail
LINUX_VERSION=c13320499ba0efd93174ef6462ae8a7a2933f6e7
LINUX_VERSION=v6.11-rc1
# Build rustc, rustdoc and cargo
../x.py build --stage 1 library rustdoc

View File

@ -1 +1 @@
99b7134389e9766462601a2fc4013840b9d31745
a526d7ce45fd2284e0e7c7556ccba2425b9d25e5

View File

@ -620,6 +620,14 @@ fn main() {
"-Zmiri-unique-is-unique only has an effect when -Zmiri-tree-borrows is also used"
);
}
// Tree Borrows + permissive provenance does not work.
if miri_config.provenance_mode == ProvenanceMode::Permissive
&& matches!(miri_config.borrow_tracker, Some(BorrowTrackerMethod::TreeBorrows))
{
show_error!(
"Tree Borrows does not support integer-to-pointer casts, and is hence not compatible with permissive provenance"
);
}
debug!("rustc arguments: {:?}", rustc_args);
debug!("crate arguments: {:?}", miri_config.args);

View File

@ -232,6 +232,10 @@ impl GlobalStateInner {
pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_>) {
self.root_ptr_tags.retain(|id, _| allocs.is_live(*id));
}
pub fn borrow_tracker_method(&self) -> BorrowTrackerMethod {
self.borrow_tracker_method
}
}
/// Which borrow tracking method to use

View File

@ -5,6 +5,7 @@ pub mod diagnostics;
mod item;
mod stack;
use std::cell::RefCell;
use std::cmp;
use std::fmt::Write;
use std::mem;
@ -820,7 +821,19 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/276.
let size = match size {
Some(size) => size,
None => return Ok(place.clone()),
None => {
// The first time this happens, show a warning.
thread_local! { static WARNING_SHOWN: RefCell<bool> = const { RefCell::new(false) }; }
WARNING_SHOWN.with_borrow_mut(|shown| {
if *shown {
return;
}
// Not yet shown. Show it!
*shown = true;
this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow);
});
return Ok(place.clone());
}
};
// Compute new borrow.

View File

@ -141,8 +141,15 @@ impl<'tcx> NewPermission {
) -> Option<Self> {
let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.param_env());
let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.param_env());
let is_protected = kind == RetagKind::FnEntry;
// As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`,
// interior mutability and protectors interact poorly.
// To eliminate the case of Protected Reserved IM we override interior mutability
// in the case of a protected reference: protected references are always considered
// "freeze".
let initial_state = match mutability {
Mutability::Mut if ty_is_unpin => Permission::new_reserved(ty_is_freeze),
Mutability::Mut if ty_is_unpin =>
Permission::new_reserved(ty_is_freeze || is_protected),
Mutability::Not if ty_is_freeze => Permission::new_frozen(),
// Raw pointers never enter this function so they are not handled.
// However raw pointers are not the only pointers that take the parent
@ -151,7 +158,7 @@ impl<'tcx> NewPermission {
_ => return None,
};
let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector);
let protector = is_protected.then_some(ProtectorKind::StrongProtector);
Some(Self { zero_size: false, initial_state, protector })
}

View File

@ -22,6 +22,11 @@ enum PermissionPriv {
/// - foreign-read then child-write is UB due to `conflicted`,
/// - child-write then foreign-read is UB since child-write will activate and then
/// foreign-read disables a protected `Active`, which is UB.
///
/// Note: since the discovery of `tests/fail/tree_borrows/reservedim_spurious_write.rs`,
/// `ty_is_freeze` does not strictly mean that the type has no interior mutability,
/// it could be an interior mutable type that lost its interior mutability privileges
/// when retagged with a protector.
Reserved { ty_is_freeze: bool, conflicted: bool },
/// represents: a unique pointer;
/// allows: child reads, child writes;
@ -141,6 +146,12 @@ mod transition {
/// non-protected interior mutable `Reserved` which stay the same.
fn foreign_write(state: PermissionPriv, protected: bool) -> Option<PermissionPriv> {
Some(match state {
// FIXME: since the fix related to reservedim_spurious_write, it is now possible
// to express these transitions of the state machine without an explicit dependency
// on `protected`: because `ty_is_freeze: false` implies `!protected` then
// the line handling `Reserved { .. } if protected` could be deleted.
// This will however require optimizations to the exhaustive tests because
// fewer initial conditions are valid.
Reserved { .. } if protected => Disabled,
res @ Reserved { ty_is_freeze: false, .. } => res,
_ => Disabled,

View File

@ -130,6 +130,7 @@ pub enum NonHaltingDiagnostic {
WeakMemoryOutdatedLoad {
ptr: Pointer,
},
ExternTypeReborrow,
}
/// Level of Miri specific diagnostics
@ -139,6 +140,15 @@ pub enum DiagLevel {
Note,
}
/// Generate a note/help text without a span.
macro_rules! note {
($($tt:tt)*) => { (None, format!($($tt)*)) };
}
/// Generate a note/help text with a span.
macro_rules! note_span {
($span:expr, $($tt:tt)*) => { (Some($span), format!($($tt)*)) };
}
/// Attempts to prune a stacktrace to omit the Rust runtime, and returns a bool indicating if any
/// frames were pruned. If the stacktrace does not have any local frames, we conclude that it must
/// be pointing to a problem in the Rust runtime itself, and do not prune it at all.
@ -227,38 +237,38 @@ pub fn report_error<'tcx>(
let helps = match info {
UnsupportedInIsolation(_) =>
vec![
(None, format!("set `MIRIFLAGS=-Zmiri-disable-isolation` to disable isolation;")),
(None, format!("or set `MIRIFLAGS=-Zmiri-isolation-error=warn` to make Miri return an error code from isolated operations (if supported for that operation) and continue with a warning")),
note!("set `MIRIFLAGS=-Zmiri-disable-isolation` to disable isolation;"),
note!("or set `MIRIFLAGS=-Zmiri-isolation-error=warn` to make Miri return an error code from isolated operations (if supported for that operation) and continue with a warning"),
],
UnsupportedForeignItem(_) => {
vec![
(None, format!("if this is a basic API commonly used on this target, please report an issue with Miri")),
(None, format!("however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases")),
note!("if this is a basic API commonly used on this target, please report an issue with Miri"),
note!("however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases"),
]
}
StackedBorrowsUb { help, history, .. } => {
msg.extend(help.clone());
let mut helps = vec![
(None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental")),
(None, format!("see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information")),
note!("this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental"),
note!("see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information"),
];
if let Some(TagHistory {created, invalidated, protected}) = history.clone() {
helps.push((Some(created.1), created.0));
if let Some((msg, span)) = invalidated {
helps.push((Some(span), msg));
helps.push(note_span!(span, "{msg}"));
}
if let Some((protector_msg, protector_span)) = protected {
helps.push((Some(protector_span), protector_msg));
helps.push(note_span!(protector_span, "{protector_msg}"));
}
}
helps
},
TreeBorrowsUb { title: _, details, history } => {
let mut helps = vec![
(None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental"))
note!("this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental")
];
for m in details {
helps.push((None, m.clone()));
helps.push(note!("{m}"));
}
for event in history.events.clone() {
helps.push(event);
@ -267,26 +277,26 @@ pub fn report_error<'tcx>(
}
MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
vec![
(Some(*first), format!("it's first defined here, in crate `{first_crate}`")),
(Some(*second), format!("then it's defined here again, in crate `{second_crate}`")),
note_span!(*first, "it's first defined here, in crate `{first_crate}`"),
note_span!(*second, "then it's defined here again, in crate `{second_crate}`"),
],
SymbolShimClashing { link_name, span } =>
vec![(Some(*span), format!("the `{link_name}` symbol is defined here"))],
vec![note_span!(*span, "the `{link_name}` symbol is defined here")],
Int2PtrWithStrictProvenance =>
vec![(None, format!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"))],
vec![note!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead")],
DataRace { op1, extra, retag_explain, .. } => {
let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))];
let mut helps = vec![note_span!(op1.span, "and (1) occurred earlier here")];
if let Some(extra) = extra {
helps.push((None, format!("{extra}")));
helps.push((None, format!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model")));
helps.push(note!("{extra}"));
helps.push(note!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model"));
}
if *retag_explain {
helps.push((None, "retags occur on all (re)borrows and as well as when references are copied or moved".to_owned()));
helps.push((None, "retags permit optimizations that insert speculative reads or writes".to_owned()));
helps.push((None, "therefore from the perspective of data races, a retag has the same implications as a read or write".to_owned()));
helps.push(note!("retags occur on all (re)borrows and as well as when references are copied or moved"));
helps.push(note!("retags permit optimizations that insert speculative reads or writes"));
helps.push(note!("therefore from the perspective of data races, a retag has the same implications as a read or write"));
}
helps.push((None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")));
helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")));
helps.push(note!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"));
helps.push(note!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"));
helps
}
,
@ -331,32 +341,32 @@ pub fn report_error<'tcx>(
let helps = match e.kind() {
Unsupported(_) =>
vec![
(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support")),
note!("this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support"),
],
UndefinedBehavior(AlignmentCheckFailed { .. })
if ecx.machine.check_alignment == AlignmentCheck::Symbolic
=>
vec![
(None, format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior")),
(None, format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives")),
note!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"),
note!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives"),
],
UndefinedBehavior(info) => {
let mut helps = vec![
(None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")),
(None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")),
note!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
note!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
];
match info {
PointerUseAfterFree(alloc_id, _) | PointerOutOfBounds { alloc_id, .. } => {
if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
helps.push((Some(span), format!("{:?} was allocated here:", alloc_id)));
helps.push(note_span!(span, "{:?} was allocated here:", alloc_id));
}
if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
helps.push((Some(span), format!("{:?} was deallocated here:", alloc_id)));
helps.push(note_span!(span, "{:?} was deallocated here:", alloc_id));
}
}
AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => {
helps.push((None, format!("this means these two types are not *guaranteed* to be ABI-compatible across all targets")));
helps.push((None, format!("if you think this code should be accepted anyway, please report an issue with Miri")));
helps.push(note!("this means these two types are not *guaranteed* to be ABI-compatible across all targets"));
helps.push(note!("if you think this code should be accepted anyway, please report an issue with Miri"));
}
_ => {},
}
@ -593,6 +603,8 @@ impl<'tcx> MiriMachine<'tcx> {
RejectedIsolatedOp(_) =>
("operation rejected by isolation".to_string(), DiagLevel::Warning),
Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
ExternTypeReborrow =>
("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
CreatedPointerTag(..)
| PoppedPointerTag(..)
| CreatedCallId(..)
@ -630,51 +642,56 @@ impl<'tcx> MiriMachine<'tcx> {
Int2Ptr { .. } => format!("integer-to-pointer cast"),
WeakMemoryOutdatedLoad { ptr } =>
format!("weak memory emulation: outdated value returned from load at {ptr}"),
ExternTypeReborrow =>
format!("reborrow of a reference to `extern type` is not properly supported"),
};
let notes = match &e {
ProgressReport { block_count } => {
// It is important that each progress report is slightly different, since
// identical diagnostics are being deduplicated.
vec![(None, format!("so far, {block_count} basic blocks have been executed"))]
vec![note!("so far, {block_count} basic blocks have been executed")]
}
_ => vec![],
};
let helps = match &e {
Int2Ptr { details: true } =>
Int2Ptr { details: true } => {
let mut v = vec![
note!(
"this program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program"
),
note!(
"see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation"
),
note!(
"to ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"
),
note!(
"you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics"
),
];
if self.borrow_tracker.as_ref().is_some_and(|b| {
matches!(b.borrow().borrow_tracker_method(), BorrowTrackerMethod::TreeBorrows)
}) {
v.push(
note!("Tree Borrows does not support integer-to-pointer casts, so the program is likely to go wrong when this pointer gets used")
);
} else {
v.push(
note!("alternatively, `MIRIFLAGS=-Zmiri-permissive-provenance` disables this warning")
);
}
v
}
ExternTypeReborrow => {
vec![
(
None,
format!(
"This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program."
),
note!(
"`extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code"
),
(
None,
format!(
"See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation."
),
note!(
"try running with `MIRIFLAGS=-Zmiri-tree-borrows` to use the more permissive but also even more experimental Tree Borrows aliasing checks instead"
),
(
None,
format!(
"To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead."
),
),
(
None,
format!(
"You can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics."
),
),
(
None,
format!(
"Alternatively, `MIRIFLAGS=-Zmiri-permissive-provenance` disables this warning."
),
),
],
]
}
_ => vec![],
};

View File

@ -660,7 +660,7 @@ impl<'tcx> MiriMachine<'tcx> {
tls: TlsData::default(),
isolated_op: config.isolated_op,
validate: config.validate,
fds: shims::FdTable::new(config.mute_stdout_stderr),
fds: shims::FdTable::init(config.mute_stdout_stderr),
dirs: Default::default(),
layouts,
threads,

View File

@ -108,4 +108,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
EnvVars::Windows(vars) => vars.get(name),
}
}
fn get_pid(&self) -> u32 {
let this = self.eval_context_ref();
if this.machine.communicate() { std::process::id() } else { 1000 }
}
}

View File

@ -274,12 +274,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("getpid");
this.check_no_isolation("`getpid`")?;
// The reason we need to do this wacky of a conversion is because
// `libc::getpid` returns an i32, however, `std::process::id()` return an u32.
// So we un-do the conversion that stdlib does and turn it back into an i32.
#[allow(clippy::cast_possible_wrap)]
Ok(std::process::id() as i32)
Ok(this.get_pid() as i32)
}
fn linux_gettid(&mut self) -> InterpResult<'tcx, i32> {
let this = self.eval_context_ref();
this.assert_target_os("linux", "gettid");
let index = this.machine.threads.active_thread().to_u32();
// Compute a TID for this thread, ensuring that the main thread has PID == TID.
let tid = this.get_pid().strict_add(index);
#[allow(clippy::cast_possible_wrap)]
Ok(tid as i32)
}
}

View File

@ -36,6 +36,30 @@ pub trait FileDescription: std::fmt::Debug + Any {
throw_unsup_format!("cannot write to {}", self.name());
}
/// Reads as much as possible into the given buffer from a given offset,
/// and returns the number of bytes read.
fn pread<'tcx>(
&mut self,
_communicate_allowed: bool,
_bytes: &mut [u8],
_offset: u64,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
throw_unsup_format!("cannot pread from {}", self.name());
}
/// Writes as much as possible from the given buffer starting at a given offset,
/// and returns the number of bytes written.
fn pwrite<'tcx>(
&mut self,
_communicate_allowed: bool,
_bytes: &[u8],
_offset: u64,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
throw_unsup_format!("cannot pwrite to {}", self.name());
}
/// Seeks to the given offset (which can be relative to the beginning, end, or current position).
/// Returns the new position from the start of the stream.
fn seek<'tcx>(
@ -168,10 +192,6 @@ impl FileDescription for NullOutput {
pub struct FileDescriptor(Rc<RefCell<Box<dyn FileDescription>>>);
impl FileDescriptor {
pub fn new<T: FileDescription>(fd: T) -> Self {
FileDescriptor(Rc::new(RefCell::new(Box::new(fd))))
}
pub fn borrow(&self) -> Ref<'_, dyn FileDescription> {
Ref::map(self.0.borrow(), |fd| fd.as_ref())
}
@ -203,20 +223,25 @@ impl VisitProvenance for FdTable {
}
impl FdTable {
pub(crate) fn new(mute_stdout_stderr: bool) -> FdTable {
let mut fds: BTreeMap<_, FileDescriptor> = BTreeMap::new();
fds.insert(0i32, FileDescriptor::new(io::stdin()));
fn new() -> Self {
FdTable { fds: BTreeMap::new() }
}
pub(crate) fn init(mute_stdout_stderr: bool) -> FdTable {
let mut fds = FdTable::new();
fds.insert_fd(io::stdin());
if mute_stdout_stderr {
fds.insert(1i32, FileDescriptor::new(NullOutput));
fds.insert(2i32, FileDescriptor::new(NullOutput));
assert_eq!(fds.insert_fd(NullOutput), 1);
assert_eq!(fds.insert_fd(NullOutput), 2);
} else {
fds.insert(1i32, FileDescriptor::new(io::stdout()));
fds.insert(2i32, FileDescriptor::new(io::stderr()));
assert_eq!(fds.insert_fd(io::stdout()), 1);
assert_eq!(fds.insert_fd(io::stderr()), 2);
}
FdTable { fds }
fds
}
pub fn insert_fd(&mut self, file_handle: FileDescriptor) -> i32 {
/// Insert a file descriptor to the FdTable.
pub fn insert_fd<T: FileDescription>(&mut self, fd: T) -> i32 {
let file_handle = FileDescriptor(Rc::new(RefCell::new(Box::new(fd))));
self.insert_fd_with_min_fd(file_handle, 0)
}
@ -380,7 +405,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok((-1).into())
}
fn read(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> {
/// Read data from `fd` into buffer specified by `buf` and `count`.
///
/// If `offset` is `None`, reads data from current cursor position associated with `fd`
/// and updates cursor position on completion. Otherwise, reads from the specified offset
/// and keeps the cursor unchanged.
fn read(
&mut self,
fd: i32,
buf: Pointer,
count: u64,
offset: Option<i128>,
) -> InterpResult<'tcx, i64> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait.
@ -398,25 +434,31 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let communicate = this.machine.communicate();
// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(file_descriptor) = this.machine.fds.dup(fd) else {
let Some(fd) = this.machine.fds.dup(fd) else {
trace!("read: FD not found");
return this.fd_not_found();
};
trace!("read: FD mapped to {:?}", file_descriptor);
trace!("read: FD mapped to {fd:?}");
// We want to read at most `count` bytes. We are sure that `count` is not negative
// because it was a target's `usize`. Also we are sure that its smaller than
// `usize::MAX` because it is bounded by the host's `isize`.
let mut bytes = vec![0; usize::try_from(count).unwrap()];
// `File::read` never returns a value larger than `count`,
// so this cannot fail.
let result = file_descriptor
.borrow_mut()
.read(communicate, &mut bytes, this)?
.map(|c| i64::try_from(c).unwrap());
drop(file_descriptor);
let result = match offset {
None => fd.borrow_mut().read(communicate, &mut bytes, this),
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
return Ok(-1);
};
fd.borrow_mut().pread(communicate, &mut bytes, offset, this)
}
};
drop(fd);
match result {
// `File::read` never returns a value larger than `count`, so this cannot fail.
match result?.map(|c| i64::try_from(c).unwrap()) {
Ok(read_bytes) => {
// If reading to `bytes` did not fail, we write those bytes to the buffer.
// Crucially, if fewer than `bytes.len()` bytes were read, only write
@ -434,7 +476,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn write(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> {
fn write(
&mut self,
fd: i32,
buf: Pointer,
count: u64,
offset: Option<i128>,
) -> InterpResult<'tcx, i64> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait.
@ -451,16 +499,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(file_descriptor) = this.machine.fds.dup(fd) else {
let Some(fd) = this.machine.fds.dup(fd) else {
return this.fd_not_found();
};
let result = file_descriptor
.borrow_mut()
.write(communicate, &bytes, this)?
.map(|c| i64::try_from(c).unwrap());
drop(file_descriptor);
let result = match offset {
None => fd.borrow_mut().write(communicate, &bytes, this),
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
return Ok(-1);
};
fd.borrow_mut().pwrite(communicate, &bytes, offset, this)
}
};
drop(fd);
let result = result?.map(|c| i64::try_from(c).unwrap());
this.try_unwrap_io_result(result)
}
}

View File

@ -92,7 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let result = this.read(fd, buf, count)?;
let result = this.read(fd, buf, count, None)?;
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"write" => {
@ -101,7 +101,47 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
let result = this.write(fd, buf, count)?;
let result = this.write(fd, buf, count, None)?;
// Now, `result` is the value we return back to the program.
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"pread" => {
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let result = this.read(fd, buf, count, Some(offset))?;
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"pwrite" => {
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
let result = this.write(fd, buf, count, Some(offset))?;
// Now, `result` is the value we return back to the program.
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"pread64" => {
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
let result = this.read(fd, buf, count, Some(offset))?;
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}
"pwrite64" => {
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
let result = this.write(fd, buf, count, Some(offset))?;
// Now, `result` is the value we return back to the program.
this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
}

View File

@ -16,8 +16,6 @@ use crate::shims::unix::*;
use crate::*;
use shims::time::system_time_to_duration;
use self::fd::FileDescriptor;
#[derive(Debug)]
struct FileHandle {
file: File,
@ -49,6 +47,54 @@ impl FileDescription for FileHandle {
Ok(self.file.write(bytes))
}
fn pread<'tcx>(
&mut self,
communicate_allowed: bool,
bytes: &mut [u8],
offset: u64,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
// Emulates pread using seek + read + seek to restore cursor position.
// Correctness of this emulation relies on sequential nature of Miri execution.
// The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
let mut f = || {
let cursor_pos = self.file.stream_position()?;
self.file.seek(SeekFrom::Start(offset))?;
let res = self.file.read(bytes);
// Attempt to restore cursor position even if the read has failed
self.file
.seek(SeekFrom::Start(cursor_pos))
.expect("failed to restore file position, this shouldn't be possible");
res
};
Ok(f())
}
fn pwrite<'tcx>(
&mut self,
communicate_allowed: bool,
bytes: &[u8],
offset: u64,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
// Emulates pwrite using seek + write + seek to restore cursor position.
// Correctness of this emulation relies on sequential nature of Miri execution.
// The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
let mut f = || {
let cursor_pos = self.file.stream_position()?;
self.file.seek(SeekFrom::Start(offset))?;
let res = self.file.write(bytes);
// Attempt to restore cursor position even if the write has failed
self.file
.seek(SeekFrom::Start(cursor_pos))
.expect("failed to restore file position, this shouldn't be possible");
res
};
Ok(f())
}
fn seek<'tcx>(
&mut self,
communicate_allowed: bool,
@ -266,7 +312,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
let path = this.read_pointer(&args[0])?;
let path_raw = this.read_pointer(&args[0])?;
let path = this.read_path_from_c_str(path_raw)?;
let flag = this.read_scalar(&args[1])?.to_i32()?;
let mut options = OpenOptions::new();
@ -366,14 +413,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return Ok(-1);
}
}
let o_nofollow = this.eval_libc_i32("O_NOFOLLOW");
if flag & o_nofollow == o_nofollow {
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
options.custom_flags(libc::O_NOFOLLOW);
}
// Strictly speaking, this emulation is not equivalent to the O_NOFOLLOW flag behavior:
// the path could change between us checking it here and the later call to `open`.
// But it's good enough for Miri purposes.
#[cfg(not(unix))]
{
// O_NOFOLLOW only fails when the trailing component is a symlink;
// the entire rest of the path can still contain symlinks.
if path.is_symlink() {
let eloop = this.eval_libc("ELOOP");
this.set_last_error(eloop)?;
return Ok(-1);
}
}
mirror |= o_nofollow;
}
// If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
// then we throw an error.
if flag != mirror {
throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
}
let path = this.read_path_from_c_str(path)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`open`", reject_with)?;
@ -381,10 +450,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return Ok(-1);
}
let fd = options.open(path).map(|file| {
let fh = &mut this.machine.fds;
fh.insert_fd(FileDescriptor::new(FileHandle { file, writable }))
});
let fd = options
.open(path)
.map(|file| this.machine.fds.insert_fd(FileHandle { file, writable }));
this.try_unwrap_io_result(fd)
}
@ -1476,9 +1544,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match file {
Ok(f) => {
let fh = &mut this.machine.fds;
let fd =
fh.insert_fd(FileDescriptor::new(FileHandle { file: f, writable: true }));
let fd = this.machine.fds.insert_fd(FileHandle { file: f, writable: true });
return Ok(fd);
}
Err(e) =>

View File

@ -5,8 +5,6 @@ use rustc_data_structures::fx::FxHashMap;
use crate::shims::unix::*;
use crate::*;
use self::shims::unix::fd::FileDescriptor;
/// An `Epoll` file descriptor connects file handles and epoll events
#[derive(Clone, Debug, Default)]
struct Epoll {
@ -66,7 +64,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
);
}
let fd = this.machine.fds.insert_fd(FileDescriptor::new(Epoll::default()));
let fd = this.machine.fds.insert_fd(Epoll::default());
Ok(Scalar::from_i32(fd))
}

View File

@ -8,8 +8,6 @@ use rustc_target::abi::Endian;
use crate::shims::unix::*;
use crate::{concurrency::VClock, *};
use self::shims::unix::fd::FileDescriptor;
// We'll only do reads and writes in chunks of size u64.
const U64_ARRAY_SIZE: usize = mem::size_of::<u64>();
@ -180,11 +178,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
throw_unsup_format!("eventfd: encountered unknown unsupported flags {:#x}", flags);
}
let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event {
let fd = this.machine.fds.insert_fd(Event {
counter: val.into(),
is_nonblock,
clock: VClock::default(),
}));
});
Ok(Scalar::from_i32(fd))
}
}

View File

@ -94,6 +94,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
)?;
this.write_scalar(res, dest)?;
}
"gettid" => {
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.linux_gettid()?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
// Dynamically invoked syscalls
"syscall" => {

View File

@ -7,8 +7,6 @@ use std::rc::{Rc, Weak};
use crate::shims::unix::*;
use crate::{concurrency::VClock, *};
use self::fd::FileDescriptor;
/// The maximum capacity of the socketpair buffer in bytes.
/// This number is arbitrary as the value can always
/// be configured in the real system.
@ -221,9 +219,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let fds = &mut this.machine.fds;
let sv0 = fds.insert_fd(FileDescriptor::new(socketpair_0));
let sv0 = fds.insert_fd(socketpair_0);
let sv1 = fds.insert_fd(socketpair_1);
let sv0 = Scalar::from_int(sv0, sv.layout.size);
let sv1 = fds.insert_fd(FileDescriptor::new(socketpair_1));
let sv1 = Scalar::from_int(sv1, sv.layout.size);
this.write_scalar(sv0, &sv)?;

View File

@ -200,9 +200,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, u32> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "GetCurrentProcessId");
this.check_no_isolation("`GetCurrentProcessId`")?;
Ok(std::process::id())
Ok(this.get_pid())
}
#[allow(non_snake_case)]

View File

@ -1178,7 +1178,7 @@ fn pclmulqdq<'tcx>(
// if the i-th bit in right is set
if (right & (1 << i)) != 0 {
// xor result with `left` shifted to the left by i positions
result ^= (left as u128) << i;
result ^= u128::from(left) << i;
}
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: ALLOC has size 128, so pointer to 129 bytes starting at offset 0 is out-of-bounds
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
--> $DIR/affinity.rs:LL:CC
|
LL | let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>() + 1, &cpuset) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 128, so pointer to 129 bytes starting at offset 0 is out-of-bounds
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC and there are only 128 bytes starting at that pointer
|
= 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

View File

@ -3,6 +3,6 @@ use std::ptr;
// null is explicitly called out as UB in the C docs.
fn main() {
unsafe {
libc::memchr(ptr::null(), 0, 0); //~ERROR: dangling
libc::memchr(ptr::null(), 0, 0); //~ERROR: null pointer
}
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
--> $DIR/memchr_null.rs:LL:CC
|
LL | libc::memchr(ptr::null(), 0, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
|
= 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

View File

@ -3,6 +3,6 @@ use std::ptr;
// null is explicitly called out as UB in the C docs.
fn main() {
unsafe {
libc::memcmp(ptr::null(), ptr::null(), 0); //~ERROR: dangling
libc::memcmp(ptr::null(), ptr::null(), 0); //~ERROR: null pointer
}
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
--> $DIR/memcmp_null.rs:LL:CC
|
LL | libc::memcmp(ptr::null(), ptr::null(), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: 0x2a[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/memcmp_zero.rs:LL:CC
|
LL | libc::memcmp(ptr.cast(), ptr.cast(), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: 0x2a[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: 0x17[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got 0x17[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/memcpy_zero.rs:LL:CC
|
LL | libc::memcpy(to.cast(), from.cast(), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: 0x17[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got 0x17[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -6,6 +6,6 @@ use std::ptr;
// null is explicitly called out as UB in the C docs.
fn main() {
unsafe {
libc::memrchr(ptr::null(), 0, 0); //~ERROR: dangling
libc::memrchr(ptr::null(), 0, 0); //~ERROR: null pointer
}
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
--> $DIR/memrchr_null.rs:LL:CC
|
LL | libc::memrchr(ptr::null(), 0, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
|
= 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

View File

@ -1,6 +1,6 @@
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows
//@error-in-other-file: pointer to 4 bytes starting at offset 0 is out-of-bounds
//@error-in-other-file: expected a pointer to 4 bytes of memory
fn main() {
unsafe {

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: ALLOC has size 2, so pointer to 4 bytes starting at offset 0 is out-of-bounds
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC and there are only 2 bytes starting at that pointer
--> RUSTLIB/alloc/src/boxed.rs:LL:CC
|
LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: ALLOC has size 2, so pointer to 4 bytes starting at offset 0 is out-of-bounds
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC and there are only 2 bytes starting at that pointer
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: ALLOC has size 2, so pointer to 4 bytes starting at offset 0 is out-of-bounds
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC and there are only 2 bytes starting at that pointer
--> RUSTLIB/alloc/src/boxed.rs:LL:CC
|
LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: ALLOC has size 2, so pointer to 4 bytes starting at offset 0 is out-of-bounds
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC and there are only 2 bytes starting at that pointer
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: 0x4[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x4[noalloc] which is a dangling pointer (it has no provenance)
--> RUSTLIB/alloc/src/boxed.rs:LL:CC
|
LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: 0x4[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x4[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: 0x4[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x4[noalloc] which is a dangling pointer (it has no provenance)
--> RUSTLIB/alloc/src/boxed.rs:LL:CC
|
LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: 0x4[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x4[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x10[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/dangling_pointer_to_raw_pointer.rs:LL:CC
|
LL | unsafe { &(*x).0 as *const i32 }
| ^^^^^^^ out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x10[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x10[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/deref-invalid-ptr.rs:LL:CC
|
LL | let _y = unsafe { &*x as *const u32 };
| ^^^ out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
| ^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got 0x10[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,5 +1,5 @@
#[allow(deref_nullptr)]
fn main() {
let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: null pointer is a dangling pointer
let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: null pointer
panic!("this should never print: {}", x);
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got a null pointer
--> $DIR/null_pointer_deref.rs:LL:CC
|
LL | let x: i32 = unsafe { *std::ptr::null() };
| ^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got a null pointer
|
= 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

View File

@ -1,4 +1,4 @@
#[allow(deref_nullptr)]
fn main() {
unsafe { *std::ptr::null_mut() = 0i32 }; //~ ERROR: null pointer is a dangling pointer
unsafe { *std::ptr::null_mut() = 0i32 }; //~ ERROR: null pointer
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got a null pointer
--> $DIR/null_pointer_write.rs:LL:CC
|
LL | unsafe { *std::ptr::null_mut() = 0i32 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got a null pointer
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC and there are only 4 bytes starting at that pointer
--> $DIR/out_of_bounds_project.rs:LL:CC
|
LL | let _field = addr_of!((*ptr).2);
| ^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
| ^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC and there are only 4 bytes starting at that pointer
|
= 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

View File

@ -1,6 +1,6 @@
fn main() {
let v: Vec<u16> = vec![1, 2];
// This read is also misaligned. We make sure that the OOB message has priority.
let x = unsafe { *v.as_ptr().wrapping_byte_add(5) }; //~ ERROR: out-of-bounds
let x = unsafe { *v.as_ptr().wrapping_byte_add(5) }; //~ ERROR: expected a pointer to 2 bytes of memory
panic!("this should never print: {}", x);
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: ALLOC has size 4, so pointer to 2 bytes starting at offset 5 is out-of-bounds
error: Undefined Behavior: memory access failed: expected a pointer to 2 bytes of memory, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes
--> $DIR/out_of_bounds_read.rs:LL:CC
|
LL | let x = unsafe { *v.as_ptr().wrapping_byte_add(5) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 4, so pointer to 2 bytes starting at offset 5 is out-of-bounds
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 2 bytes of memory, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes
|
= 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

View File

@ -1,5 +1,5 @@
fn main() {
let mut v: Vec<u16> = vec![1, 2];
// This read is also misaligned. We make sure that the OOB message has priority.
unsafe { *v.as_mut_ptr().wrapping_byte_add(5) = 0 }; //~ ERROR: out-of-bounds
unsafe { *v.as_mut_ptr().wrapping_byte_add(5) = 0 }; //~ ERROR: expected a pointer to 2 bytes of memory
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: ALLOC has size 4, so pointer to 2 bytes starting at offset 5 is out-of-bounds
error: Undefined Behavior: memory access failed: expected a pointer to 2 bytes of memory, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes
--> $DIR/out_of_bounds_write.rs:LL:CC
|
LL | unsafe { *v.as_mut_ptr().wrapping_byte_add(5) = 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 4, so pointer to 2 bytes starting at offset 5 is out-of-bounds
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 2 bytes of memory, but got ALLOC+0x5 which is at or beyond the end of the allocation of size 4 bytes
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: $HEX[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/storage_dead_dangling.rs:LL:CC
|
LL | let _ref = unsafe { &mut *(LEAK as *mut i32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: $HEX[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: 0x2c[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/wild_pointer_deref.rs:LL:CC
|
LL | let x = unsafe { *p };
| ^^ memory access failed: 0x2c[noalloc] is a dangling pointer (it has no provenance)
| ^^ memory access failed: expected a pointer to 4 bytes of memory, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,3 +1,14 @@
warning: reborrow of reference to `extern type`
--> $DIR/extern-type-field-offset.rs:LL:CC
|
LL | let x: &Newtype = unsafe { &*(&buf as *const _ as *const Newtype) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reborrow of a reference to `extern type` is not properly supported
|
= help: `extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code
= help: try running with `MIRIFLAGS=-Zmiri-tree-borrows` to use the more permissive but also even more experimental Tree Borrows aliasing checks instead
= note: BACKTRACE:
= note: inside `main` at $DIR/extern-type-field-offset.rs:LL:CC
error: unsupported operation: `extern type` field does not have a known offset
--> $DIR/extern-type-field-offset.rs:LL:CC
|
@ -10,5 +21,5 @@ LL | let _field = &x.a;
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
error: aborting due to 1 previous error; 1 warning emitted

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: 0x2a[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/cast_int_to_fn_ptr.rs:LL:CC
|
LL | g(42)
| ^^^^^ out-of-bounds pointer use: 0x2a[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -2,6 +2,6 @@ fn main() {
let v = [0i8; 4];
let x = &v as *const i8;
// The error is inside another function, so we cannot match it by line
let x = unsafe { x.offset(5) }; //~ERROR: pointer to 5 bytes starting at offset 0 is out-of-bounds
let x = unsafe { x.offset(5) }; //~ERROR: expected a pointer to 5 bytes of memory
panic!("this should never print: {:?}", x);
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer to 5 bytes starting at offset 0 is out-of-bounds
error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 5 bytes of memory, but got ALLOC and there are only 4 bytes starting at that pointer
--> $DIR/out_of_bounds_ptr_1.rs:LL:CC
|
LL | let x = unsafe { x.offset(5) };
| ^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer to 5 bytes starting at offset 0 is out-of-bounds
| ^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 5 bytes of memory, but got ALLOC and there are only 4 bytes starting at that pointer
|
= 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

View File

@ -1,6 +1,6 @@
fn main() {
let v = [0i8; 4];
let x = &v as *const i8;
let x = unsafe { x.offset(-1) }; //~ERROR: pointer to 1 byte starting at offset -1 is out-of-bounds
let x = unsafe { x.offset(-1) }; //~ERROR: expected a pointer to 1 byte of memory
panic!("this should never print: {:?}", x);
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer to 1 byte starting at offset -1 is out-of-bounds
error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got ALLOC-0x1 which points to before the beginning of the allocation
--> $DIR/out_of_bounds_ptr_3.rs:LL:CC
|
LL | let x = unsafe { x.offset(-1) };
| ^^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer to 1 byte starting at offset -1 is out-of-bounds
| ^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got ALLOC-0x1 which points to before the beginning 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

View File

@ -4,11 +4,11 @@ warning: integer-to-pointer cast
LL | (*p.as_mut_ptr().cast::<[*const i32; 2]>())[0] = 4 as *const i32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
|
= help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program.
= help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation.
= help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.
= help: You can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics.
= help: Alternatively, `MIRIFLAGS=-Zmiri-permissive-provenance` disables this warning.
= help: this program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program
= help: see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation
= help: to ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead
= help: you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics
= help: alternatively, `MIRIFLAGS=-Zmiri-permissive-provenance` disables this warning
= note: BACKTRACE:
= note: inside `main` at $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC

View File

@ -16,6 +16,6 @@ fn main() {
let _ = p1.byte_offset_from(p1);
// UB because different pointers.
let _ = p1.byte_offset_from(p2); //~ERROR: different pointers without provenance
let _ = p1.byte_offset_from(p2); //~ERROR: no provenance
}
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation)
error: Undefined Behavior: out-of-bounds `offset_from`: expected a pointer to 1 byte of memory, but got 0xa[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/ptr_offset_from_different_ints.rs:LL:CC
|
LL | let _ = p1.byte_offset_from(p2);
| ^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation)
| ^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: expected a pointer to 1 byte of memory, but got 0xa[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,9 @@
//@normalize-stderr-test: "\d+ < \d+" -> "$$ADDR < $$ADDR"
#![feature(ptr_sub_ptr)]
fn main() {
let arr = [0u8; 8];
let ptr1 = arr.as_ptr();
let ptr2 = ptr1.wrapping_add(4);
let _val = unsafe { ptr1.sub_ptr(ptr2) }; //~ERROR: first pointer has smaller offset than second: 0 < 4
let _val = unsafe { ptr1.sub_ptr(ptr2) }; //~ERROR: first pointer has smaller address than second
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: 0 < 4
error: Undefined Behavior: `ptr_offset_from_unsigned` called when first pointer has smaller address than second: $ADDR < $ADDR
--> $DIR/ptr_offset_from_unsigned_neg.rs:LL:CC
|
LL | let _val = unsafe { ptr1.sub_ptr(ptr2) };
| ^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: 0 < 4
| ^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller address than second: $ADDR < $ADDR
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer arithmetic: 0x1[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/ptr_offset_int_plus_int.rs:LL:CC
|
LL | let _val = (1 as *mut u8).offset(1);
| ^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: 0x1[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,4 +1,5 @@
//@compile-flags: -Zmiri-permissive-provenance
//@normalize-stderr-test: "to \d+ bytes of memory" -> "to $$BYTES bytes of memory"
fn main() {
let ptr = Box::into_raw(Box::new(0u32));

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer arithmetic: 0x1[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/ptr_offset_int_plus_ptr.rs:LL:CC
|
LL | let _val = (1 as *mut u8).offset(ptr as isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: 0x1[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -5,6 +5,7 @@ fn main() {
unsafe {
let vec: &[i8] = &[10, 11, 12, 13, 14, 15, 16, 17, 18];
let idxs = Simd::from_array([9, 3, 0, 17]);
let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0)); //~ERROR: pointer to 1 byte starting at offset 9 is out-of-bounds
let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0));
//~^ERROR: expected a pointer to 1 byte of memory
}
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
error: Undefined Behavior: memory access failed: expected a pointer to 1 byte of memory, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes
--> $DIR/simd-gather.rs:LL:CC
|
LL | let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes
|
= 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

View File

@ -6,7 +6,7 @@ fn main() {
let mut vec: Vec<i8> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
let idxs = Simd::from_array([9, 3, 0, 17]);
Simd::from_array([-27, 82, -41, 124]).scatter_select_unchecked(
//~^ERROR: pointer to 1 byte starting at offset 9 is out-of-bounds
//~^ERROR: expected a pointer to 1 byte of memory
&mut vec,
Mask::splat(true),
idxs,

View File

@ -1,4 +1,4 @@
error: Undefined Behavior: memory access failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
error: Undefined Behavior: memory access failed: expected a pointer to 1 byte of memory, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes
--> $DIR/simd-scatter.rs:LL:CC
|
LL | / Simd::from_array([-27, 82, -41, 124]).scatter_select_unchecked(
@ -7,7 +7,7 @@ LL | | &mut vec,
LL | | Mask::splat(true),
LL | | idxs,
LL | | );
| |_________^ memory access failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
| |_________^ memory access failed: expected a pointer to 1 byte of memory, but got ALLOC+0x9 which is at or beyond the end of the allocation of size 9 bytes
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/pointer_partial_overwrite.rs:LL:CC
|
LL | let x = *p;
| ^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
| ^^ memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: memory access failed: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/provenance_transmute.rs:LL:CC
|
LL | let _val = *left_ptr;
| ^^^^^^^^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/ptr_int_unexposed.rs:LL:CC
|
LL | assert_eq!(unsafe { *ptr }, 3);
| ^^^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
| ^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/ptr_invalid.rs:LL:CC
|
LL | let _val = unsafe { *xptr_invalid };
| ^^^^^^^^^^^^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer arithmetic: $HEX[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/ptr_invalid_offset.rs:LL:CC
|
LL | let _ = unsafe { roundtrip.offset(1) };
| ^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: $HEX[noalloc] is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
error: Undefined Behavior: memory access failed: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/reading_half_a_pointer.rs:LL:CC
|
LL | let _val = *x;
| ^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
| ^^ memory access failed: expected a pointer to 1 byte of memory, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
|
= 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

View File

@ -4,6 +4,6 @@ extern "Rust" {
fn main() {
unsafe {
miri_resolve_frame(std::ptr::null_mut(), 0); //~ ERROR: null pointer is a dangling pointer
miri_resolve_frame(std::ptr::null_mut(), 0); //~ ERROR: got a null pointer
}
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
--> $DIR/bad-backtrace-ptr.rs:LL:CC
|
LL | miri_resolve_frame(std::ptr::null_mut(), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
|
= 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

Some files were not shown because too many files have changed in this diff Show More