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
+ } ;
2
6
use rustc_ast:: LitKind ;
3
7
use rustc_hir:: { BinOpKind , Expr , ExprKind , TyKind } ;
8
+ use rustc_middle:: ty:: { RawPtr , TypeAndMut } ;
4
9
use rustc_session:: { declare_lint, declare_lint_pass} ;
5
10
use rustc_span:: sym;
6
11
@@ -29,7 +34,29 @@ declare_lint! {
29
34
"useless checking of non-null-typed pointer"
30
35
}
31
36
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 ] ) ;
33
60
34
61
/// This function checks if the expression is from a series of consecutive casts,
35
62
/// 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>(
83
110
}
84
111
}
85
112
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
+
86
131
impl < ' tcx > LateLintPass < ' tcx > for PtrNullChecks {
87
132
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
88
133
match expr. kind {
@@ -100,6 +145,54 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
100
145
cx. emit_spanned_lint ( USELESS_PTR_NULL_CHECKS , expr. span , diag)
101
146
}
102
147
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
+
103
196
// Catching:
104
197
// (fn_ptr as *<const/mut> <ty>).is_null()
105
198
ExprKind :: MethodCall ( _, receiver, _, _)
0 commit comments