rustc_mir_transform/
check_null.rs1use rustc_hir::attrs::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 =
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), AssertKind::NullPointerDereference)
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 { cond: Operand::Copy(is_ok), assert_kind: Box::new(assert_kind) }
124}