11use rustc_ast:: LitKind ;
22use rustc_hir:: { BinOpKind , Expr , ExprKind , TyKind } ;
3+ use rustc_middle:: ty:: RawPtr ;
34use rustc_session:: { declare_lint, declare_lint_pass} ;
4- use rustc_span:: sym;
5+ use rustc_span:: { Span , sym} ;
56
6- use crate :: lints:: UselessPtrNullChecksDiag ;
7+ use crate :: lints:: {
8+ InvalidNullArgumentsDiag , InvalidNullArgumentsSuggestion , UselessPtrNullChecksDiag ,
9+ } ;
10+ use crate :: utils:: peel_casts;
711use crate :: { LateContext , LateLintPass , LintContext } ;
812
913declare_lint ! {
@@ -31,7 +35,30 @@ declare_lint! {
3135 "useless checking of non-null-typed pointer"
3236}
3337
34- declare_lint_pass ! ( PtrNullChecks => [ USELESS_PTR_NULL_CHECKS ] ) ;
38+ declare_lint ! {
39+ /// The `invalid_null_arguments` lint checks for invalid usage of null pointers in arguments.
40+ ///
41+ /// ### Example
42+ ///
43+ /// ```rust,compile_fail
44+ /// # use std::{slice, ptr};
45+ /// // Undefined behavior
46+ /// # let _slice: &[u8] =
47+ /// unsafe { slice::from_raw_parts(ptr::null(), 0) };
48+ /// ```
49+ ///
50+ /// {{produces}}
51+ ///
52+ /// ### Explanation
53+ ///
54+ /// Calling methods whos safety invariants requires non-null ptr with a null pointer
55+ /// is Undefined Behavior!
56+ INVALID_NULL_ARGUMENTS ,
57+ Deny ,
58+ "invalid null pointer in arguments"
59+ }
60+
61+ declare_lint_pass ! ( PtrNullChecks => [ USELESS_PTR_NULL_CHECKS , INVALID_NULL_ARGUMENTS ] ) ;
3562
3663/// This function checks if the expression is from a series of consecutive casts,
3764/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either
@@ -85,6 +112,25 @@ fn useless_check<'a, 'tcx: 'a>(
85112 }
86113}
87114
115+ /// Checks if the given expression is a null pointer (modulo casting)
116+ fn is_null_ptr < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < Span > {
117+ let ( expr, _) = peel_casts ( cx, expr) ;
118+
119+ if let ExprKind :: Call ( path, [ ] ) = expr. kind
120+ && let ExprKind :: Path ( ref qpath) = path. kind
121+ && let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
122+ && let Some ( diag_item) = cx. tcx . get_diagnostic_name ( def_id)
123+ {
124+ ( diag_item == sym:: ptr_null || diag_item == sym:: ptr_null_mut) . then_some ( expr. span )
125+ } else if let ExprKind :: Lit ( spanned) = expr. kind
126+ && let LitKind :: Int ( v, _) = spanned. node
127+ {
128+ ( v == 0 ) . then_some ( expr. span )
129+ } else {
130+ None
131+ }
132+ }
133+
88134impl < ' tcx > LateLintPass < ' tcx > for PtrNullChecks {
89135 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
90136 match expr. kind {
@@ -102,6 +148,61 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
102148 cx. emit_span_lint ( USELESS_PTR_NULL_CHECKS , expr. span , diag)
103149 }
104150
151+ // Catching:
152+ // <path>(arg...) where `arg` is null-ptr and `path` is a fn that expect non-null-ptr
153+ ExprKind :: Call ( path, args)
154+ if let ExprKind :: Path ( ref qpath) = path. kind
155+ && let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
156+ && let Some ( diag_name) = cx. tcx . get_diagnostic_name ( def_id) =>
157+ {
158+ // `arg` positions where null would cause U.B.
159+ //
160+ // We should probably have a `rustc` attribute, but checking them is costly,
161+ // maybe if we checked for null ptr first, it would be acceptable?
162+ let arg_indices: & [ _ ] = match diag_name {
163+ sym:: ptr_read
164+ | sym:: ptr_read_unaligned
165+ | sym:: ptr_read_volatile
166+ | sym:: ptr_replace
167+ | sym:: ptr_write
168+ | sym:: ptr_write_bytes
169+ | sym:: ptr_write_unaligned
170+ | sym:: ptr_write_volatile
171+ | sym:: slice_from_raw_parts
172+ | sym:: slice_from_raw_parts_mut => & [ 0 ] ,
173+ sym:: ptr_copy
174+ | sym:: ptr_copy_nonoverlapping
175+ | sym:: ptr_swap
176+ | sym:: ptr_swap_nonoverlapping => & [ 0 , 1 ] ,
177+ _ => return ,
178+ } ;
179+
180+ for & arg_idx in arg_indices {
181+ if let Some ( arg) = args. get ( arg_idx)
182+ && let Some ( null_span) = is_null_ptr ( cx, arg)
183+ {
184+ let arg_span = arg. span ;
185+
186+ let suggestion = if let ExprKind :: Cast ( ..) = arg. peel_blocks ( ) . kind
187+ && let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( arg)
188+ && let RawPtr ( ty, _mutbl) = ty. kind ( )
189+ {
190+ InvalidNullArgumentsSuggestion :: WithExplicitType { ty : * ty, arg_span }
191+ } else {
192+ InvalidNullArgumentsSuggestion :: WithoutExplicitType { arg_span }
193+ } ;
194+
195+ let null_span = if arg_span != null_span { Some ( null_span) } else { None } ;
196+
197+ cx. emit_span_lint (
198+ INVALID_NULL_ARGUMENTS ,
199+ expr. span ,
200+ InvalidNullArgumentsDiag { null_span, suggestion } ,
201+ )
202+ }
203+ }
204+ }
205+
105206 // Catching:
106207 // (fn_ptr as *<const/mut> <ty>).is_null()
107208 ExprKind :: MethodCall ( _, receiver, _, _)
0 commit comments