Skip to content

Commit 41ca286

Browse files
committed
Auto merge of #2845 - oli-obk:rustup, r=oli-obk
Rustup
2 parents bf51be1 + 393159a commit 41ca286

15 files changed

+174
-45
lines changed

README.md

+9-5
Original file line numberDiff line numberDiff line change
@@ -301,18 +301,22 @@ environment variable. We first document the most relevant and most commonly used
301301
* `-Zmiri-disable-isolation` disables host isolation. As a consequence,
302302
the program has access to host resources such as environment variables, file
303303
systems, and randomness.
304+
* `-Zmiri-disable-leak-backtraces` disables backtraces reports for memory leaks. By default, a
305+
backtrace is captured for every allocation when it is created, just in case it leaks. This incurs
306+
some memory overhead to store data that is almost never used. This flag is implied by
307+
`-Zmiri-ignore-leaks`.
308+
* `-Zmiri-env-forward=<var>` forwards the `var` environment variable to the interpreted program. Can
309+
be used multiple times to forward several variables. Execution will still be deterministic if the
310+
value of forwarded variables stays the same. Has no effect if `-Zmiri-disable-isolation` is set.
311+
* `-Zmiri-ignore-leaks` disables the memory leak checker, and also allows some
312+
remaining threads to exist when the main thread exits.
304313
* `-Zmiri-isolation-error=<action>` configures Miri's response to operations
305314
requiring host access while isolation is enabled. `abort`, `hide`, `warn`,
306315
and `warn-nobacktrace` are the supported actions. The default is to `abort`,
307316
which halts the machine. Some (but not all) operations also support continuing
308317
execution with a "permission denied" error being returned to the program.
309318
`warn` prints a full backtrace when that happens; `warn-nobacktrace` is less
310319
verbose. `hide` hides the warning entirely.
311-
* `-Zmiri-env-forward=<var>` forwards the `var` environment variable to the interpreted program. Can
312-
be used multiple times to forward several variables. Execution will still be deterministic if the
313-
value of forwarded variables stays the same. Has no effect if `-Zmiri-disable-isolation` is set.
314-
* `-Zmiri-ignore-leaks` disables the memory leak checker, and also allows some
315-
remaining threads to exist when the main thread exits.
316320
* `-Zmiri-num-cpus` states the number of available CPUs to be reported by miri. By default, the
317321
number of available CPUs is `1`. Note that this flag does not affect how miri handles threads in
318322
any way.

rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
71ef9ecbdedb67c32f074884f503f8e582855c2f
1+
53ac4f8e2fc15e49ef3a04f98622a9b9db755fd4

src/bin/miri.rs

+3
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ fn main() {
359359
isolation_enabled = Some(false);
360360
}
361361
miri_config.isolated_op = miri::IsolatedOp::Allow;
362+
} else if arg == "-Zmiri-disable-leak-backtraces" {
363+
miri_config.collect_leak_backtraces = false;
362364
} else if arg == "-Zmiri-disable-weak-memory-emulation" {
363365
miri_config.weak_memory_emulation = false;
364366
} else if arg == "-Zmiri-track-weak-memory-loads" {
@@ -385,6 +387,7 @@ fn main() {
385387
};
386388
} else if arg == "-Zmiri-ignore-leaks" {
387389
miri_config.ignore_leaks = true;
390+
miri_config.collect_leak_backtraces = false;
388391
} else if arg == "-Zmiri-panic-on-unsupported" {
389392
miri_config.panic_on_unsupported = true;
390393
} else if arg == "-Zmiri-tag-raw-pointers" {

src/borrow_tracker/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ pub enum AllocState {
352352
TreeBorrows(Box<RefCell<tree_borrows::AllocState>>),
353353
}
354354

355-
impl machine::AllocExtra {
355+
impl machine::AllocExtra<'_> {
356356
#[track_caller]
357357
pub fn borrow_tracker_sb(&self) -> &RefCell<stacked_borrows::AllocState> {
358358
match self.borrow_tracker {

src/diagnostics.rs

+36-3
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ pub enum NonHaltingDiagnostic {
105105
}
106106

107107
/// Level of Miri specific diagnostics
108-
enum DiagLevel {
108+
pub enum DiagLevel {
109109
Error,
110110
Warning,
111111
Note,
@@ -114,7 +114,7 @@ enum DiagLevel {
114114
/// Attempts to prune a stacktrace to omit the Rust runtime, and returns a bool indicating if any
115115
/// frames were pruned. If the stacktrace does not have any local frames, we conclude that it must
116116
/// be pointing to a problem in the Rust runtime itself, and do not prune it at all.
117-
fn prune_stacktrace<'tcx>(
117+
pub fn prune_stacktrace<'tcx>(
118118
mut stacktrace: Vec<FrameInfo<'tcx>>,
119119
machine: &MiriMachine<'_, 'tcx>,
120120
) -> (Vec<FrameInfo<'tcx>>, bool) {
@@ -338,12 +338,45 @@ pub fn report_error<'tcx, 'mir>(
338338
None
339339
}
340340

341+
pub fn report_leaks<'mir, 'tcx>(
342+
ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
343+
leaks: Vec<(AllocId, MemoryKind<MiriMemoryKind>, Allocation<Provenance, AllocExtra<'tcx>>)>,
344+
) {
345+
let mut any_pruned = false;
346+
for (id, kind, mut alloc) in leaks {
347+
let Some(backtrace) = alloc.extra.backtrace.take() else {
348+
continue;
349+
};
350+
let (backtrace, pruned) = prune_stacktrace(backtrace, &ecx.machine);
351+
any_pruned |= pruned;
352+
report_msg(
353+
DiagLevel::Error,
354+
&format!(
355+
"memory leaked: {id:?} ({}, size: {:?}, align: {:?}), allocated here:",
356+
kind,
357+
alloc.size().bytes(),
358+
alloc.align.bytes()
359+
),
360+
vec![],
361+
vec![],
362+
vec![],
363+
&backtrace,
364+
&ecx.machine,
365+
);
366+
}
367+
if any_pruned {
368+
ecx.tcx.sess.diagnostic().note_without_error(
369+
"some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
370+
);
371+
}
372+
}
373+
341374
/// Report an error or note (depending on the `error` argument) with the given stacktrace.
342375
/// Also emits a full stacktrace of the interpreter stack.
343376
/// We want to present a multi-line span message for some errors. Diagnostics do not support this
344377
/// directly, so we pass the lines as a `Vec<String>` and display each line after the first with an
345378
/// additional `span_label` or `note` call.
346-
fn report_msg<'tcx>(
379+
pub fn report_msg<'tcx>(
347380
diag_level: DiagLevel,
348381
title: &str,
349382
span_msg: Vec<String>,

src/eval.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::thread;
1010
use log::info;
1111

1212
use crate::borrow_tracker::RetagFields;
13+
use crate::diagnostics::report_leaks;
1314
use rustc_data_structures::fx::FxHashSet;
1415
use rustc_hir::def::Namespace;
1516
use rustc_hir::def_id::DefId;
@@ -145,6 +146,8 @@ pub struct MiriConfig {
145146
pub num_cpus: u32,
146147
/// Requires Miri to emulate pages of a certain size
147148
pub page_size: Option<u64>,
149+
/// Whether to collect a backtrace when each allocation is created, just in case it leaks.
150+
pub collect_leak_backtraces: bool,
148151
}
149152

150153
impl Default for MiriConfig {
@@ -179,6 +182,7 @@ impl Default for MiriConfig {
179182
gc_interval: 10_000,
180183
num_cpus: 1,
181184
page_size: None,
185+
collect_leak_backtraces: true,
182186
}
183187
}
184188
}
@@ -457,10 +461,17 @@ pub fn eval_entry<'tcx>(
457461
}
458462
// Check for memory leaks.
459463
info!("Additional static roots: {:?}", ecx.machine.static_roots);
460-
let leaks = ecx.leak_report(&ecx.machine.static_roots);
461-
if leaks != 0 {
462-
tcx.sess.err("the evaluated program leaked memory");
463-
tcx.sess.note_without_error("pass `-Zmiri-ignore-leaks` to disable this check");
464+
let leaks = ecx.find_leaked_allocations(&ecx.machine.static_roots);
465+
if !leaks.is_empty() {
466+
report_leaks(&ecx, leaks);
467+
let leak_message = "the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check";
468+
if ecx.machine.collect_leak_backtraces {
469+
// If we are collecting leak backtraces, each leak is a distinct error diagnostic.
470+
tcx.sess.note_without_error(leak_message);
471+
} else {
472+
// If we do not have backtraces, we just report an error without any span.
473+
tcx.sess.err(leak_message);
474+
};
464475
// Ignore the provided return code - let the reported error
465476
// determine the return code.
466477
return None;

src/machine.rs

+39-12
Original file line numberDiff line numberDiff line change
@@ -259,20 +259,25 @@ impl ProvenanceExtra {
259259

260260
/// Extra per-allocation data
261261
#[derive(Debug, Clone)]
262-
pub struct AllocExtra {
262+
pub struct AllocExtra<'tcx> {
263263
/// Global state of the borrow tracker, if enabled.
264264
pub borrow_tracker: Option<borrow_tracker::AllocState>,
265-
/// Data race detection via the use of a vector-clock,
266-
/// this is only added if it is enabled.
265+
/// Data race detection via the use of a vector-clock.
266+
/// This is only added if it is enabled.
267267
pub data_race: Option<data_race::AllocState>,
268-
/// Weak memory emulation via the use of store buffers,
269-
/// this is only added if it is enabled.
268+
/// Weak memory emulation via the use of store buffers.
269+
/// This is only added if it is enabled.
270270
pub weak_memory: Option<weak_memory::AllocState>,
271+
/// A backtrace to where this allocation was allocated.
272+
/// As this is recorded for leak reports, it only exists
273+
/// if this allocation is leakable. The backtrace is not
274+
/// pruned yet; that should be done before printing it.
275+
pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
271276
}
272277

273-
impl VisitTags for AllocExtra {
278+
impl VisitTags for AllocExtra<'_> {
274279
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
275-
let AllocExtra { borrow_tracker, data_race, weak_memory } = self;
280+
let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self;
276281

277282
borrow_tracker.visit_tags(visit);
278283
data_race.visit_tags(visit);
@@ -473,12 +478,17 @@ pub struct MiriMachine<'mir, 'tcx> {
473478
pub(crate) gc_interval: u32,
474479
/// The number of blocks that passed since the last BorTag GC pass.
475480
pub(crate) since_gc: u32,
481+
476482
/// The number of CPUs to be reported by miri.
477483
pub(crate) num_cpus: u32,
484+
478485
/// Determines Miri's page size and associated values
479486
pub(crate) page_size: u64,
480487
pub(crate) stack_addr: u64,
481488
pub(crate) stack_size: u64,
489+
490+
/// Whether to collect a backtrace when each allocation is created, just in case it leaks.
491+
pub(crate) collect_leak_backtraces: bool,
482492
}
483493

484494
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
@@ -587,6 +597,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
587597
page_size,
588598
stack_addr,
589599
stack_size,
600+
collect_leak_backtraces: config.collect_leak_backtraces,
590601
}
591602
}
592603

@@ -734,6 +745,7 @@ impl VisitTags for MiriMachine<'_, '_> {
734745
page_size: _,
735746
stack_addr: _,
736747
stack_size: _,
748+
collect_leak_backtraces: _,
737749
} = self;
738750

739751
threads.visit_tags(visit);
@@ -779,7 +791,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
779791
type ExtraFnVal = Dlsym;
780792

781793
type FrameExtra = FrameExtra<'tcx>;
782-
type AllocExtra = AllocExtra;
794+
type AllocExtra = AllocExtra<'tcx>;
783795

784796
type Provenance = Provenance;
785797
type ProvenanceExtra = ProvenanceExtra;
@@ -973,9 +985,24 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
973985
)
974986
});
975987
let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation);
988+
989+
// If an allocation is leaked, we want to report a backtrace to indicate where it was
990+
// allocated. We don't need to record a backtrace for allocations which are allowed to
991+
// leak.
992+
let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces {
993+
None
994+
} else {
995+
Some(ecx.generate_stacktrace())
996+
};
997+
976998
let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx(
977999
&ecx.tcx,
978-
AllocExtra { borrow_tracker, data_race: race_alloc, weak_memory: buffer_alloc },
1000+
AllocExtra {
1001+
borrow_tracker,
1002+
data_race: race_alloc,
1003+
weak_memory: buffer_alloc,
1004+
backtrace,
1005+
},
9791006
|ptr| ecx.global_base_pointer(ptr),
9801007
)?;
9811008
Ok(Cow::Owned(alloc))
@@ -1055,7 +1082,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
10551082
fn before_memory_read(
10561083
_tcx: TyCtxt<'tcx>,
10571084
machine: &Self,
1058-
alloc_extra: &AllocExtra,
1085+
alloc_extra: &AllocExtra<'tcx>,
10591086
(alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
10601087
range: AllocRange,
10611088
) -> InterpResult<'tcx> {
@@ -1075,7 +1102,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
10751102
fn before_memory_write(
10761103
_tcx: TyCtxt<'tcx>,
10771104
machine: &mut Self,
1078-
alloc_extra: &mut AllocExtra,
1105+
alloc_extra: &mut AllocExtra<'tcx>,
10791106
(alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
10801107
range: AllocRange,
10811108
) -> InterpResult<'tcx> {
@@ -1095,7 +1122,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
10951122
fn before_memory_deallocation(
10961123
_tcx: TyCtxt<'tcx>,
10971124
machine: &mut Self,
1098-
alloc_extra: &mut AllocExtra,
1125+
alloc_extra: &mut AllocExtra<'tcx>,
10991126
(alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
11001127
range: AllocRange,
11011128
) -> InterpResult<'tcx> {

src/tag_gc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ impl VisitTags for Operand<Provenance> {
125125
}
126126
}
127127

128-
impl VisitTags for Allocation<Provenance, AllocExtra> {
128+
impl VisitTags for Allocation<Provenance, AllocExtra<'_>> {
129129
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
130130
for prov in self.provenance().provenances() {
131131
prov.visit_tags(visit);

tests/fail/memleak.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@error-pattern: the evaluated program leaked memory
1+
//@error-pattern: memory leaked
22
//@normalize-stderr-test: ".*│.*" -> "$$stripped$$"
33

44
fn main() {

tests/fail/memleak.stderr

+18-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1-
The following memory was leaked: ALLOC (Rust heap, size: 4, align: 4) {
2-
$stripped$
3-
}
1+
error: memory leaked: ALLOC (Rust heap, size: 4, align: 4), allocated here:
2+
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
3+
|
4+
LL | unsafe { __rust_alloc(layout.size(), layout.align()) }
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
8+
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC
9+
= note: inside `<std::alloc::Global as std::alloc::Allocator>::allocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC
10+
= note: inside `alloc::alloc::exchange_malloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
11+
= note: inside `std::boxed::Box::<i32>::new` at RUSTLIB/alloc/src/boxed.rs:LL:CC
12+
note: inside `main`
13+
--> $DIR/memleak.rs:LL:CC
14+
|
15+
LL | std::mem::forget(Box::new(42));
16+
| ^^^^^^^^^^^^
417

5-
error: the evaluated program leaked memory
18+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
619

7-
note: pass `-Zmiri-ignore-leaks` to disable this check
20+
note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
821

922
error: aborting due to previous error
1023

tests/fail/memleak_no_backtrace.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//@compile-flags: -Zmiri-disable-leak-backtraces
2+
//@error-pattern: the evaluated program leaked memory
3+
//@normalize-stderr-test: ".*│.*" -> "$$stripped$$"
4+
5+
fn main() {
6+
std::mem::forget(Box::new(42));
7+
}
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
error: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
2+
3+
error: aborting due to previous error
4+

tests/fail/memleak_rc.32bit.stderr

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1-
The following memory was leaked: ALLOC (Rust heap, size: 16, align: 4) {
2-
$stripped$
3-
}
1+
error: memory leaked: ALLOC (Rust heap, size: 16, align: 4), allocated here:
2+
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
3+
|
4+
LL | unsafe { __rust_alloc(layout.size(), layout.align()) }
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
8+
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC
9+
= note: inside `<std::alloc::Global as std::alloc::Allocator>::allocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC
10+
= note: inside `alloc::alloc::exchange_malloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
11+
= note: inside `std::boxed::Box::<std::rc::RcBox<std::cell::RefCell<std::option::Option<Dummy>>>>::new` at RUSTLIB/alloc/src/boxed.rs:LL:CC
12+
= note: inside `std::rc::Rc::<std::cell::RefCell<std::option::Option<Dummy>>>::new` at RUSTLIB/alloc/src/rc.rs:LL:CC
13+
note: inside `main`
14+
--> $DIR/memleak_rc.rs:LL:CC
15+
|
16+
LL | let x = Dummy(Rc::new(RefCell::new(None)));
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
418

5-
error: the evaluated program leaked memory
19+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
620

7-
note: pass `-Zmiri-ignore-leaks` to disable this check
21+
note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
822

923
error: aborting due to previous error
1024

0 commit comments

Comments
 (0)