|  | use rustc_abi::Align; | 
|  | use rustc_hir::LangItem; | 
|  | use rustc_index::IndexVec; | 
|  | use rustc_middle::mir::interpret::Scalar; | 
|  | use rustc_middle::mir::visit::PlaceContext; | 
|  | use rustc_middle::mir::*; | 
|  | use rustc_middle::ty::{Ty, TyCtxt}; | 
|  | use rustc_session::Session; | 
|  |  | 
|  | use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers}; | 
|  |  | 
|  | pub(super) struct CheckAlignment; | 
|  |  | 
|  | impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { | 
|  | fn is_enabled(&self, sess: &Session) -> bool { | 
|  | sess.ub_checks() | 
|  | } | 
|  |  | 
|  | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | 
|  | // Skip trivially aligned place types. | 
|  | let excluded_pointees = [tcx.types.bool, tcx.types.i8, tcx.types.u8]; | 
|  |  | 
|  | // When checking the alignment of references to field projections (`&(*ptr).a`), | 
|  | // we need to make sure that the reference is aligned according to the field type | 
|  | // and not to the pointer type. | 
|  | check_pointers( | 
|  | tcx, | 
|  | body, | 
|  | &excluded_pointees, | 
|  | insert_alignment_check, | 
|  | BorrowedFieldProjectionMode::FollowProjections, | 
|  | ); | 
|  | } | 
|  |  | 
|  | fn is_required(&self) -> bool { | 
|  | true | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Inserts the actual alignment check's logic. Returns a | 
|  | /// [AssertKind::MisalignedPointerDereference] on failure. | 
|  | fn insert_alignment_check<'tcx>( | 
|  | tcx: TyCtxt<'tcx>, | 
|  | pointer: Place<'tcx>, | 
|  | pointee_ty: Ty<'tcx>, | 
|  | _context: PlaceContext, | 
|  | local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>, | 
|  | stmts: &mut Vec<Statement<'tcx>>, | 
|  | source_info: SourceInfo, | 
|  | ) -> PointerCheck<'tcx> { | 
|  | // Cast the pointer to a *const (). | 
|  | let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); | 
|  | let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr); | 
|  | let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into(); | 
|  | stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((thin_ptr, rvalue))))); | 
|  |  | 
|  | // Transmute the pointer to a usize (equivalent to `ptr.addr()`). | 
|  | let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize); | 
|  | let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); | 
|  | stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((addr, rvalue))))); | 
|  |  | 
|  | // Get the alignment of the pointee | 
|  | let align_def_id = tcx.require_lang_item(LangItem::AlignOf, source_info.span); | 
|  | let alignment = | 
|  | Operand::unevaluated_constant(tcx, align_def_id, &[pointee_ty.into()], source_info.span); | 
|  |  | 
|  | // Subtract 1 from the alignment to get the alignment mask | 
|  | let alignment_mask = | 
|  | local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); | 
|  | let one = Operand::Constant(Box::new(ConstOperand { | 
|  | span: source_info.span, | 
|  | user_ty: None, | 
|  | const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(1, &tcx)), tcx.types.usize), | 
|  | })); | 
|  | stmts.push(Statement::new( | 
|  | source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | alignment_mask, | 
|  | Rvalue::BinaryOp(BinOp::Sub, Box::new((alignment.clone(), one))), | 
|  | ))), | 
|  | )); | 
|  |  | 
|  | // If this target does not have reliable alignment, further limit the mask by anding it with | 
|  | // the mask for the highest reliable alignment. | 
|  | #[allow(irrefutable_let_patterns)] | 
|  | if let max_align = tcx.sess.target.max_reliable_alignment() | 
|  | && max_align < Align::MAX | 
|  | { | 
|  | let max_mask = max_align.bytes() - 1; | 
|  | let max_mask = Operand::Constant(Box::new(ConstOperand { | 
|  | span: source_info.span, | 
|  | user_ty: None, | 
|  | const_: Const::Val( | 
|  | ConstValue::Scalar(Scalar::from_target_usize(max_mask, &tcx)), | 
|  | tcx.types.usize, | 
|  | ), | 
|  | })); | 
|  | stmts.push(Statement::new( | 
|  | source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | alignment_mask, | 
|  | Rvalue::BinaryOp( | 
|  | BinOp::BitAnd, | 
|  | Box::new((Operand::Copy(alignment_mask), max_mask)), | 
|  | ), | 
|  | ))), | 
|  | )); | 
|  | } | 
|  |  | 
|  | // BitAnd the alignment mask with the pointer | 
|  | let alignment_bits = | 
|  | local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); | 
|  | stmts.push(Statement::new( | 
|  | source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | alignment_bits, | 
|  | Rvalue::BinaryOp( | 
|  | BinOp::BitAnd, | 
|  | Box::new((Operand::Copy(addr), Operand::Copy(alignment_mask))), | 
|  | ), | 
|  | ))), | 
|  | )); | 
|  |  | 
|  | // Check if the alignment bits are all zero | 
|  | let is_ok = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); | 
|  | let zero = Operand::Constant(Box::new(ConstOperand { | 
|  | span: source_info.span, | 
|  | user_ty: None, | 
|  | const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(0, &tcx)), tcx.types.usize), | 
|  | })); | 
|  | stmts.push(Statement::new( | 
|  | source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | is_ok, | 
|  | Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(alignment_bits), zero.clone()))), | 
|  | ))), | 
|  | )); | 
|  |  | 
|  | // Emit a check that asserts on the alignment and otherwise triggers a | 
|  | // AssertKind::MisalignedPointerDereference. | 
|  | PointerCheck { | 
|  | cond: Operand::Copy(is_ok), | 
|  | assert_kind: Box::new(AssertKind::MisalignedPointerDereference { | 
|  | required: alignment, | 
|  | found: Operand::Copy(addr), | 
|  | }), | 
|  | } | 
|  | } |