1- use crate :: { lints:: UselessPtrNullChecksDiag , LateContext , LateLintPass , LintContext } ;
1+ use crate :: {
2+ lints:: { InvalidNullPtrUsagesDiag , InvalidNullPtrUsagesSuggestion , UselessPtrNullChecksDiag } ,
3+ reference_casting:: peel_casts,
4+ LateContext , LateLintPass , LintContext ,
5+ } ;
26use rustc_ast:: LitKind ;
37use rustc_hir:: { BinOpKind , Expr , ExprKind , TyKind } ;
8+ use rustc_middle:: ty:: { RawPtr , TypeAndMut } ;
49use rustc_session:: { declare_lint, declare_lint_pass} ;
510use rustc_span:: sym;
611
@@ -29,7 +34,29 @@ declare_lint! {
2934 "useless checking of non-null-typed pointer"
3035}
3136
32- declare_lint_pass ! ( PtrNullChecks => [ USELESS_PTR_NULL_CHECKS ] ) ;
37+ declare_lint ! {
38+ /// The `invalid_null_ptr_usages` lint checks for invalid usage of null pointers.
39+ ///
40+ /// ### Example
41+ ///
42+ /// ```rust
43+ /// # use std::{slice, ptr};
44+ /// // Undefined behavior
45+ /// unsafe { slice::from_raw_parts(ptr::null(), 0); }
46+ /// ```
47+ ///
48+ /// {{produces}}
49+ ///
50+ /// ### Explanation
51+ ///
52+ /// Calling methods whos safety invariants requires non-null ptr with a null-ptr
53+ /// is undefined behavior.
54+ INVALID_NULL_PTR_USAGES ,
55+ Deny ,
56+ "invalid call with null ptr"
57+ }
58+
59+ declare_lint_pass ! ( PtrNullChecks => [ USELESS_PTR_NULL_CHECKS , INVALID_NULL_PTR_USAGES ] ) ;
3360
3461/// This function checks if the expression is from a series of consecutive casts,
3562/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either
@@ -83,6 +110,24 @@ fn useless_check<'a, 'tcx: 'a>(
83110 }
84111}
85112
113+ fn is_null_ptr < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> bool {
114+ let ( expr, _) = peel_casts ( cx, expr) ;
115+
116+ if let ExprKind :: Call ( path, [ ] ) = expr. kind
117+ && let ExprKind :: Path ( ref qpath) = path. kind
118+ && let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
119+ && let Some ( diag_item) = cx. tcx . get_diagnostic_name ( def_id)
120+ {
121+ diag_item == sym:: ptr_null || diag_item == sym:: ptr_null_mut
122+ } else if let ExprKind :: Lit ( spanned) = expr. kind
123+ && let LitKind :: Int ( v, _) = spanned. node
124+ {
125+ v == 0
126+ } else {
127+ false
128+ }
129+ }
130+
86131impl < ' tcx > LateLintPass < ' tcx > for PtrNullChecks {
87132 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
88133 match expr. kind {
@@ -100,6 +145,54 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
100145 cx. emit_spanned_lint ( USELESS_PTR_NULL_CHECKS , expr. span , diag)
101146 }
102147
148+ // Catching:
149+ // <path>(arg...) where `arg` is null-ptr and `path` is a fn that expect non-null-ptr
150+ ExprKind :: Call ( path, args)
151+ if let ExprKind :: Path ( ref qpath) = path. kind
152+ && let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
153+ && let Some ( diag_name) = cx. tcx . get_diagnostic_name ( def_id) =>
154+ {
155+ // `arg` positions where null would cause U.B.
156+ let arg_indices: & [ _ ] = match diag_name {
157+ sym:: ptr_read
158+ | sym:: ptr_read_unaligned
159+ | sym:: ptr_read_volatile
160+ | sym:: ptr_replace
161+ | sym:: ptr_write
162+ | sym:: ptr_write_bytes
163+ | sym:: ptr_write_unaligned
164+ | sym:: ptr_write_volatile
165+ | sym:: slice_from_raw_parts
166+ | sym:: slice_from_raw_parts_mut => & [ 0 ] ,
167+ sym:: ptr_copy
168+ | sym:: ptr_copy_nonoverlapping
169+ | sym:: ptr_swap
170+ | sym:: ptr_swap_nonoverlapping => & [ 0 , 1 ] ,
171+ _ => return ,
172+ } ;
173+
174+ for & arg_idx in arg_indices {
175+ if let Some ( arg) = args. get ( arg_idx) . filter ( |arg| is_null_ptr ( cx, arg) ) {
176+ let arg_span = arg. span ;
177+
178+ let suggestion = if let ExprKind :: Cast ( ..) = arg. peel_blocks ( ) . kind
179+ && let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( arg)
180+ && let RawPtr ( TypeAndMut { ty, .. } ) = ty. kind ( )
181+ {
182+ InvalidNullPtrUsagesSuggestion :: WithExplicitType { ty : * ty, arg_span }
183+ } else {
184+ InvalidNullPtrUsagesSuggestion :: WithoutExplicitType { arg_span }
185+ } ;
186+
187+ cx. emit_spanned_lint (
188+ INVALID_NULL_PTR_USAGES ,
189+ expr. span ,
190+ InvalidNullPtrUsagesDiag { suggestion } ,
191+ )
192+ }
193+ }
194+ }
195+
103196 // Catching:
104197 // (fn_ptr as *<const/mut> <ty>).is_null()
105198 ExprKind :: MethodCall ( _, receiver, _, _)
0 commit comments