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,162 +18,49 @@ 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
- }
62
- }
63
- }
64
-
65
- struct PointerFinder < ' a , ' tcx > {
66
- tcx : TyCtxt < ' tcx > ,
67
- local_decls : & ' a mut LocalDecls < ' tcx > ,
68
- typing_env : ty:: TypingEnv < ' tcx > ,
69
- pointers : Vec < ( Place < ' tcx > , Ty < ' tcx > ) > ,
70
- }
71
-
72
- impl < ' a , ' tcx > Visitor < ' tcx > for PointerFinder < ' a , ' tcx > {
73
- fn visit_place ( & mut self , place : & Place < ' tcx > , context : PlaceContext , location : Location ) {
74
- // We want to only check reads and writes to Places, so we specifically exclude
75
- // Borrow and RawBorrow.
76
- match context {
77
- PlaceContext :: MutatingUse (
78
- MutatingUseContext :: Store
79
- | MutatingUseContext :: AsmOutput
80
- | MutatingUseContext :: Call
81
- | MutatingUseContext :: Yield
82
- | MutatingUseContext :: Drop ,
83
- ) => { }
84
- PlaceContext :: NonMutatingUse (
85
- NonMutatingUseContext :: Copy | NonMutatingUseContext :: Move ,
86
- ) => { }
87
- _ => {
88
- return ;
89
- }
90
- }
91
-
92
- if !place. is_indirect ( ) {
93
- return ;
94
- }
95
-
96
- // Since Deref projections must come first and only once, the pointer for an indirect place
97
- // is the Local that the Place is based on.
98
- let pointer = Place :: from ( place. local ) ;
99
- let pointer_ty = self . local_decls [ place. local ] . ty ;
100
-
101
- // We only want to check places based on unsafe pointers
102
- if !pointer_ty. is_unsafe_ptr ( ) {
103
- trace ! ( "Indirect, but not based on an unsafe ptr, not checking {:?}" , place) ;
104
- return ;
105
- }
106
-
107
- let pointee_ty =
108
- pointer_ty. builtin_deref ( true ) . expect ( "no builtin_deref for an unsafe pointer" ) ;
109
- // Ideally we'd support this in the future, but for now we are limited to sized types.
110
- if !pointee_ty. is_sized ( self . tcx , self . typing_env ) {
111
- debug ! ( "Unsafe pointer, but pointee is not known to be sized: {:?}" , pointer_ty) ;
112
- return ;
113
- }
114
-
115
- // Try to detect types we are sure have an alignment of 1 and skip the check
116
- // We don't need to look for str and slices, we already rejected unsized types above
117
- let element_ty = match pointee_ty. kind ( ) {
118
- ty:: Array ( ty, _) => * ty,
119
- _ => pointee_ty,
120
- } ;
121
- if [ self . tcx . types . bool , self . tcx . types . i8 , self . tcx . types . u8 ] . contains ( & element_ty) {
122
- debug ! ( "Trivially aligned place type: {:?}" , pointee_ty) ;
123
- return ;
124
- }
125
-
126
- // Ensure that this place is based on an aligned pointer.
127
- self . pointers . push ( ( pointer, pointee_ty) ) ;
128
-
129
- self . super_place ( place, context, location) ;
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
+ ) ;
130
34
}
131
35
}
132
36
133
- fn split_block (
134
- basic_blocks : & mut IndexVec < BasicBlock , BasicBlockData < ' _ > > ,
135
- location : Location ,
136
- ) -> BasicBlock {
137
- let block_data = & mut basic_blocks[ location. block ] ;
138
-
139
- // Drain every statement after this one and move the current terminator to a new basic block
140
- let new_block = BasicBlockData {
141
- statements : block_data. statements . split_off ( location. statement_index ) ,
142
- terminator : block_data. terminator . take ( ) ,
143
- is_cleanup : block_data. is_cleanup ,
144
- } ;
145
-
146
- basic_blocks. push ( new_block)
147
- }
148
-
37
+ /// Inserts the actual alignment check's logic. Returns a
38
+ /// [AssertKind::MisalignedPointerDereference] on failure.
149
39
fn insert_alignment_check < ' tcx > (
150
40
tcx : TyCtxt < ' tcx > ,
151
- local_decls : & mut IndexVec < Local , LocalDecl < ' tcx > > ,
152
- block_data : & mut BasicBlockData < ' tcx > ,
153
41
pointer : Place < ' tcx > ,
154
42
pointee_ty : Ty < ' tcx > ,
43
+ local_decls : & mut IndexVec < Local , LocalDecl < ' tcx > > ,
44
+ stmts : & mut Vec < Statement < ' tcx > > ,
155
45
source_info : SourceInfo ,
156
- new_block : BasicBlock ,
157
- ) {
158
- // Cast the pointer to a *const ()
46
+ ) -> PointerCheck < ' tcx > {
47
+ // Cast the pointer to a *const ().
159
48
let const_raw_ptr = Ty :: new_imm_ptr ( tcx, tcx. types . unit ) ;
160
49
let rvalue = Rvalue :: Cast ( CastKind :: PtrToPtr , Operand :: Copy ( pointer) , const_raw_ptr) ;
161
50
let thin_ptr = local_decls. push ( LocalDecl :: with_source_info ( const_raw_ptr, source_info) ) . into ( ) ;
162
- block_data
163
- . statements
51
+ stmts
164
52
. push ( Statement { source_info, kind : StatementKind :: Assign ( Box :: new ( ( thin_ptr, rvalue) ) ) } ) ;
165
53
166
- // Transmute the pointer to a usize (equivalent to `ptr.addr()`)
54
+ // Transmute the pointer to a usize (equivalent to `ptr.addr()`).
167
55
let rvalue = Rvalue :: Cast ( CastKind :: Transmute , Operand :: Copy ( thin_ptr) , tcx. types . usize ) ;
168
56
let addr = local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
169
- block_data
170
- . statements
171
- . push ( Statement { source_info, kind : StatementKind :: Assign ( Box :: new ( ( addr, rvalue) ) ) } ) ;
57
+ stmts. push ( Statement { source_info, kind : StatementKind :: Assign ( Box :: new ( ( addr, rvalue) ) ) } ) ;
172
58
173
59
// Get the alignment of the pointee
174
60
let alignment =
175
61
local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
176
62
let rvalue = Rvalue :: NullaryOp ( NullOp :: AlignOf , pointee_ty) ;
177
- block_data . statements . push ( Statement {
63
+ stmts . push ( Statement {
178
64
source_info,
179
65
kind : StatementKind :: Assign ( Box :: new ( ( alignment, rvalue) ) ) ,
180
66
} ) ;
@@ -187,7 +73,7 @@ fn insert_alignment_check<'tcx>(
187
73
user_ty : None ,
188
74
const_ : Const :: Val ( ConstValue :: Scalar ( Scalar :: from_target_usize ( 1 , & tcx) ) , tcx. types . usize ) ,
189
75
} ) ) ;
190
- block_data . statements . push ( Statement {
76
+ stmts . push ( Statement {
191
77
source_info,
192
78
kind : StatementKind :: Assign ( Box :: new ( (
193
79
alignment_mask,
@@ -198,7 +84,7 @@ fn insert_alignment_check<'tcx>(
198
84
// BitAnd the alignment mask with the pointer
199
85
let alignment_bits =
200
86
local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
201
- block_data . statements . push ( Statement {
87
+ stmts . push ( Statement {
202
88
source_info,
203
89
kind : StatementKind :: Assign ( Box :: new ( (
204
90
alignment_bits,
@@ -216,29 +102,21 @@ fn insert_alignment_check<'tcx>(
216
102
user_ty : None ,
217
103
const_ : Const :: Val ( ConstValue :: Scalar ( Scalar :: from_target_usize ( 0 , & tcx) ) , tcx. types . usize ) ,
218
104
} ) ) ;
219
- block_data . statements . push ( Statement {
105
+ stmts . push ( Statement {
220
106
source_info,
221
107
kind : StatementKind :: Assign ( Box :: new ( (
222
108
is_ok,
223
109
Rvalue :: BinaryOp ( BinOp :: Eq , Box :: new ( ( Operand :: Copy ( alignment_bits) , zero. clone ( ) ) ) ) ,
224
110
) ) ) ,
225
111
} ) ;
226
112
227
- // Set this block's terminator to our assert, continuing to new_block if we pass
228
- block_data. terminator = Some ( Terminator {
229
- source_info,
230
- kind : TerminatorKind :: Assert {
231
- cond : Operand :: Copy ( is_ok) ,
232
- expected : true ,
233
- target : new_block,
234
- msg : Box :: new ( AssertKind :: MisalignedPointerDereference {
235
- required : Operand :: Copy ( alignment) ,
236
- found : Operand :: Copy ( addr) ,
237
- } ) ,
238
- // This calls panic_misaligned_pointer_dereference, which is #[rustc_nounwind].
239
- // We never want to insert an unwind into unsafe code, because unwinding could
240
- // make a failing UB check turn into much worse UB when we start unwinding.
241
- unwind : UnwindAction :: Unreachable ,
242
- } ,
243
- } ) ;
113
+ // Emit a check that asserts on the alignment and otherwise triggers a
114
+ // AssertKind::MisalignedPointerDereference.
115
+ PointerCheck {
116
+ cond : Operand :: Copy ( is_ok) ,
117
+ assert_kind : Box :: new ( AssertKind :: MisalignedPointerDereference {
118
+ required : Operand :: Copy ( alignment) ,
119
+ found : Operand :: Copy ( addr) ,
120
+ } ) ,
121
+ }
244
122
}
0 commit comments