1
Fork 0

add lint deref_nullptr

This commit is contained in:
Aliénore Bouttefeux 2021-04-06 22:01:00 +02:00
parent 5c897d430d
commit 389100921a
4 changed files with 94 additions and 0 deletions

View file

@ -2961,3 +2961,92 @@ 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
/// let x: i32 = unsafe {
/// *ptr::null()
/// };
/// ```
/// ```rust,no_run
/// unsafe {
/// *(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 causes undefined behavior");
err.span_label(expr.span, "a null pointer is dereferenced");
err.span_label(
expr.span,
"this code causes undefined behavior when executed",
);
err.emit();
});
}
}
}
}
}

View file

@ -206,6 +206,7 @@ macro_rules! late_lint_mod_passes {
UnreachablePub: UnreachablePub,
ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
InvalidValue: InvalidValue,
DerefNullPtr: DerefNullPtr,
]
);
};

View file

@ -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,

View file

@ -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
}