1- use rustc_hir:: lang_items:: LangItem ;
21use rustc_index:: IndexVec ;
32use rustc_middle:: mir:: interpret:: Scalar ;
4- use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
53use rustc_middle:: mir:: * ;
6- use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
4+ use rustc_middle:: ty:: { Ty , TyCtxt } ;
75use rustc_session:: Session ;
8- use tracing:: { debug, trace} ;
6+
7+ use crate :: check_pointers:: { BorrowCheckMode , PointerCheck , check_pointers} ;
98
109pub ( super ) struct CheckAlignment ;
1110
@@ -19,162 +18,49 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
1918 }
2019
2120 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+ ) ;
13034 }
13135}
13236
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.
14939fn insert_alignment_check < ' tcx > (
15040 tcx : TyCtxt < ' tcx > ,
151- local_decls : & mut IndexVec < Local , LocalDecl < ' tcx > > ,
152- block_data : & mut BasicBlockData < ' tcx > ,
15341 pointer : Place < ' tcx > ,
15442 pointee_ty : Ty < ' tcx > ,
43+ local_decls : & mut IndexVec < Local , LocalDecl < ' tcx > > ,
44+ stmts : & mut Vec < Statement < ' tcx > > ,
15545 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 ().
15948 let const_raw_ptr = Ty :: new_imm_ptr ( tcx, tcx. types . unit ) ;
16049 let rvalue = Rvalue :: Cast ( CastKind :: PtrToPtr , Operand :: Copy ( pointer) , const_raw_ptr) ;
16150 let thin_ptr = local_decls. push ( LocalDecl :: with_source_info ( const_raw_ptr, source_info) ) . into ( ) ;
162- block_data
163- . statements
51+ stmts
16452 . push ( Statement { source_info, kind : StatementKind :: Assign ( Box :: new ( ( thin_ptr, rvalue) ) ) } ) ;
16553
166- // Transmute the pointer to a usize (equivalent to `ptr.addr()`)
54+ // Transmute the pointer to a usize (equivalent to `ptr.addr()`).
16755 let rvalue = Rvalue :: Cast ( CastKind :: Transmute , Operand :: Copy ( thin_ptr) , tcx. types . usize ) ;
16856 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) ) ) } ) ;
17258
17359 // Get the alignment of the pointee
17460 let alignment =
17561 local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
17662 let rvalue = Rvalue :: NullaryOp ( NullOp :: AlignOf , pointee_ty) ;
177- block_data . statements . push ( Statement {
63+ stmts . push ( Statement {
17864 source_info,
17965 kind : StatementKind :: Assign ( Box :: new ( ( alignment, rvalue) ) ) ,
18066 } ) ;
@@ -187,7 +73,7 @@ fn insert_alignment_check<'tcx>(
18773 user_ty : None ,
18874 const_ : Const :: Val ( ConstValue :: Scalar ( Scalar :: from_target_usize ( 1 , & tcx) ) , tcx. types . usize ) ,
18975 } ) ) ;
190- block_data . statements . push ( Statement {
76+ stmts . push ( Statement {
19177 source_info,
19278 kind : StatementKind :: Assign ( Box :: new ( (
19379 alignment_mask,
@@ -198,7 +84,7 @@ fn insert_alignment_check<'tcx>(
19884 // BitAnd the alignment mask with the pointer
19985 let alignment_bits =
20086 local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
201- block_data . statements . push ( Statement {
87+ stmts . push ( Statement {
20288 source_info,
20389 kind : StatementKind :: Assign ( Box :: new ( (
20490 alignment_bits,
@@ -216,29 +102,21 @@ fn insert_alignment_check<'tcx>(
216102 user_ty : None ,
217103 const_ : Const :: Val ( ConstValue :: Scalar ( Scalar :: from_target_usize ( 0 , & tcx) ) , tcx. types . usize ) ,
218104 } ) ) ;
219- block_data . statements . push ( Statement {
105+ stmts . push ( Statement {
220106 source_info,
221107 kind : StatementKind :: Assign ( Box :: new ( (
222108 is_ok,
223109 Rvalue :: BinaryOp ( BinOp :: Eq , Box :: new ( ( Operand :: Copy ( alignment_bits) , zero. clone ( ) ) ) ) ,
224110 ) ) ) ,
225111 } ) ;
226112
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+ }
244122}
0 commit comments