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