rustc_mir_transform/
check_null.rs1use rustc_hir::LangItem;
2use rustc_index::IndexVec;
3use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext};
4use rustc_middle::mir::*;
5use rustc_middle::ty::{Ty, TyCtxt};
6use rustc_session::Session;
7
8use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers};
9
10pub(super) struct CheckNull;
11
12impl<'tcx> crate::MirPass<'tcx> for CheckNull {
13 fn is_enabled(&self, sess: &Session) -> bool {
14 sess.ub_checks()
15 }
16
17 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
18 check_pointers(
19 tcx,
20 body,
21 &[],
22 insert_null_check,
23 BorrowedFieldProjectionMode::NoFollowProjections,
24 );
25 }
26
27 fn is_required(&self) -> bool {
28 true
29 }
30}
31
32fn insert_null_check<'tcx>(
33 tcx: TyCtxt<'tcx>,
34 pointer: Place<'tcx>,
35 pointee_ty: Ty<'tcx>,
36 context: PlaceContext,
37 local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
38 stmts: &mut Vec<Statement<'tcx>>,
39 source_info: SourceInfo,
40) -> PointerCheck<'tcx> {
41 let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
43 let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr);
44 let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into();
45 stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((thin_ptr, rvalue)))));
46
47 let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize);
49 let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
50 stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((addr, rvalue)))));
51
52 let zero = Operand::Constant(Box::new(ConstOperand {
53 span: source_info.span,
54 user_ty: None,
55 const_: Const::Val(ConstValue::from_target_usize(0, &tcx), tcx.types.usize),
56 }));
57
58 let pointee_should_be_checked = match context {
59 PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
61 | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
62 Operand::Constant(Box::new(ConstOperand {
64 span: source_info.span,
65 user_ty: None,
66 const_: Const::from_bool(tcx, true),
67 }))
68 }
69 _ => {
71 let size_of = tcx.require_lang_item(LangItem::SizeOf, source_info.span);
72 let size_of =
73 Operand::unevaluated_constant(tcx, size_of, &[pointee_ty.into()], source_info.span);
74
75 let pointee_should_be_checked =
76 local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
77 let rvalue = Rvalue::BinaryOp(BinOp::Ne, Box::new((size_of, zero.clone())));
78 stmts.push(Statement::new(
79 source_info,
80 StatementKind::Assign(Box::new((pointee_should_be_checked, rvalue))),
81 ));
82 Operand::Copy(pointee_should_be_checked.into())
83 }
84 };
85
86 let is_null = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
88 stmts.push(Statement::new(
89 source_info,
90 StatementKind::Assign(Box::new((
91 is_null,
92 Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(addr), zero))),
93 ))),
94 ));
95
96 let should_throw_exception =
99 local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
100 stmts.push(Statement::new(
101 source_info,
102 StatementKind::Assign(Box::new((
103 should_throw_exception,
104 Rvalue::BinaryOp(
105 BinOp::BitAnd,
106 Box::new((Operand::Copy(is_null), pointee_should_be_checked)),
107 ),
108 ))),
109 ));
110
111 let is_ok = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
113 stmts.push(Statement::new(
114 source_info,
115 StatementKind::Assign(Box::new((
116 is_ok,
117 Rvalue::UnaryOp(UnOp::Not, Operand::Copy(should_throw_exception)),
118 ))),
119 ));
120
121 PointerCheck {
124 cond: Operand::Copy(is_ok),
125 assert_kind: Box::new(AssertKind::NullPointerDereference),
126 }
127}