mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-03 10:33:34 +00:00
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
This commit is contained in:
commit
7537b20626
@ -1,3 +1,5 @@
|
||||
// ignore-tidy-filelength
|
||||
|
||||
//! Lints in the Rust compiler.
|
||||
//!
|
||||
//! This contains lints which can feasibly be implemented as their own
|
||||
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +206,7 @@ macro_rules! late_lint_mod_passes {
|
||||
UnreachablePub: UnreachablePub,
|
||||
ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
|
||||
InvalidValue: InvalidValue,
|
||||
DerefNullPtr: DerefNullPtr,
|
||||
]
|
||||
);
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
// pretty-expanded FIXME #23616
|
||||
|
||||
#![allow(deref_nullptr)]
|
||||
|
||||
|
||||
use std::env;
|
||||
|
||||
pub fn main() {
|
||||
|
38
src/test/ui/lint/lint-deref-nullptr.rs
Normal file
38
src/test/ui/lint/lint-deref-nullptr.rs
Normal file
@ -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
src/test/ui/lint/lint-deref-nullptr.stderr
Normal file
68
src/test/ui/lint/lint-deref-nullptr.stderr
Normal file
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user