rustc_mir_transform/
check_alignment.rs1use rustc_abi::Align;
2use rustc_hir::attrs::lang_items::LangItem;
3use rustc_index::IndexVec;
4use rustc_middle::mir::interpret::Scalar;
5use rustc_middle::mir::visit::PlaceContext;
6use rustc_middle::mir::*;
7use rustc_middle::ty::{Ty, TyCtxt};
8use rustc_session::Session;
9
10use crate::PassPolicy;
11use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers};
12
13pub(super) struct CheckAlignment;
14
15impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
16 fn policy(&self, sess: &Session) -> PassPolicy {
17 PassPolicy::optional_non_optimization(sess.ub_checks())
19 }
20
21 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
22 let excluded_pointees = [tcx.types.bool, tcx.types.i8, tcx.types.u8];
24
25 check_pointers(
29 tcx,
30 body,
31 &excluded_pointees,
32 insert_alignment_check,
33 BorrowedFieldProjectionMode::FollowProjections,
34 );
35 }
36}
37
38fn insert_alignment_check<'tcx>(
41 tcx: TyCtxt<'tcx>,
42 pointer: Place<'tcx>,
43 pointee_ty: Ty<'tcx>,
44 _context: PlaceContext,
45 local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
46 stmts: &mut Vec<Statement<'tcx>>,
47 source_info: SourceInfo,
48) -> PointerCheck<'tcx> {
49 let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
51 let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr);
52 let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into();
53 stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((thin_ptr, rvalue)))));
54
55 let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize);
57 let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
58 stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((addr, rvalue)))));
59
60 let align_def_id = tcx.require_lang_item(LangItem::AlignOf, source_info.span);
62 let alignment =
63 Operand::unevaluated_constant(tcx, align_def_id, &[pointee_ty.into()], source_info.span);
64
65 let alignment_mask =
67 local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
68 let one = Operand::Constant(Box::new(ConstOperand {
69 span: source_info.span,
70 user_ty: None,
71 const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(1, &tcx)), tcx.types.usize),
72 }));
73 stmts.push(Statement::new(
74 source_info,
75 StatementKind::Assign(Box::new((
76 alignment_mask,
77 Rvalue::BinaryOp(BinOp::Sub, Box::new((alignment.clone(), one))),
78 ))),
79 ));
80
81 if let max_align = tcx.sess.target.max_reliable_alignment()
84 && max_align < Align::MAX
85 {
86 let max_mask = max_align.bytes() - 1;
87 let max_mask = Operand::Constant(Box::new(ConstOperand {
88 span: source_info.span,
89 user_ty: None,
90 const_: Const::Val(
91 ConstValue::Scalar(Scalar::from_target_usize(max_mask, &tcx)),
92 tcx.types.usize,
93 ),
94 }));
95 stmts.push(Statement::new(
96 source_info,
97 StatementKind::Assign(Box::new((
98 alignment_mask,
99 Rvalue::BinaryOp(
100 BinOp::BitAnd,
101 Box::new((Operand::Copy(alignment_mask), max_mask)),
102 ),
103 ))),
104 ));
105 }
106
107 let alignment_bits =
109 local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
110 stmts.push(Statement::new(
111 source_info,
112 StatementKind::Assign(Box::new((
113 alignment_bits,
114 Rvalue::BinaryOp(
115 BinOp::BitAnd,
116 Box::new((Operand::Copy(addr), Operand::Copy(alignment_mask))),
117 ),
118 ))),
119 ));
120
121 let is_ok = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
123 let zero = Operand::Constant(Box::new(ConstOperand {
124 span: source_info.span,
125 user_ty: None,
126 const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(0, &tcx)), tcx.types.usize),
127 }));
128 stmts.push(Statement::new(
129 source_info,
130 StatementKind::Assign(Box::new((
131 is_ok,
132 Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(alignment_bits), zero.clone()))),
133 ))),
134 ));
135
136 PointerCheck {
139 cond: Operand::Copy(is_ok),
140 assert_kind: Box::new(AssertKind::MisalignedPointerDereference {
141 required: alignment,
142 found: Operand::Copy(addr),
143 }),
144 }
145}