Skip to content

Commit

Permalink
Auto merge of rust-lang#83948 - ABouttefeux:lint-nullprt-deref, r=Ral…
Browse files Browse the repository at this point in the history
…fJung

add lint deref_nullptr detecting when a null ptr is dereferenced

fixes rust-lang#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
  • Loading branch information
bors committed Apr 14, 2021
2 parents 07ef259 + 7f0f83a commit 7537b20
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 0 deletions.
87 changes: 87 additions & 0 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ignore-tidy-filelength

//! Lints in the Rust compiler.
//!
//! This contains lints which can feasibly be implemented as their own
Expand Down Expand Up @@ -2964,3 +2966,88 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
}
}
}

declare_lint! {
/// The `deref_nullptr` lint detects when an null pointer is dereferenced,
/// which causes [undefined behavior].
///
/// ### Example
///
/// ```rust,no_run
/// # #![allow(unused)]
/// use std::ptr;
/// unsafe {
/// let x = &*ptr::null::<i32>();
/// let x = ptr::addr_of!(*ptr::null::<i32>());
/// let x = *(0 as *const i32);
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Dereferencing a null pointer causes [undefined behavior] even as a place expression,
/// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`.
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
pub DEREF_NULLPTR,
Warn,
"detects when an null pointer is dereferenced"
}

declare_lint_pass!(DerefNullPtr => [DEREF_NULLPTR]);

impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
/// test if expression is a null ptr
fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
match &expr.kind {
rustc_hir::ExprKind::Cast(ref expr, ref ty) => {
if let rustc_hir::TyKind::Ptr(_) = ty.kind {
return is_zero(expr) || is_null_ptr(cx, expr);
}
}
// check for call to `core::ptr::null` or `core::ptr::null_mut`
rustc_hir::ExprKind::Call(ref path, _) => {
if let rustc_hir::ExprKind::Path(ref qpath) = path.kind {
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() {
return cx.tcx.is_diagnostic_item(sym::ptr_null, def_id)
|| cx.tcx.is_diagnostic_item(sym::ptr_null_mut, def_id);
}
}
}
_ => {}
}
false
}

/// test if experssion is the literal `0`
fn is_zero(expr: &hir::Expr<'_>) -> bool {
match &expr.kind {
rustc_hir::ExprKind::Lit(ref lit) => {
if let LitKind::Int(a, _) = lit.node {
return a == 0;
}
}
_ => {}
}
false
}

if let rustc_hir::ExprKind::Unary(ref un_op, ref expr_deref) = expr.kind {
if let rustc_hir::UnOp::Deref = un_op {
if is_null_ptr(cx, expr_deref) {
cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| {
let mut err = lint.build("dereferencing a null pointer");
err.span_label(
expr.span,
"this code causes undefined behavior when executed",
);
err.emit();
});
}
}
}
}
}
1 change: 1 addition & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ macro_rules! late_lint_mod_passes {
UnreachablePub: UnreachablePub,
ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
InvalidValue: InvalidValue,
DerefNullPtr: DerefNullPtr,
]
);
};
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,8 @@ symbols! {
profiler_runtime,
ptr_guaranteed_eq,
ptr_guaranteed_ne,
ptr_null,
ptr_null_mut,
ptr_offset_from,
pub_macro_rules,
pub_restricted,
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_promotable]
#[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")]
#[rustc_diagnostic_item = "ptr_null"]
pub const fn null<T>() -> *const T {
0 as *const T
}
Expand All @@ -229,6 +230,7 @@ pub const fn null<T>() -> *const T {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_promotable]
#[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")]
#[rustc_diagnostic_item = "ptr_null_mut"]
pub const fn null_mut<T>() -> *mut T {
0 as *mut T
}
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/cleanup-shortcircuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

// pretty-expanded FIXME #23616

#![allow(deref_nullptr)]


use std::env;

pub fn main() {
Expand Down
38 changes: 38 additions & 0 deletions src/test/ui/lint/lint-deref-nullptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// test the deref_nullptr lint

#![deny(deref_nullptr)]

use std::ptr;

struct Struct {
field: u8,
}

fn f() {
unsafe {
let a = 1;
let ub = *(a as *const i32);
let ub = *(0 as *const i32);
//~^ ERROR dereferencing a null pointer
let ub = *ptr::null::<i32>();
//~^ ERROR dereferencing a null pointer
let ub = *ptr::null_mut::<i32>();
//~^ ERROR dereferencing a null pointer
let ub = *(ptr::null::<i16>() as *const i32);
//~^ ERROR dereferencing a null pointer
let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8);
//~^ ERROR dereferencing a null pointer
let ub = &*ptr::null::<i32>();
//~^ ERROR dereferencing a null pointer
let ub = &*ptr::null_mut::<i32>();
//~^ ERROR dereferencing a null pointer
ptr::addr_of!(*ptr::null::<i32>());
//~^ ERROR dereferencing a null pointer
ptr::addr_of_mut!(*ptr::null_mut::<i32>());
//~^ ERROR dereferencing a null pointer
let offset = ptr::addr_of!((*ptr::null::<Struct>()).field);
//~^ ERROR dereferencing a null pointer
}
}

fn main() {}
68 changes: 68 additions & 0 deletions src/test/ui/lint/lint-deref-nullptr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:15:18
|
LL | let ub = *(0 as *const i32);
| ^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
|
note: the lint level is defined here
--> $DIR/lint-deref-nullptr.rs:3:9
|
LL | #![deny(deref_nullptr)]
| ^^^^^^^^^^^^^

error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:17:18
|
LL | let ub = *ptr::null::<i32>();
| ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed

error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:19:18
|
LL | let ub = *ptr::null_mut::<i32>();
| ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed

error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:21:18
|
LL | let ub = *(ptr::null::<i16>() as *const i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed

error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:23:18
|
LL | let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed

error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:25:19
|
LL | let ub = &*ptr::null::<i32>();
| ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed

error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:27:19
|
LL | let ub = &*ptr::null_mut::<i32>();
| ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed

error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:29:23
|
LL | ptr::addr_of!(*ptr::null::<i32>());
| ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed

error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:31:27
|
LL | ptr::addr_of_mut!(*ptr::null_mut::<i32>());
| ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed

error: dereferencing a null pointer
--> $DIR/lint-deref-nullptr.rs:33:36
|
LL | let offset = ptr::addr_of!((*ptr::null::<Struct>()).field);
| ^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed

error: aborting due to 10 previous errors

0 comments on commit 7537b20

Please sign in to comment.