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,30 @@ 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
+ /// # 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 ] ) ;
33
61
34
62
/// This function checks if the expression is from a series of consecutive casts,
35
63
/// 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>(
83
111
}
84
112
}
85
113
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
+
86
132
impl < ' tcx > LateLintPass < ' tcx > for PtrNullChecks {
87
133
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
88
134
match expr. kind {
@@ -100,6 +146,54 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
100
146
cx. emit_spanned_lint ( USELESS_PTR_NULL_CHECKS , expr. span , diag)
101
147
}
102
148
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
+
103
197
// Catching:
104
198
// (fn_ptr as *<const/mut> <ty>).is_null()
105
199
ExprKind :: MethodCall ( _, receiver, _, _)
0 commit comments