Skip to content

Commit 367183b

Browse files
Don't emit null pointer lint for raw ref of null deref
1 parent f2a80a0 commit 367183b

File tree

7 files changed

+28
-41
lines changed

7 files changed

+28
-41
lines changed

compiler/rustc_lint/src/builtin.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -2657,8 +2657,8 @@ declare_lint! {
26572657
///
26582658
/// ### Explanation
26592659
///
2660-
/// Dereferencing a null pointer causes [undefined behavior] even as a place expression,
2661-
/// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`.
2660+
/// Dereferencing a null pointer causes [undefined behavior] if it is accessed
2661+
/// (loaded from or stored to).
26622662
///
26632663
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
26642664
pub DEREF_NULLPTR,
@@ -2673,14 +2673,14 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
26732673
/// test if expression is a null ptr
26742674
fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
26752675
match &expr.kind {
2676-
rustc_hir::ExprKind::Cast(expr, ty) => {
2677-
if let rustc_hir::TyKind::Ptr(_) = ty.kind {
2676+
hir::ExprKind::Cast(expr, ty) => {
2677+
if let hir::TyKind::Ptr(_) = ty.kind {
26782678
return is_zero(expr) || is_null_ptr(cx, expr);
26792679
}
26802680
}
26812681
// check for call to `core::ptr::null` or `core::ptr::null_mut`
2682-
rustc_hir::ExprKind::Call(path, _) => {
2683-
if let rustc_hir::ExprKind::Path(ref qpath) = path.kind {
2682+
hir::ExprKind::Call(path, _) => {
2683+
if let hir::ExprKind::Path(ref qpath) = path.kind {
26842684
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() {
26852685
return matches!(
26862686
cx.tcx.get_diagnostic_name(def_id),
@@ -2697,7 +2697,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
26972697
/// test if expression is the literal `0`
26982698
fn is_zero(expr: &hir::Expr<'_>) -> bool {
26992699
match &expr.kind {
2700-
rustc_hir::ExprKind::Lit(lit) => {
2700+
hir::ExprKind::Lit(lit) => {
27012701
if let LitKind::Int(a, _) = lit.node {
27022702
return a == 0;
27032703
}
@@ -2707,8 +2707,16 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
27072707
false
27082708
}
27092709

2710-
if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind {
2711-
if is_null_ptr(cx, expr_deref) {
2710+
if let hir::ExprKind::Unary(hir::UnOp::Deref, expr_deref) = expr.kind
2711+
&& is_null_ptr(cx, expr_deref)
2712+
{
2713+
if let hir::Node::Expr(hir::Expr {
2714+
kind: hir::ExprKind::AddrOf(hir::BorrowKind::Raw, ..),
2715+
..
2716+
}) = cx.tcx.parent_hir_node(expr.hir_id)
2717+
{
2718+
// `&raw *NULL` is ok.
2719+
} else {
27122720
cx.emit_span_lint(DEREF_NULLPTR, expr.span, BuiltinDerefNullptr {
27132721
label: expr.span,
27142722
});

compiler/rustc_mir_build/src/check_unsafety.rs

-2
Original file line numberDiff line numberDiff line change
@@ -509,8 +509,6 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
509509
}
510510
ExprKind::RawBorrow { arg, .. } => {
511511
if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
512-
// THIR desugars UNSAFE_STATIC into *UNSAFE_STATIC_REF, where
513-
// UNSAFE_STATIC_REF holds the addr of the UNSAFE_STATIC, so: take two steps
514512
&& let ExprKind::Deref { arg } = self.thir[arg].kind
515513
{
516514
// Taking a raw ref to a deref place expr is always safe.

tests/ui/lint/lint-deref-nullptr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ fn f() {
2727
let ub = &*ptr::null_mut::<i32>();
2828
//~^ ERROR dereferencing a null pointer
2929
ptr::addr_of!(*ptr::null::<i32>());
30-
//~^ ERROR dereferencing a null pointer
30+
// ^^ OKAY
3131
ptr::addr_of_mut!(*ptr::null_mut::<i32>());
32-
//~^ ERROR dereferencing a null pointer
32+
// ^^ OKAY
3333
let offset = ptr::addr_of!((*ptr::null::<Struct>()).field);
3434
//~^ ERROR dereferencing a null pointer
3535
}

tests/ui/lint/lint-deref-nullptr.stderr

+1-13
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,11 @@ error: dereferencing a null pointer
4646
LL | let ub = &*ptr::null_mut::<i32>();
4747
| ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
4848

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

67-
error: aborting due to 10 previous errors
55+
error: aborting due to 8 previous errors
6856

tests/ui/static/raw-ref-deref-with-unsafe.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
//@ check-pass
22
use std::ptr;
33

4-
// This code should remain unsafe because of the two unsafe operations here,
5-
// even if in a hypothetical future we deem all &raw (const|mut) *ptr exprs safe.
6-
74
static mut BYTE: u8 = 0;
85
static mut BYTE_PTR: *mut u8 = ptr::addr_of_mut!(BYTE);
6+
7+
// This code should remain unsafe because reading from a static mut is *always* unsafe.
8+
99
// An unsafe static's ident is a place expression in its own right, so despite the above being safe
10-
// (it's fine to create raw refs to places!) the following derefs the ptr before creating its ref
10+
// (it's fine to create raw refs to places!) the following *reads* from the static mut place before
11+
// derefing it explicitly with the `*` below.
1112
static mut DEREF_BYTE_PTR: *mut u8 = unsafe { ptr::addr_of_mut!(*BYTE_PTR) };
1213

1314
fn main() {

tests/ui/static/raw-ref-deref-without-unsafe.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use std::ptr;
22

3-
// This code should remain unsafe because of the two unsafe operations here,
4-
// even if in a hypothetical future we deem all &raw (const|mut) *ptr exprs safe.
5-
63
static mut BYTE: u8 = 0;
74
static mut BYTE_PTR: *mut u8 = ptr::addr_of_mut!(BYTE);
5+
6+
// This code should remain unsafe because reading from a static mut is *always* unsafe.
7+
88
// An unsafe static's ident is a place expression in its own right, so despite the above being safe
99
// (it's fine to create raw refs to places!) the following derefs the ptr before creating its ref!
1010
static mut DEREF_BYTE_PTR: *mut u8 = ptr::addr_of_mut!(*BYTE_PTR);

tests/ui/static/raw-ref-deref-without-unsafe.stderr

-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
2-
--> $DIR/raw-ref-deref-without-unsafe.rs:10:56
3-
|
4-
LL | static mut DEREF_BYTE_PTR: *mut u8 = ptr::addr_of_mut!(*BYTE_PTR);
5-
| ^^^^^^^^^ dereference of raw pointer
6-
|
7-
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
8-
91
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
102
--> $DIR/raw-ref-deref-without-unsafe.rs:10:57
113
|

0 commit comments

Comments
 (0)