Skip to content

Commit 63b314c

Browse files
authored
Rollup merge of #72810 - RalfJung:mir-terminate-sanity, r=jonas-schievink
validate basic sanity for TerminatorKind r? @jonas-schievink This mainly checks that all `BasicBlock` actually exist. On top of that, it checks that `Call` actually calls something of `FnPtr`/`FnDef` type, and `Assert` has to work on a `bool`. Also `SwitchInt` cannot have an empty target list.
2 parents 9c1857f + f793c0b commit 63b314c

File tree

2 files changed

+127
-16
lines changed

2 files changed

+127
-16
lines changed

src/librustc_mir/interpret/terminator.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
5050
self.go_to_block(target_block);
5151
}
5252

53-
Call { ref func, ref args, destination, ref cleanup, .. } => {
53+
Call {
54+
ref func,
55+
ref args,
56+
destination,
57+
ref cleanup,
58+
from_hir_call: _from_hir_call,
59+
} => {
5460
let old_stack = self.frame_idx();
5561
let old_loc = self.frame().loc;
5662
let func = self.eval_operand(func, None)?;

src/librustc_mir/transform/validate.rs

+120-15
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
use super::{MirPass, MirSource};
44
use rustc_middle::mir::visit::Visitor;
55
use rustc_middle::{
6-
mir::{Body, Location, Operand, Rvalue, Statement, StatementKind},
7-
ty::{ParamEnv, TyCtxt},
6+
mir::{
7+
BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
8+
TerminatorKind,
9+
},
10+
ty::{self, ParamEnv, TyCtxt},
811
};
9-
use rustc_span::{def_id::DefId, Span, DUMMY_SP};
12+
use rustc_span::def_id::DefId;
1013

1114
pub struct Validator {
1215
/// Describes at which point in the pipeline this validation is happening.
@@ -30,27 +33,38 @@ struct TypeChecker<'a, 'tcx> {
3033
}
3134

3235
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
33-
fn fail(&self, span: Span, msg: impl AsRef<str>) {
36+
fn fail(&self, location: Location, msg: impl AsRef<str>) {
37+
let span = self.body.source_info(location).span;
3438
// We use `delay_span_bug` as we might see broken MIR when other errors have already
3539
// occurred.
3640
self.tcx.sess.diagnostic().delay_span_bug(
3741
span,
38-
&format!("broken MIR in {:?} ({}): {}", self.def_id, self.when, msg.as_ref()),
42+
&format!(
43+
"broken MIR in {:?} ({}) at {:?}:\n{}",
44+
self.def_id,
45+
self.when,
46+
location,
47+
msg.as_ref()
48+
),
3949
);
4050
}
51+
52+
fn check_bb(&self, location: Location, bb: BasicBlock) {
53+
if self.body.basic_blocks().get(bb).is_none() {
54+
self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
55+
}
56+
}
4157
}
4258

4359
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
4460
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
4561
// `Operand::Copy` is only supposed to be used with `Copy` types.
4662
if let Operand::Copy(place) = operand {
4763
let ty = place.ty(&self.body.local_decls, self.tcx).ty;
64+
let span = self.body.source_info(location).span;
4865

49-
if !ty.is_copy_modulo_regions(self.tcx, self.param_env, DUMMY_SP) {
50-
self.fail(
51-
DUMMY_SP,
52-
format!("`Operand::Copy` with non-`Copy` type {} at {:?}", ty, location),
53-
);
66+
if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
67+
self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
5468
}
5569
}
5670

@@ -65,16 +79,107 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
6579
Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
6680
if dest == src {
6781
self.fail(
68-
DUMMY_SP,
69-
format!(
70-
"encountered `Assign` statement with overlapping memory at {:?}",
71-
location
72-
),
82+
location,
83+
"encountered `Assign` statement with overlapping memory",
7384
);
7485
}
7586
}
7687
_ => {}
7788
}
7889
}
7990
}
91+
92+
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
93+
match &terminator.kind {
94+
TerminatorKind::Goto { target } => {
95+
self.check_bb(location, *target);
96+
}
97+
TerminatorKind::SwitchInt { targets, values, .. } => {
98+
if targets.len() != values.len() + 1 {
99+
self.fail(
100+
location,
101+
format!(
102+
"encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
103+
values.len(),
104+
targets.len(),
105+
),
106+
);
107+
}
108+
for target in targets {
109+
self.check_bb(location, *target);
110+
}
111+
}
112+
TerminatorKind::Drop { target, unwind, .. } => {
113+
self.check_bb(location, *target);
114+
if let Some(unwind) = unwind {
115+
self.check_bb(location, *unwind);
116+
}
117+
}
118+
TerminatorKind::DropAndReplace { target, unwind, .. } => {
119+
self.check_bb(location, *target);
120+
if let Some(unwind) = unwind {
121+
self.check_bb(location, *unwind);
122+
}
123+
}
124+
TerminatorKind::Call { func, destination, cleanup, .. } => {
125+
let func_ty = func.ty(&self.body.local_decls, self.tcx);
126+
match func_ty.kind {
127+
ty::FnPtr(..) | ty::FnDef(..) => {}
128+
_ => self.fail(
129+
location,
130+
format!("encountered non-callable type {} in `Call` terminator", func_ty),
131+
),
132+
}
133+
if let Some((_, target)) = destination {
134+
self.check_bb(location, *target);
135+
}
136+
if let Some(cleanup) = cleanup {
137+
self.check_bb(location, *cleanup);
138+
}
139+
}
140+
TerminatorKind::Assert { cond, target, cleanup, .. } => {
141+
let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
142+
if cond_ty != self.tcx.types.bool {
143+
self.fail(
144+
location,
145+
format!(
146+
"encountered non-boolean condition of type {} in `Assert` terminator",
147+
cond_ty
148+
),
149+
);
150+
}
151+
self.check_bb(location, *target);
152+
if let Some(cleanup) = cleanup {
153+
self.check_bb(location, *cleanup);
154+
}
155+
}
156+
TerminatorKind::Yield { resume, drop, .. } => {
157+
self.check_bb(location, *resume);
158+
if let Some(drop) = drop {
159+
self.check_bb(location, *drop);
160+
}
161+
}
162+
TerminatorKind::FalseEdges { real_target, imaginary_target } => {
163+
self.check_bb(location, *real_target);
164+
self.check_bb(location, *imaginary_target);
165+
}
166+
TerminatorKind::FalseUnwind { real_target, unwind } => {
167+
self.check_bb(location, *real_target);
168+
if let Some(unwind) = unwind {
169+
self.check_bb(location, *unwind);
170+
}
171+
}
172+
TerminatorKind::InlineAsm { destination, .. } => {
173+
if let Some(destination) = destination {
174+
self.check_bb(location, *destination);
175+
}
176+
}
177+
// Nothing to validate for these.
178+
TerminatorKind::Resume
179+
| TerminatorKind::Abort
180+
| TerminatorKind::Return
181+
| TerminatorKind::Unreachable
182+
| TerminatorKind::GeneratorDrop => {}
183+
}
184+
}
80185
}

0 commit comments

Comments
 (0)