Skip to content

Commit 7537b20

Browse files
committed
Auto merge of #83948 - ABouttefeux:lint-nullprt-deref, r=RalfJung
add lint deref_nullptr detecting when a null ptr is dereferenced fixes #83856 changelog: add lint that detect code like ```rust unsafe { &*core::ptr::null::<i32>() }; unsafe { addr_of!(std::ptr::null::<i32>()) }; let x: i32 = unsafe {*core::ptr::null()}; let x: i32 = unsafe {*core::ptr::null_mut()}; unsafe {*(0 as *const i32)}; unsafe {*(core::ptr::null() as *const i32)}; ``` ``` warning: Dereferencing a null pointer causes undefined behavior --> src\main.rs:5:26 | 5 | let x: i32 = unsafe {*core::ptr::null()}; | ^^^^^^^^^^^^^^^^^^ | | | a null pointer is dereferenced | this code causes undefined behavior when executed | = note: `#[warn(deref_nullptr)]` on by default ``` Limitation: It does not detect code like ```rust const ZERO: usize = 0; unsafe {*(ZERO as *const i32)}; ``` or code where `0` is not directly a literal
2 parents 07ef259 + 7f0f83a commit 7537b20

File tree

7 files changed

+201
-0
lines changed

7 files changed

+201
-0
lines changed

compiler/rustc_lint/src/builtin.rs

+87
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// ignore-tidy-filelength
2+
13
//! Lints in the Rust compiler.
24
//!
35
//! This contains lints which can feasibly be implemented as their own
@@ -2964,3 +2966,88 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
29642966
}
29652967
}
29662968
}
2969+
2970+
declare_lint! {
2971+
/// The `deref_nullptr` lint detects when an null pointer is dereferenced,
2972+
/// which causes [undefined behavior].
2973+
///
2974+
/// ### Example
2975+
///
2976+
/// ```rust,no_run
2977+
/// # #![allow(unused)]
2978+
/// use std::ptr;
2979+
/// unsafe {
2980+
/// let x = &*ptr::null::<i32>();
2981+
/// let x = ptr::addr_of!(*ptr::null::<i32>());
2982+
/// let x = *(0 as *const i32);
2983+
/// }
2984+
/// ```
2985+
///
2986+
/// {{produces}}
2987+
///
2988+
/// ### Explanation
2989+
///
2990+
/// Dereferencing a null pointer causes [undefined behavior] even as a place expression,
2991+
/// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`.
2992+
///
2993+
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
2994+
pub DEREF_NULLPTR,
2995+
Warn,
2996+
"detects when an null pointer is dereferenced"
2997+
}
2998+
2999+
declare_lint_pass!(DerefNullPtr => [DEREF_NULLPTR]);
3000+
3001+
impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
3002+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
3003+
/// test if expression is a null ptr
3004+
fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
3005+
match &expr.kind {
3006+
rustc_hir::ExprKind::Cast(ref expr, ref ty) => {
3007+
if let rustc_hir::TyKind::Ptr(_) = ty.kind {
3008+
return is_zero(expr) || is_null_ptr(cx, expr);
3009+
}
3010+
}
3011+
// check for call to `core::ptr::null` or `core::ptr::null_mut`
3012+
rustc_hir::ExprKind::Call(ref path, _) => {
3013+
if let rustc_hir::ExprKind::Path(ref qpath) = path.kind {
3014+
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() {
3015+
return cx.tcx.is_diagnostic_item(sym::ptr_null, def_id)
3016+
|| cx.tcx.is_diagnostic_item(sym::ptr_null_mut, def_id);
3017+
}
3018+
}
3019+
}
3020+
_ => {}
3021+
}
3022+
false
3023+
}
3024+
3025+
/// test if experssion is the literal `0`
3026+
fn is_zero(expr: &hir::Expr<'_>) -> bool {
3027+
match &expr.kind {
3028+
rustc_hir::ExprKind::Lit(ref lit) => {
3029+
if let LitKind::Int(a, _) = lit.node {
3030+
return a == 0;
3031+
}
3032+
}
3033+
_ => {}
3034+
}
3035+
false
3036+
}
3037+
3038+
if let rustc_hir::ExprKind::Unary(ref un_op, ref expr_deref) = expr.kind {
3039+
if let rustc_hir::UnOp::Deref = un_op {
3040+
if is_null_ptr(cx, expr_deref) {
3041+
cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| {
3042+
let mut err = lint.build("dereferencing a null pointer");
3043+
err.span_label(
3044+
expr.span,
3045+
"this code causes undefined behavior when executed",
3046+
);
3047+
err.emit();
3048+
});
3049+
}
3050+
}
3051+
}
3052+
}
3053+
}

compiler/rustc_lint/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ macro_rules! late_lint_mod_passes {
206206
UnreachablePub: UnreachablePub,
207207
ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
208208
InvalidValue: InvalidValue,
209+
DerefNullPtr: DerefNullPtr,
209210
]
210211
);
211212
};

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,8 @@ symbols! {
900900
profiler_runtime,
901901
ptr_guaranteed_eq,
902902
ptr_guaranteed_ne,
903+
ptr_null,
904+
ptr_null_mut,
903905
ptr_offset_from,
904906
pub_macro_rules,
905907
pub_restricted,

library/core/src/ptr/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
211211
#[stable(feature = "rust1", since = "1.0.0")]
212212
#[rustc_promotable]
213213
#[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")]
214+
#[rustc_diagnostic_item = "ptr_null"]
214215
pub const fn null<T>() -> *const T {
215216
0 as *const T
216217
}
@@ -229,6 +230,7 @@ pub const fn null<T>() -> *const T {
229230
#[stable(feature = "rust1", since = "1.0.0")]
230231
#[rustc_promotable]
231232
#[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")]
233+
#[rustc_diagnostic_item = "ptr_null_mut"]
232234
pub const fn null_mut<T>() -> *mut T {
233235
0 as *mut T
234236
}

src/test/ui/cleanup-shortcircuit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
// pretty-expanded FIXME #23616
55

6+
#![allow(deref_nullptr)]
7+
8+
69
use std::env;
710

811
pub fn main() {
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// test the deref_nullptr lint
2+
3+
#![deny(deref_nullptr)]
4+
5+
use std::ptr;
6+
7+
struct Struct {
8+
field: u8,
9+
}
10+
11+
fn f() {
12+
unsafe {
13+
let a = 1;
14+
let ub = *(a as *const i32);
15+
let ub = *(0 as *const i32);
16+
//~^ ERROR dereferencing a null pointer
17+
let ub = *ptr::null::<i32>();
18+
//~^ ERROR dereferencing a null pointer
19+
let ub = *ptr::null_mut::<i32>();
20+
//~^ ERROR dereferencing a null pointer
21+
let ub = *(ptr::null::<i16>() as *const i32);
22+
//~^ ERROR dereferencing a null pointer
23+
let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8);
24+
//~^ ERROR dereferencing a null pointer
25+
let ub = &*ptr::null::<i32>();
26+
//~^ ERROR dereferencing a null pointer
27+
let ub = &*ptr::null_mut::<i32>();
28+
//~^ ERROR dereferencing a null pointer
29+
ptr::addr_of!(*ptr::null::<i32>());
30+
//~^ ERROR dereferencing a null pointer
31+
ptr::addr_of_mut!(*ptr::null_mut::<i32>());
32+
//~^ ERROR dereferencing a null pointer
33+
let offset = ptr::addr_of!((*ptr::null::<Struct>()).field);
34+
//~^ ERROR dereferencing a null pointer
35+
}
36+
}
37+
38+
fn main() {}
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
error: dereferencing a null pointer
2+
--> $DIR/lint-deref-nullptr.rs:15:18
3+
|
4+
LL | let ub = *(0 as *const i32);
5+
| ^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/lint-deref-nullptr.rs:3:9
9+
|
10+
LL | #![deny(deref_nullptr)]
11+
| ^^^^^^^^^^^^^
12+
13+
error: dereferencing a null pointer
14+
--> $DIR/lint-deref-nullptr.rs:17:18
15+
|
16+
LL | let ub = *ptr::null::<i32>();
17+
| ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
18+
19+
error: dereferencing a null pointer
20+
--> $DIR/lint-deref-nullptr.rs:19:18
21+
|
22+
LL | let ub = *ptr::null_mut::<i32>();
23+
| ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
24+
25+
error: dereferencing a null pointer
26+
--> $DIR/lint-deref-nullptr.rs:21:18
27+
|
28+
LL | let ub = *(ptr::null::<i16>() as *const i32);
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
30+
31+
error: dereferencing a null pointer
32+
--> $DIR/lint-deref-nullptr.rs:23:18
33+
|
34+
LL | let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8);
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
36+
37+
error: dereferencing a null pointer
38+
--> $DIR/lint-deref-nullptr.rs:25:19
39+
|
40+
LL | let ub = &*ptr::null::<i32>();
41+
| ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
42+
43+
error: dereferencing a null pointer
44+
--> $DIR/lint-deref-nullptr.rs:27:19
45+
|
46+
LL | let ub = &*ptr::null_mut::<i32>();
47+
| ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
48+
49+
error: dereferencing a null pointer
50+
--> $DIR/lint-deref-nullptr.rs:29:23
51+
|
52+
LL | ptr::addr_of!(*ptr::null::<i32>());
53+
| ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
54+
55+
error: dereferencing a null pointer
56+
--> $DIR/lint-deref-nullptr.rs:31:27
57+
|
58+
LL | ptr::addr_of_mut!(*ptr::null_mut::<i32>());
59+
| ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
60+
61+
error: dereferencing a null pointer
62+
--> $DIR/lint-deref-nullptr.rs:33:36
63+
|
64+
LL | let offset = ptr::addr_of!((*ptr::null::<Struct>()).field);
65+
| ^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
66+
67+
error: aborting due to 10 previous errors
68+

0 commit comments

Comments
 (0)