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, assert_kind) = match context {
59 PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
61 | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
62 (
64 Operand::Constant(Box::new(ConstOperand {
65 span: source_info.span,
66 user_ty: None,
67 const_: Const::from_bool(tcx, true),
68 })),
69 AssertKind::NullReferenceConstructed,
70 )
71 }
72 _ => {
74 let size_of = tcx.require_lang_item(LangItem::SizeOf, source_info.span);
75 let size_of =
76 Operand::unevaluated_constant(tcx, size_of, &[pointee_ty.into()], source_info.span);
77
78 let pointee_should_be_checked =
79 local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
80 let rvalue = Rvalue::BinaryOp(BinOp::Ne, Box::new((size_of, zero.clone())));
81 stmts.push(Statement::new(
82 source_info,
83 StatementKind::Assign(Box::new((pointee_should_be_checked, rvalue))),
84 ));
85 (Operand::Copy(pointee_should_be_checked), AssertKind::NullPointerDereference)
86 }
87 };
88
89 let is_null = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
91 stmts.push(Statement::new(
92 source_info,
93 StatementKind::Assign(Box::new((
94 is_null,
95 Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(addr), zero))),
96 ))),
97 ));
98
99 let should_throw_exception =
102 local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
103 stmts.push(Statement::new(
104 source_info,
105 StatementKind::Assign(Box::new((
106 should_throw_exception,
107 Rvalue::BinaryOp(
108 BinOp::BitAnd,
109 Box::new((Operand::Copy(is_null), pointee_should_be_checked)),
110 ),
111 ))),
112 ));
113
114 let is_ok = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
116 stmts.push(Statement::new(
117 source_info,
118 StatementKind::Assign(Box::new((
119 is_ok,
120 Rvalue::UnaryOp(UnOp::Not, Operand::Copy(should_throw_exception)),
121 ))),
122 ));
123
124 PointerCheck { cond: Operand::Copy(is_ok), assert_kind: Box::new(assert_kind) }
127}