mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-30 05:51:58 +00:00
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:
commit
56c698c711
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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`.
|
||||
|
@ -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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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() {
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
@ -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)]
|
||||
|
@ -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(),
|
||||
},
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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"]
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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)]
|
||||
|
@ -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
|
||||
|
@ -24,6 +24,7 @@
|
||||
#![feature(panic_can_unwind)]
|
||||
#![feature(test)]
|
||||
#![allow(internal_features)]
|
||||
#![warn(rustdoc::unescaped_backticks)]
|
||||
|
||||
pub use cli::TestOpts;
|
||||
|
||||
|
@ -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 });
|
||||
}
|
||||
|
||||
|
@ -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 });
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -106,7 +106,6 @@ impl Step for JsonDocs {
|
||||
builder.ensure(crate::core::build_steps::doc::Std::new(
|
||||
builder.top_stage,
|
||||
host,
|
||||
builder,
|
||||
DocumentationFormat::Json,
|
||||
));
|
||||
|
||||
|
@ -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" };
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
)
|
||||
}};
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
99b7134389e9766462601a2fc4013840b9d31745
|
||||
a526d7ce45fd2284e0e7c7556ccba2425b9d25e5
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 })
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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![],
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)?;
|
||||
}
|
||||
|
@ -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) =>
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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" => {
|
||||
|
@ -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)?;
|
||||
|
@ -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)]
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user