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,30 @@ 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,compile_fail
43+ /// # use std::{slice, ptr};
44+ /// // Undefined behavior
45+ /// # let _slice: &[u8] =
46+ /// unsafe { slice::from_raw_parts(ptr::null(), 0) };
47+ /// ```
48+ ///
49+ /// {{produces}}
50+ ///
51+ /// ### Explanation
52+ ///
53+ /// Calling methods whos safety invariants requires non-null ptr with a null-ptr
54+ /// is undefined behavior.
55+ INVALID_NULL_PTR_USAGES ,
56+ Deny ,
57+ "invalid call with null ptr"
58+ }
59+
60+ declare_lint_pass ! ( PtrNullChecks => [ USELESS_PTR_NULL_CHECKS , INVALID_NULL_PTR_USAGES ] ) ;
3361
3462/// This function checks if the expression is from a series of consecutive casts,
3563/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either
@@ -83,6 +111,24 @@ fn useless_check<'a, 'tcx: 'a>(
83111 }
84112}
85113
114+ fn is_null_ptr < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> bool {
115+ let ( expr, _) = peel_casts ( cx, expr) ;
116+
117+ if let ExprKind :: Call ( path, [ ] ) = expr. kind
118+ && let ExprKind :: Path ( ref qpath) = path. kind
119+ && let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
120+ && let Some ( diag_item) = cx. tcx . get_diagnostic_name ( def_id)
121+ {
122+ diag_item == sym:: ptr_null || diag_item == sym:: ptr_null_mut
123+ } else if let ExprKind :: Lit ( spanned) = expr. kind
124+ && let LitKind :: Int ( v, _) = spanned. node
125+ {
126+ v == 0
127+ } else {
128+ false
129+ }
130+ }
131+
86132impl < ' tcx > LateLintPass < ' tcx > for PtrNullChecks {
87133 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
88134 match expr. kind {
@@ -100,6 +146,54 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
100146 cx. emit_spanned_lint ( USELESS_PTR_NULL_CHECKS , expr. span , diag)
101147 }
102148
149+ // Catching:
150+ // <path>(arg...) where `arg` is null-ptr and `path` is a fn that expect non-null-ptr
151+ ExprKind :: Call ( path, args)
152+ if let ExprKind :: Path ( ref qpath) = path. kind
153+ && let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
154+ && let Some ( diag_name) = cx. tcx . get_diagnostic_name ( def_id) =>
155+ {
156+ // `arg` positions where null would cause U.B.
157+ let arg_indices: & [ _ ] = match diag_name {
158+ sym:: ptr_read
159+ | sym:: ptr_read_unaligned
160+ | sym:: ptr_read_volatile
161+ | sym:: ptr_replace
162+ | sym:: ptr_write
163+ | sym:: ptr_write_bytes
164+ | sym:: ptr_write_unaligned
165+ | sym:: ptr_write_volatile
166+ | sym:: slice_from_raw_parts
167+ | sym:: slice_from_raw_parts_mut => & [ 0 ] ,
168+ sym:: ptr_copy
169+ | sym:: ptr_copy_nonoverlapping
170+ | sym:: ptr_swap
171+ | sym:: ptr_swap_nonoverlapping => & [ 0 , 1 ] ,
172+ _ => return ,
173+ } ;
174+
175+ for & arg_idx in arg_indices {
176+ if let Some ( arg) = args. get ( arg_idx) . filter ( |arg| is_null_ptr ( cx, arg) ) {
177+ let arg_span = arg. span ;
178+
179+ let suggestion = if let ExprKind :: Cast ( ..) = arg. peel_blocks ( ) . kind
180+ && let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( arg)
181+ && let RawPtr ( TypeAndMut { ty, .. } ) = ty. kind ( )
182+ {
183+ InvalidNullPtrUsagesSuggestion :: WithExplicitType { ty : * ty, arg_span }
184+ } else {
185+ InvalidNullPtrUsagesSuggestion :: WithoutExplicitType { arg_span }
186+ } ;
187+
188+ cx. emit_spanned_lint (
189+ INVALID_NULL_PTR_USAGES ,
190+ expr. span ,
191+ InvalidNullPtrUsagesDiag { suggestion } ,
192+ )
193+ }
194+ }
195+ }
196+
103197 // Catching:
104198 // (fn_ptr as *<const/mut> <ty>).is_null()
105199 ExprKind :: MethodCall ( _, receiver, _, _)
0 commit comments