Skip to content

Commit aaed11e

Browse files
committed
Uplift clippy::invalid_null_ptr_usage
1 parent 3dd2da6 commit aaed11e

File tree

6 files changed

+504
-2
lines changed

6 files changed

+504
-2
lines changed

compiler/rustc_lint/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be dir
313313
314314
lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable
315315
316+
lint_invalid_null_ptr_usages = calling this function with a null pointer is undefined behavior, even if the result of the function is unused, consider using a dangling pointer instead
317+
.suggestion = use a dangling pointer instead
318+
316319
lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
317320
.label = casting happend here
318321

compiler/rustc_lint/src/lints.rs

+22
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,28 @@ pub enum UselessPtrNullChecksDiag<'a> {
614614
FnRet { fn_name: Ident },
615615
}
616616

617+
#[derive(LintDiagnostic)]
618+
#[diag(lint_invalid_null_ptr_usages)]
619+
pub struct InvalidNullPtrUsagesDiag<'a> {
620+
#[subdiagnostic]
621+
pub suggestion: InvalidNullPtrUsagesSuggestion<'a>,
622+
}
623+
624+
#[derive(Subdiagnostic)]
625+
pub enum InvalidNullPtrUsagesSuggestion<'a> {
626+
#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")]
627+
WithoutExplicitType {
628+
#[suggestion_part(code = "core::ptr::NonNull::dangling().as_ptr()")]
629+
arg_span: Span,
630+
},
631+
#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")]
632+
WithExplicitType {
633+
ty: Ty<'a>,
634+
#[suggestion_part(code = "core::ptr::NonNull::<{ty}>::dangling().as_ptr()")]
635+
arg_span: Span,
636+
},
637+
}
638+
617639
// for_loops_over_fallibles.rs
618640
#[derive(LintDiagnostic)]
619641
#[diag(lint_for_loops_over_fallibles)]

compiler/rustc_lint/src/ptr_nulls.rs

+96-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
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+
};
26
use rustc_ast::LitKind;
37
use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind};
8+
use rustc_middle::ty::{RawPtr, TypeAndMut};
49
use rustc_session::{declare_lint, declare_lint_pass};
510
use 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
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+
86132
impl<'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, _, _)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// check-fail
2+
// run-rustfix
3+
4+
use std::ptr;
5+
use std::mem;
6+
7+
unsafe fn null_ptr() {
8+
ptr::write(
9+
//~^ ERROR calling this function with a null pointer is undefined behavior
10+
core::ptr::NonNull::<u32>::dangling().as_ptr(),
11+
mem::transmute::<[u8; 4], _>([0, 0, 0, 255]),
12+
);
13+
14+
let _: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
15+
//~^ ERROR calling this function with a null pointer is undefined behavior
16+
let _: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
17+
//~^ ERROR calling this function with a null pointer is undefined behavior
18+
let _: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::<usize>::dangling().as_ptr(), 0);
19+
//~^ ERROR calling this function with a null pointer is undefined behavior
20+
let _: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
21+
//~^ ERROR calling this function with a null pointer is undefined behavior
22+
23+
let _: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
24+
//~^ ERROR calling this function with a null pointer is undefined behavior
25+
26+
ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), ptr::NonNull::dangling().as_ptr(), 0);
27+
//~^ ERROR calling this function with a null pointer is undefined behavior
28+
ptr::copy::<usize>(ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
29+
//~^ ERROR calling this function with a null pointer is undefined behavior
30+
31+
ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), ptr::NonNull::dangling().as_ptr(), 0);
32+
//~^ ERROR calling this function with a null pointer is undefined behavior
33+
ptr::copy_nonoverlapping::<usize>(
34+
//~^ ERROR calling this function with a null pointer is undefined behavior
35+
ptr::NonNull::dangling().as_ptr(),
36+
core::ptr::NonNull::dangling().as_ptr(),
37+
0
38+
);
39+
40+
struct A; // zero sized struct
41+
assert_eq!(std::mem::size_of::<A>(), 0);
42+
43+
let _a: A = ptr::read(core::ptr::NonNull::dangling().as_ptr());
44+
//~^ ERROR calling this function with a null pointer is undefined behavior
45+
let _a: A = ptr::read(core::ptr::NonNull::dangling().as_ptr());
46+
//~^ ERROR calling this function with a null pointer is undefined behavior
47+
48+
let _a: A = ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
49+
//~^ ERROR calling this function with a null pointer is undefined behavior
50+
let _a: A = ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
51+
//~^ ERROR calling this function with a null pointer is undefined behavior
52+
53+
let _a: A = ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
54+
//~^ ERROR calling this function with a null pointer is undefined behavior
55+
let _a: A = ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
56+
//~^ ERROR calling this function with a null pointer is undefined behavior
57+
58+
let _a: A = ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A);
59+
//~^ ERROR calling this function with a null pointer is undefined behavior
60+
61+
ptr::swap::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A);
62+
//~^ ERROR calling this function with a null pointer is undefined behavior
63+
ptr::swap::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr());
64+
//~^ ERROR calling this function with a null pointer is undefined behavior
65+
66+
ptr::swap_nonoverlapping::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0);
67+
//~^ ERROR calling this function with a null pointer is undefined behavior
68+
ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0);
69+
//~^ ERROR calling this function with a null pointer is undefined behavior
70+
71+
ptr::write(core::ptr::NonNull::dangling().as_ptr(), A);
72+
//~^ ERROR calling this function with a null pointer is undefined behavior
73+
74+
ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A);
75+
//~^ ERROR calling this function with a null pointer is undefined behavior
76+
77+
ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A);
78+
//~^ ERROR calling this function with a null pointer is undefined behavior
79+
80+
ptr::write_bytes::<usize>(core::ptr::NonNull::dangling().as_ptr(), 42, 0);
81+
//~^ ERROR calling this function with a null pointer is undefined behavior
82+
}
83+
84+
fn main() {
85+
unsafe { null_ptr() };
86+
}
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// check-fail
2+
// run-rustfix
3+
4+
use std::ptr;
5+
use std::mem;
6+
7+
unsafe fn null_ptr() {
8+
ptr::write(
9+
//~^ ERROR calling this function with a null pointer is undefined behavior
10+
ptr::null_mut() as *mut u32,
11+
mem::transmute::<[u8; 4], _>([0, 0, 0, 255]),
12+
);
13+
14+
let _: &[usize] = std::slice::from_raw_parts(ptr::null(), 0);
15+
//~^ ERROR calling this function with a null pointer is undefined behavior
16+
let _: &[usize] = std::slice::from_raw_parts(ptr::null_mut(), 0);
17+
//~^ ERROR calling this function with a null pointer is undefined behavior
18+
let _: &[usize] = std::slice::from_raw_parts(0 as *mut _, 0);
19+
//~^ ERROR calling this function with a null pointer is undefined behavior
20+
let _: &[usize] = std::slice::from_raw_parts(mem::transmute(0usize), 0);
21+
//~^ ERROR calling this function with a null pointer is undefined behavior
22+
23+
let _: &[usize] = std::slice::from_raw_parts_mut(ptr::null_mut(), 0);
24+
//~^ ERROR calling this function with a null pointer is undefined behavior
25+
26+
ptr::copy::<usize>(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0);
27+
//~^ ERROR calling this function with a null pointer is undefined behavior
28+
ptr::copy::<usize>(ptr::NonNull::dangling().as_ptr(), ptr::null_mut(), 0);
29+
//~^ ERROR calling this function with a null pointer is undefined behavior
30+
31+
ptr::copy_nonoverlapping::<usize>(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0);
32+
//~^ ERROR calling this function with a null pointer is undefined behavior
33+
ptr::copy_nonoverlapping::<usize>(
34+
//~^ ERROR calling this function with a null pointer is undefined behavior
35+
ptr::NonNull::dangling().as_ptr(),
36+
ptr::null_mut(),
37+
0
38+
);
39+
40+
struct A; // zero sized struct
41+
assert_eq!(std::mem::size_of::<A>(), 0);
42+
43+
let _a: A = ptr::read(ptr::null());
44+
//~^ ERROR calling this function with a null pointer is undefined behavior
45+
let _a: A = ptr::read(ptr::null_mut());
46+
//~^ ERROR calling this function with a null pointer is undefined behavior
47+
48+
let _a: A = ptr::read_unaligned(ptr::null());
49+
//~^ ERROR calling this function with a null pointer is undefined behavior
50+
let _a: A = ptr::read_unaligned(ptr::null_mut());
51+
//~^ ERROR calling this function with a null pointer is undefined behavior
52+
53+
let _a: A = ptr::read_volatile(ptr::null());
54+
//~^ ERROR calling this function with a null pointer is undefined behavior
55+
let _a: A = ptr::read_volatile(ptr::null_mut());
56+
//~^ ERROR calling this function with a null pointer is undefined behavior
57+
58+
let _a: A = ptr::replace(ptr::null_mut(), A);
59+
//~^ ERROR calling this function with a null pointer is undefined behavior
60+
61+
ptr::swap::<A>(ptr::null_mut(), &mut A);
62+
//~^ ERROR calling this function with a null pointer is undefined behavior
63+
ptr::swap::<A>(&mut A, ptr::null_mut());
64+
//~^ ERROR calling this function with a null pointer is undefined behavior
65+
66+
ptr::swap_nonoverlapping::<A>(ptr::null_mut(), &mut A, 0);
67+
//~^ ERROR calling this function with a null pointer is undefined behavior
68+
ptr::swap_nonoverlapping::<A>(&mut A, ptr::null_mut(), 0);
69+
//~^ ERROR calling this function with a null pointer is undefined behavior
70+
71+
ptr::write(ptr::null_mut(), A);
72+
//~^ ERROR calling this function with a null pointer is undefined behavior
73+
74+
ptr::write_unaligned(ptr::null_mut(), A);
75+
//~^ ERROR calling this function with a null pointer is undefined behavior
76+
77+
ptr::write_volatile(ptr::null_mut(), A);
78+
//~^ ERROR calling this function with a null pointer is undefined behavior
79+
80+
ptr::write_bytes::<usize>(ptr::null_mut(), 42, 0);
81+
//~^ ERROR calling this function with a null pointer is undefined behavior
82+
}
83+
84+
fn main() {
85+
unsafe { null_ptr() };
86+
}

0 commit comments

Comments
 (0)