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