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,166 +18,53 @@ 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- }
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+ ) ;
6234 }
6335
6436 fn is_required ( & self ) -> bool {
6537 true
6638 }
6739}
6840
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.
15343fn insert_alignment_check < ' tcx > (
15444 tcx : TyCtxt < ' tcx > ,
155- local_decls : & mut IndexVec < Local , LocalDecl < ' tcx > > ,
156- block_data : & mut BasicBlockData < ' tcx > ,
15745 pointer : Place < ' tcx > ,
15846 pointee_ty : Ty < ' tcx > ,
47+ local_decls : & mut IndexVec < Local , LocalDecl < ' tcx > > ,
48+ stmts : & mut Vec < Statement < ' tcx > > ,
15949 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 ().
16352 let const_raw_ptr = Ty :: new_imm_ptr ( tcx, tcx. types . unit ) ;
16453 let rvalue = Rvalue :: Cast ( CastKind :: PtrToPtr , Operand :: Copy ( pointer) , const_raw_ptr) ;
16554 let thin_ptr = local_decls. push ( LocalDecl :: with_source_info ( const_raw_ptr, source_info) ) . into ( ) ;
166- block_data
167- . statements
55+ stmts
16856 . push ( Statement { source_info, kind : StatementKind :: Assign ( Box :: new ( ( thin_ptr, rvalue) ) ) } ) ;
16957
170- // Transmute the pointer to a usize (equivalent to `ptr.addr()`)
58+ // Transmute the pointer to a usize (equivalent to `ptr.addr()`).
17159 let rvalue = Rvalue :: Cast ( CastKind :: Transmute , Operand :: Copy ( thin_ptr) , tcx. types . usize ) ;
17260 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) ) ) } ) ;
17662
17763 // Get the alignment of the pointee
17864 let alignment =
17965 local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
18066 let rvalue = Rvalue :: NullaryOp ( NullOp :: AlignOf , pointee_ty) ;
181- block_data . statements . push ( Statement {
67+ stmts . push ( Statement {
18268 source_info,
18369 kind : StatementKind :: Assign ( Box :: new ( ( alignment, rvalue) ) ) ,
18470 } ) ;
@@ -191,7 +77,7 @@ fn insert_alignment_check<'tcx>(
19177 user_ty : None ,
19278 const_ : Const :: Val ( ConstValue :: Scalar ( Scalar :: from_target_usize ( 1 , & tcx) ) , tcx. types . usize ) ,
19379 } ) ) ;
194- block_data . statements . push ( Statement {
80+ stmts . push ( Statement {
19581 source_info,
19682 kind : StatementKind :: Assign ( Box :: new ( (
19783 alignment_mask,
@@ -202,7 +88,7 @@ fn insert_alignment_check<'tcx>(
20288 // BitAnd the alignment mask with the pointer
20389 let alignment_bits =
20490 local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
205- block_data . statements . push ( Statement {
91+ stmts . push ( Statement {
20692 source_info,
20793 kind : StatementKind :: Assign ( Box :: new ( (
20894 alignment_bits,
@@ -220,29 +106,21 @@ fn insert_alignment_check<'tcx>(
220106 user_ty : None ,
221107 const_ : Const :: Val ( ConstValue :: Scalar ( Scalar :: from_target_usize ( 0 , & tcx) ) , tcx. types . usize ) ,
222108 } ) ) ;
223- block_data . statements . push ( Statement {
109+ stmts . push ( Statement {
224110 source_info,
225111 kind : StatementKind :: Assign ( Box :: new ( (
226112 is_ok,
227113 Rvalue :: BinaryOp ( BinOp :: Eq , Box :: new ( ( Operand :: Copy ( alignment_bits) , zero. clone ( ) ) ) ) ,
228114 ) ) ) ,
229115 } ) ;
230116
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+ }
248126}
0 commit comments