@@ -2961,3 +2961,92 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
29612961 }
29622962 }
29632963}
2964+
2965+ declare_lint ! {
2966+ /// The `deref_nullptr` lint detects when an null pointer is dereferenced,
2967+ /// which causes [undefined behavior].
2968+ ///
2969+ /// ### Example
2970+ ///
2971+ /// ```rust,no_run
2972+ /// let x: i32 = unsafe {
2973+ /// *ptr::null()
2974+ /// };
2975+ /// ```
2976+ /// ```rust,no_run
2977+ /// unsafe {
2978+ /// *(0 as *const i32);
2979+ /// }
2980+ /// ```
2981+ ///
2982+ /// {{produces}}
2983+ ///
2984+ /// ### Explanation
2985+ ///
2986+ ///
2987+ /// Dereferencing a null pointer causes [undefined behavior] even as a place expression,
2988+ /// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`.
2989+ ///
2990+ /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
2991+ pub DEREF_NULLPTR ,
2992+ Warn ,
2993+ "detects when an null pointer is dereferenced"
2994+ }
2995+
2996+ declare_lint_pass ! ( DerefNullPtr => [ DEREF_NULLPTR ] ) ;
2997+
2998+ impl < ' tcx > LateLintPass < ' tcx > for DerefNullPtr {
2999+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & hir:: Expr < ' _ > ) {
3000+ /// test if expression is a null ptr
3001+ fn is_null_ptr ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
3002+ match & expr. kind {
3003+ rustc_hir:: ExprKind :: Cast ( ref expr, ref ty) => {
3004+ if let rustc_hir:: TyKind :: Ptr ( _) = ty. kind {
3005+ return is_zero ( expr) || is_null_ptr ( cx, expr) ;
3006+ }
3007+ }
3008+ // check for call to `core::ptr::null` or `core::ptr::null_mut`
3009+ rustc_hir:: ExprKind :: Call ( ref path, _) => {
3010+ if let rustc_hir:: ExprKind :: Path ( ref qpath) = path. kind {
3011+ if let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( ) {
3012+ return cx. tcx . is_diagnostic_item ( sym:: ptr_null, def_id)
3013+ || cx. tcx . is_diagnostic_item ( sym:: ptr_null_mut, def_id) ;
3014+ }
3015+ }
3016+ }
3017+ _ => { }
3018+ }
3019+ false
3020+ }
3021+
3022+ /// test if experssion is the literal `0`
3023+ fn is_zero ( expr : & hir:: Expr < ' _ > ) -> bool {
3024+ match & expr. kind {
3025+ rustc_hir:: ExprKind :: Lit ( ref lit) => {
3026+ if let LitKind :: Int ( a, _) = lit. node {
3027+ return a == 0 ;
3028+ }
3029+ }
3030+ _ => { }
3031+ }
3032+ false
3033+ }
3034+
3035+ if let rustc_hir:: ExprKind :: Unary ( ref un_op, ref expr_deref) = expr. kind {
3036+ if let rustc_hir:: UnOp :: Deref = un_op {
3037+ if is_null_ptr ( cx, expr_deref) {
3038+ cx. struct_span_lint ( DEREF_NULLPTR , expr. span , |lint| {
3039+ let mut err =
3040+ lint. build ( "Dereferencing a null pointer causes undefined behavior" ) ;
3041+ err. span_label ( expr. span , "a null pointer is dereferenced" ) ;
3042+ err. span_label (
3043+ expr. span ,
3044+ "this code causes undefined behavior when executed" ,
3045+ ) ;
3046+ err. emit ( ) ;
3047+ } ) ;
3048+ }
3049+ }
3050+ }
3051+ }
3052+ }
0 commit comments