Skip to content

Commit 3891009

Browse files
committed
add lint deref_nullptr
1 parent 5c897d4 commit 3891009

File tree

4 files changed

+94
-0
lines changed

4 files changed

+94
-0
lines changed

compiler/rustc_lint/src/builtin.rs

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

compiler/rustc_lint/src/lib.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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
}

0 commit comments

Comments
 (0)