1
- use rustc_hir::lang_items::LangItem;
2
1
use rustc_index::IndexVec;
3
2
use rustc_middle::mir::interpret::Scalar;
4
- use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
5
3
use rustc_middle::mir::*;
6
- use rustc_middle::ty::{self, Ty, TyCtxt};
4
+ use rustc_middle::ty::{Ty, TyCtxt};
7
5
use rustc_session::Session;
8
- use tracing::{debug, trace};
6
+
7
+ use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers};
9
8
10
9
pub(super) struct CheckAlignment;
11
10
@@ -19,166 +18,53 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
19
18
}
20
19
21
20
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
22
- // This pass emits new panics. If for whatever reason we do not have a panic
23
- // implementation, running this pass may cause otherwise-valid code to not compile.
24
- if tcx.lang_items().get(LangItem::PanicImpl).is_none() {
25
- return;
26
- }
27
-
28
- let typing_env = body.typing_env(tcx);
29
- let basic_blocks = body.basic_blocks.as_mut();
30
- let local_decls = &mut body.local_decls;
31
-
32
- // This pass inserts new blocks. Each insertion changes the Location for all
33
- // statements/blocks after. Iterating or visiting the MIR in order would require updating
34
- // our current location after every insertion. By iterating backwards, we dodge this issue:
35
- // The only Locations that an insertion changes have already been handled.
36
- for block in (0..basic_blocks.len()).rev() {
37
- let block = block.into();
38
- for statement_index in (0..basic_blocks[block].statements.len()).rev() {
39
- let location = Location { block, statement_index };
40
- let statement = &basic_blocks[block].statements[statement_index];
41
- let source_info = statement.source_info;
42
-
43
- let mut finder =
44
- PointerFinder { tcx, local_decls, typing_env, pointers: Vec::new() };
45
- finder.visit_statement(statement, location);
46
-
47
- for (local, ty) in finder.pointers {
48
- debug!("Inserting alignment check for {:?}", ty);
49
- let new_block = split_block(basic_blocks, location);
50
- insert_alignment_check(
51
- tcx,
52
- local_decls,
53
- &mut basic_blocks[block],
54
- local,
55
- ty,
56
- source_info,
57
- new_block,
58
- );
59
- }
60
- }
61
- }
21
+ // Skip trivially aligned place types.
22
+ let excluded_pointees = [tcx.types.bool, tcx.types.i8, tcx.types.u8];
23
+
24
+ // We have to exclude borrows here: in `&x.field`, the exact
25
+ // requirement is that the final reference must be aligned, but
26
+ // `check_pointers` would check that `x` is aligned, which would be wrong.
27
+ check_pointers(
28
+ tcx,
29
+ body,
30
+ &excluded_pointees,
31
+ insert_alignment_check,
32
+ BorrowCheckMode::ExcludeBorrows,
33
+ );
62
34
}
63
35
64
36
fn is_required(&self) -> bool {
65
37
true
66
38
}
67
39
}
68
40
69
- struct PointerFinder<'a, 'tcx> {
70
- tcx: TyCtxt<'tcx>,
71
- local_decls: &'a mut LocalDecls<'tcx>,
72
- typing_env: ty::TypingEnv<'tcx>,
73
- pointers: Vec<(Place<'tcx>, Ty<'tcx>)>,
74
- }
75
-
76
- impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
77
- fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
78
- // We want to only check reads and writes to Places, so we specifically exclude
79
- // Borrow and RawBorrow.
80
- match context {
81
- PlaceContext::MutatingUse(
82
- MutatingUseContext::Store
83
- | MutatingUseContext::AsmOutput
84
- | MutatingUseContext::Call
85
- | MutatingUseContext::Yield
86
- | MutatingUseContext::Drop,
87
- ) => {}
88
- PlaceContext::NonMutatingUse(
89
- NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
90
- ) => {}
91
- _ => {
92
- return;
93
- }
94
- }
95
-
96
- if !place.is_indirect() {
97
- return;
98
- }
99
-
100
- // Since Deref projections must come first and only once, the pointer for an indirect place
101
- // is the Local that the Place is based on.
102
- let pointer = Place::from(place.local);
103
- let pointer_ty = self.local_decls[place.local].ty;
104
-
105
- // We only want to check places based on unsafe pointers
106
- if !pointer_ty.is_unsafe_ptr() {
107
- trace!("Indirect, but not based on an unsafe ptr, not checking {:?}", place);
108
- return;
109
- }
110
-
111
- let pointee_ty =
112
- pointer_ty.builtin_deref(true).expect("no builtin_deref for an unsafe pointer");
113
- // Ideally we'd support this in the future, but for now we are limited to sized types.
114
- if !pointee_ty.is_sized(self.tcx, self.typing_env) {
115
- debug!("Unsafe pointer, but pointee is not known to be sized: {:?}", pointer_ty);
116
- return;
117
- }
118
-
119
- // Try to detect types we are sure have an alignment of 1 and skip the check
120
- // We don't need to look for str and slices, we already rejected unsized types above
121
- let element_ty = match pointee_ty.kind() {
122
- ty::Array(ty, _) => *ty,
123
- _ => pointee_ty,
124
- };
125
- if [self.tcx.types.bool, self.tcx.types.i8, self.tcx.types.u8].contains(&element_ty) {
126
- debug!("Trivially aligned place type: {:?}", pointee_ty);
127
- return;
128
- }
129
-
130
- // Ensure that this place is based on an aligned pointer.
131
- self.pointers.push((pointer, pointee_ty));
132
-
133
- self.super_place(place, context, location);
134
- }
135
- }
136
-
137
- fn split_block(
138
- basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'_>>,
139
- location: Location,
140
- ) -> BasicBlock {
141
- let block_data = &mut basic_blocks[location.block];
142
-
143
- // Drain every statement after this one and move the current terminator to a new basic block
144
- let new_block = BasicBlockData {
145
- statements: block_data.statements.split_off(location.statement_index),
146
- terminator: block_data.terminator.take(),
147
- is_cleanup: block_data.is_cleanup,
148
- };
149
-
150
- basic_blocks.push(new_block)
151
- }
152
-
41
+ /// Inserts the actual alignment check's logic. Returns a
42
+ /// [AssertKind::MisalignedPointerDereference] on failure.
153
43
fn insert_alignment_check<'tcx>(
154
44
tcx: TyCtxt<'tcx>,
155
- local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
156
- block_data: &mut BasicBlockData<'tcx>,
157
45
pointer: Place<'tcx>,
158
46
pointee_ty: Ty<'tcx>,
47
+ local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
48
+ stmts: &mut Vec<Statement<'tcx>>,
159
49
source_info: SourceInfo,
160
- new_block: BasicBlock,
161
- ) {
162
- // Cast the pointer to a *const ()
50
+ ) -> PointerCheck<'tcx> {
51
+ // Cast the pointer to a *const ().
163
52
let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
164
53
let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr);
165
54
let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into();
166
- block_data
167
- .statements
55
+ stmts
168
56
.push(Statement { source_info, kind: StatementKind::Assign(Box::new((thin_ptr, rvalue))) });
169
57
170
- // Transmute the pointer to a usize (equivalent to `ptr.addr()`)
58
+ // Transmute the pointer to a usize (equivalent to `ptr.addr()`).
171
59
let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize);
172
60
let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
173
- block_data
174
- .statements
175
- .push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) });
61
+ stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) });
176
62
177
63
// Get the alignment of the pointee
178
64
let alignment =
179
65
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
180
66
let rvalue = Rvalue::NullaryOp(NullOp::AlignOf, pointee_ty);
181
- block_data.statements .push(Statement {
67
+ stmts .push(Statement {
182
68
source_info,
183
69
kind: StatementKind::Assign(Box::new((alignment, rvalue))),
184
70
});
@@ -191,7 +77,7 @@ fn insert_alignment_check<'tcx>(
191
77
user_ty: None,
192
78
const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(1, &tcx)), tcx.types.usize),
193
79
}));
194
- block_data.statements .push(Statement {
80
+ stmts .push(Statement {
195
81
source_info,
196
82
kind: StatementKind::Assign(Box::new((
197
83
alignment_mask,
@@ -202,7 +88,7 @@ fn insert_alignment_check<'tcx>(
202
88
// BitAnd the alignment mask with the pointer
203
89
let alignment_bits =
204
90
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
205
- block_data.statements .push(Statement {
91
+ stmts .push(Statement {
206
92
source_info,
207
93
kind: StatementKind::Assign(Box::new((
208
94
alignment_bits,
@@ -220,29 +106,21 @@ fn insert_alignment_check<'tcx>(
220
106
user_ty: None,
221
107
const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(0, &tcx)), tcx.types.usize),
222
108
}));
223
- block_data.statements .push(Statement {
109
+ stmts .push(Statement {
224
110
source_info,
225
111
kind: StatementKind::Assign(Box::new((
226
112
is_ok,
227
113
Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(alignment_bits), zero.clone()))),
228
114
))),
229
115
});
230
116
231
- // Set this block's terminator to our assert, continuing to new_block if we pass
232
- block_data.terminator = Some(Terminator {
233
- source_info,
234
- kind: TerminatorKind::Assert {
235
- cond: Operand::Copy(is_ok),
236
- expected: true,
237
- target: new_block,
238
- msg: Box::new(AssertKind::MisalignedPointerDereference {
239
- required: Operand::Copy(alignment),
240
- found: Operand::Copy(addr),
241
- }),
242
- // This calls panic_misaligned_pointer_dereference, which is #[rustc_nounwind].
243
- // We never want to insert an unwind into unsafe code, because unwinding could
244
- // make a failing UB check turn into much worse UB when we start unwinding.
245
- unwind: UnwindAction::Unreachable,
246
- },
247
- });
117
+ // Emit a check that asserts on the alignment and otherwise triggers a
118
+ // AssertKind::MisalignedPointerDereference.
119
+ PointerCheck {
120
+ cond: Operand::Copy(is_ok),
121
+ assert_kind: Box::new(AssertKind::MisalignedPointerDereference {
122
+ required: Operand::Copy(alignment),
123
+ found: Operand::Copy(addr),
124
+ }),
125
+ }
248
126
}
0 commit comments