Skip to content

Commit 4a0402c

Browse files
committed
Auto merge of rust-lang#114932 - RalfJung:miri, r=RalfJung
update Miri r? `@ghost`
2 parents bd138e2 + 6249c70 commit 4a0402c

38 files changed

+472
-177
lines changed

src/tools/miri/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ behavior** in your program, and cannot run all programs:
5353
positives here, so if your program runs fine in Miri right now that is by no
5454
means a guarantee that it is UB-free when these questions get answered.
5555

56-
In particular, Miri does currently not check that references point to valid data.
56+
In particular, Miri does not check that references point to valid data.
5757
* If the program relies on unspecified details of how data is laid out, it will
5858
still run fine in Miri -- but might break (including causing UB) on different
5959
compiler versions or different platforms.

src/tools/miri/rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9fa6bdd764a1f7bdf69eccceeace6d13f38cb2e1
1+
656ee47db32e882fb02913f6204e09cc7a41a50e

src/tools/miri/src/diagnostics.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -304,11 +304,21 @@ pub fn report_error<'tcx, 'mir>(
304304
(None, format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior")),
305305
(None, format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives")),
306306
],
307-
UndefinedBehavior(_) =>
308-
vec![
307+
UndefinedBehavior(info) => {
308+
let mut helps = vec![
309309
(None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")),
310310
(None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")),
311-
],
311+
];
312+
if let UndefinedBehaviorInfo::PointerUseAfterFree(alloc_id, _) | UndefinedBehaviorInfo::PointerOutOfBounds { alloc_id, .. } = info {
313+
if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
314+
helps.push((Some(span), format!("{:?} was allocated here:", alloc_id)));
315+
}
316+
if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
317+
helps.push((Some(span), format!("{:?} was deallocated here:", alloc_id)));
318+
}
319+
}
320+
helps
321+
}
312322
InvalidProgram(
313323
InvalidProgramInfo::AlreadyReported(_)
314324
) => {

src/tools/miri/src/helpers.rs

+62-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ use rustc_index::IndexVec;
1313
use rustc_middle::mir;
1414
use rustc_middle::ty::{
1515
self,
16-
layout::{LayoutOf, TyAndLayout},
17-
List, TyCtxt,
16+
layout::{IntegerExt as _, LayoutOf, TyAndLayout},
17+
List, Ty, TyCtxt,
1818
};
1919
use rustc_span::{def_id::CrateNum, sym, Span, Symbol};
20-
use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants};
20+
use rustc_target::abi::{Align, FieldIdx, FieldsShape, Integer, Size, Variants};
2121
use rustc_target::spec::abi::Abi;
2222

2323
use rand::RngCore;
@@ -1011,6 +1011,65 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10111011
None => tcx.item_name(def_id),
10121012
}
10131013
}
1014+
1015+
/// Converts `f` to integer type `dest_ty` after rounding with mode `round`.
1016+
/// Returns `None` if `f` is NaN or out of range.
1017+
fn float_to_int_checked<F>(
1018+
&self,
1019+
f: F,
1020+
dest_ty: Ty<'tcx>,
1021+
round: rustc_apfloat::Round,
1022+
) -> Option<Scalar<Provenance>>
1023+
where
1024+
F: rustc_apfloat::Float + Into<Scalar<Provenance>>,
1025+
{
1026+
let this = self.eval_context_ref();
1027+
1028+
match dest_ty.kind() {
1029+
// Unsigned
1030+
ty::Uint(t) => {
1031+
let size = Integer::from_uint_ty(this, *t).size();
1032+
let res = f.to_u128_r(size.bits_usize(), round, &mut false);
1033+
if res.status.intersects(
1034+
rustc_apfloat::Status::INVALID_OP
1035+
| rustc_apfloat::Status::OVERFLOW
1036+
| rustc_apfloat::Status::UNDERFLOW,
1037+
) {
1038+
// Floating point value is NaN (flagged with INVALID_OP) or outside the range
1039+
// of values of the integer type (flagged with OVERFLOW or UNDERFLOW).
1040+
None
1041+
} else {
1042+
// Floating point value can be represented by the integer type after rounding.
1043+
// The INEXACT flag is ignored on purpose to allow rounding.
1044+
Some(Scalar::from_uint(res.value, size))
1045+
}
1046+
}
1047+
// Signed
1048+
ty::Int(t) => {
1049+
let size = Integer::from_int_ty(this, *t).size();
1050+
let res = f.to_i128_r(size.bits_usize(), round, &mut false);
1051+
if res.status.intersects(
1052+
rustc_apfloat::Status::INVALID_OP
1053+
| rustc_apfloat::Status::OVERFLOW
1054+
| rustc_apfloat::Status::UNDERFLOW,
1055+
) {
1056+
// Floating point value is NaN (flagged with INVALID_OP) or outside the range
1057+
// of values of the integer type (flagged with OVERFLOW or UNDERFLOW).
1058+
None
1059+
} else {
1060+
// Floating point value can be represented by the integer type after rounding.
1061+
// The INEXACT flag is ignored on purpose to allow rounding.
1062+
Some(Scalar::from_int(res.value, size))
1063+
}
1064+
}
1065+
// Nothing else
1066+
_ =>
1067+
span_bug!(
1068+
this.cur_span(),
1069+
"attempted float-to-int conversion with non-int output type {dest_ty:?}"
1070+
),
1071+
}
1072+
}
10141073
}
10151074

10161075
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {

src/tools/miri/src/machine.rs

+47-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_middle::{
2525
},
2626
};
2727
use rustc_span::def_id::{CrateNum, DefId};
28-
use rustc_span::Symbol;
28+
use rustc_span::{Span, SpanData, Symbol};
2929
use rustc_target::abi::{Align, Size};
3030
use rustc_target::spec::abi::Abi;
3131

@@ -135,6 +135,19 @@ impl MayLeak for MiriMemoryKind {
135135
}
136136
}
137137

138+
impl MiriMemoryKind {
139+
/// Whether we have a useful allocation span for an allocation of this kind.
140+
fn should_save_allocation_span(self) -> bool {
141+
use self::MiriMemoryKind::*;
142+
match self {
143+
// Heap allocations are fine since the `Allocation` is created immediately.
144+
Rust | Miri | C | WinHeap | Mmap => true,
145+
// Everything else is unclear, let's not show potentially confusing spans.
146+
Machine | Global | ExternStatic | Tls | Runtime => false,
147+
}
148+
}
149+
}
150+
138151
impl fmt::Display for MiriMemoryKind {
139152
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140153
use self::MiriMemoryKind::*;
@@ -497,6 +510,10 @@ pub struct MiriMachine<'mir, 'tcx> {
497510

498511
/// Whether to collect a backtrace when each allocation is created, just in case it leaks.
499512
pub(crate) collect_leak_backtraces: bool,
513+
514+
/// The spans we will use to report where an allocation was created and deallocated in
515+
/// diagnostics.
516+
pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>,
500517
}
501518

502519
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
@@ -621,6 +638,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
621638
stack_addr,
622639
stack_size,
623640
collect_leak_backtraces: config.collect_leak_backtraces,
641+
allocation_spans: RefCell::new(FxHashMap::default()),
624642
}
625643
}
626644

@@ -742,6 +760,21 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
742760
pub(crate) fn page_align(&self) -> Align {
743761
Align::from_bytes(self.page_size).unwrap()
744762
}
763+
764+
pub(crate) fn allocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
765+
self.allocation_spans
766+
.borrow()
767+
.get(&alloc_id)
768+
.map(|(allocated, _deallocated)| allocated.data())
769+
}
770+
771+
pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
772+
self.allocation_spans
773+
.borrow()
774+
.get(&alloc_id)
775+
.and_then(|(_allocated, deallocated)| *deallocated)
776+
.map(Span::data)
777+
}
745778
}
746779

747780
impl VisitTags for MiriMachine<'_, '_> {
@@ -791,6 +824,7 @@ impl VisitTags for MiriMachine<'_, '_> {
791824
stack_addr: _,
792825
stack_size: _,
793826
collect_leak_backtraces: _,
827+
allocation_spans: _,
794828
} = self;
795829

796830
threads.visit_tags(visit);
@@ -1051,6 +1085,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
10511085
},
10521086
|ptr| ecx.global_base_pointer(ptr),
10531087
)?;
1088+
1089+
if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
1090+
ecx.machine
1091+
.allocation_spans
1092+
.borrow_mut()
1093+
.insert(id, (ecx.machine.current_span(), None));
1094+
}
1095+
10541096
Ok(Cow::Owned(alloc))
10551097
}
10561098

@@ -1181,6 +1223,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
11811223
if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
11821224
borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, range, machine)?;
11831225
}
1226+
if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
1227+
{
1228+
*deallocated_at = Some(machine.current_span());
1229+
}
11841230
Ok(())
11851231
}
11861232

src/tools/miri/src/range_map.rs

+17-25
Original file line numberDiff line numberDiff line change
@@ -27,35 +27,27 @@ impl<T> RangeMap<T> {
2727
#[inline(always)]
2828
pub fn new(size: Size, init: T) -> RangeMap<T> {
2929
let size = size.bytes();
30-
let mut map = RangeMap { v: Vec::new() };
31-
if size > 0 {
32-
map.v.push(Elem { range: 0..size, data: init });
33-
}
34-
map
30+
let v = if size > 0 { vec![Elem { range: 0..size, data: init }] } else { Vec::new() };
31+
RangeMap { v }
3532
}
3633

3734
/// Finds the index containing the given offset.
3835
fn find_offset(&self, offset: u64) -> usize {
39-
// We do a binary search.
40-
let mut left = 0usize; // inclusive
41-
let mut right = self.v.len(); // exclusive
42-
loop {
43-
debug_assert!(left < right, "find_offset: offset {offset} is out-of-bounds");
44-
let candidate = left.checked_add(right).unwrap() / 2;
45-
let elem = &self.v[candidate];
46-
if offset < elem.range.start {
47-
// We are too far right (offset is further left).
48-
debug_assert!(candidate < right); // we are making progress
49-
right = candidate;
50-
} else if offset >= elem.range.end {
51-
// We are too far left (offset is further right).
52-
debug_assert!(candidate >= left); // we are making progress
53-
left = candidate + 1;
54-
} else {
55-
// This is it!
56-
return candidate;
57-
}
58-
}
36+
self.v
37+
.binary_search_by(|elem| -> std::cmp::Ordering {
38+
if offset < elem.range.start {
39+
// We are too far right (offset is further left).
40+
// (`Greater` means that `elem` is greater than the desired target.)
41+
std::cmp::Ordering::Greater
42+
} else if offset >= elem.range.end {
43+
// We are too far left (offset is further right).
44+
std::cmp::Ordering::Less
45+
} else {
46+
// This is it!
47+
std::cmp::Ordering::Equal
48+
}
49+
})
50+
.unwrap()
5951
}
6052

6153
/// Provides read-only iteration over everything in the given range. This does

src/tools/miri/src/shims/foreign_items.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
690690
let right = this.read_pointer(right)?;
691691
let n = Size::from_bytes(this.read_target_usize(n)?);
692692

693+
// C requires that this must always be a valid pointer (C18 §7.1.4).
694+
this.ptr_get_alloc_id(left)?;
695+
this.ptr_get_alloc_id(right)?;
696+
693697
let result = {
694698
let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
695699
let right_bytes = this.read_bytes_ptr_strip_provenance(right, n)?;
@@ -714,6 +718,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
714718
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
715719
let val = val as u8;
716720

721+
// C requires that this must always be a valid pointer (C18 §7.1.4).
722+
this.ptr_get_alloc_id(ptr)?;
723+
717724
if let Some(idx) = this
718725
.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
719726
.iter()
@@ -738,6 +745,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
738745
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
739746
let val = val as u8;
740747

748+
// C requires that this must always be a valid pointer (C18 §7.1.4).
749+
this.ptr_get_alloc_id(ptr)?;
750+
741751
let idx = this
742752
.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
743753
.iter()
@@ -752,6 +762,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
752762
"strlen" => {
753763
let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
754764
let ptr = this.read_pointer(ptr)?;
765+
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
755766
let n = this.read_c_str(ptr)?.len();
756767
this.write_scalar(
757768
Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
@@ -791,6 +802,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
791802
// pointer provenance is preserved by this implementation of `strcpy`.
792803
// That is probably overly cautious, but there also is no fundamental
793804
// reason to have `strcpy` destroy pointer provenance.
805+
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
794806
let n = this.read_c_str(ptr_src)?.len().checked_add(1).unwrap();
795807
this.mem_copy(
796808
ptr_src,
@@ -942,6 +954,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
942954
this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?;
943955
}
944956

957+
// LLVM intrinsics
945958
"llvm.prefetch" => {
946959
let [p, rw, loc, ty] =
947960
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@@ -968,8 +981,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
968981
throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty);
969982
}
970983
}
971-
972-
// Architecture-specific shims
973984
"llvm.x86.addcarry.64" if this.tcx.sess.target.arch == "x86_64" => {
974985
// Computes u8+u64+u64, returning tuple (u8,u64) comprising the output carry and truncated sum.
975986
let [c_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;

0 commit comments

Comments
 (0)