Skip to content

Commit b46b311

Browse files
authored
add manual_dangling_ptr lint (#14107)
close #2177 changelog: [`manual_dangling_ptr`]: new lint
2 parents eee7c58 + 7b1f9d8 commit b46b311

14 files changed

+283
-11
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5791,6 +5791,7 @@ Released 2018-09-13
57915791
[`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
57925792
[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
57935793
[`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains
5794+
[`manual_dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_dangling_ptr
57945795
[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil
57955796
[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
57965797
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::SpanRangeExt;
3+
use clippy_utils::ty::is_normalizable;
4+
use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core};
5+
use rustc_ast::LitKind;
6+
use rustc_errors::Applicability;
7+
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
8+
use rustc_lint::LateContext;
9+
use rustc_span::source_map::Spanned;
10+
11+
use super::MANUAL_DANGLING_PTR;
12+
13+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
14+
if let TyKind::Ptr(ref ptr_ty) = to.kind {
15+
let init_expr = expr_or_init(cx, from);
16+
if is_expr_const_aligned(cx, init_expr, ptr_ty.ty)
17+
&& let Some(std_or_core) = std_or_core(cx)
18+
{
19+
let sugg_fn = match ptr_ty.mutbl {
20+
Mutability::Not => "ptr::dangling",
21+
Mutability::Mut => "ptr::dangling_mut",
22+
};
23+
24+
let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind {
25+
format!("{std_or_core}::{sugg_fn}()")
26+
} else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) {
27+
format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
28+
} else {
29+
return;
30+
};
31+
32+
span_lint_and_sugg(
33+
cx,
34+
MANUAL_DANGLING_PTR,
35+
expr.span,
36+
"manual creation of a dangling pointer",
37+
"use",
38+
sugg,
39+
Applicability::MachineApplicable,
40+
);
41+
}
42+
}
43+
}
44+
45+
// Checks if the given expression is a call to `align_of` whose generic argument matches the target
46+
// type, or a positive constant literal that matches the target type's alignment.
47+
fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool {
48+
match expr.kind {
49+
ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to),
50+
ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to),
51+
_ => false,
52+
}
53+
}
54+
55+
fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
56+
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
57+
&& let Some(fun_id) = path_def_id(cx, fun)
58+
&& match_def_path(cx, fun_id, &paths::ALIGN_OF)
59+
&& let Some(args) = path.segments.last().and_then(|seg| seg.args)
60+
&& let [GenericArg::Type(generic_ty)] = args.args
61+
{
62+
let typeck = cx.typeck_results();
63+
return typeck.node_type(generic_ty.hir_id) == typeck.node_type(to.hir_id);
64+
}
65+
false
66+
}
67+
68+
fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned<LitKind>, to: &Ty<'_>) -> bool {
69+
let LitKind::Int(val, _) = lit.node else { return false };
70+
if val == 0 {
71+
return false;
72+
}
73+
let to_mid_ty = cx.typeck_results().node_type(to.hir_id);
74+
is_normalizable(cx, cx.param_env, to_mid_ty)
75+
&& cx
76+
.tcx
77+
.layout_of(cx.typing_env().as_query_input(to_mid_ty))
78+
.is_ok_and(|layout| {
79+
let align = u128::from(layout.align.abi.bytes());
80+
u128::from(val) <= align
81+
})
82+
}

clippy_lints/src/casts/mod.rs

+32
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod char_lit_as_u8;
1717
mod fn_to_numeric_cast;
1818
mod fn_to_numeric_cast_any;
1919
mod fn_to_numeric_cast_with_truncation;
20+
mod manual_dangling_ptr;
2021
mod ptr_as_ptr;
2122
mod ptr_cast_constness;
2223
mod ref_as_ptr;
@@ -759,6 +760,32 @@ declare_clippy_lint! {
759760
"detects `as *mut _` and `as *const _` conversion"
760761
}
761762

763+
declare_clippy_lint! {
764+
/// ### What it does
765+
/// Checks for casts of small constant literals or `mem::align_of` results to raw pointers.
766+
///
767+
/// ### Why is this bad?
768+
/// This creates a dangling pointer and is better expressed as
769+
/// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
770+
///
771+
/// ### Example
772+
/// ```no_run
773+
/// let ptr = 4 as *const u32;
774+
/// let aligned = std::mem::align_of::<u32>() as *const u32;
775+
/// let mut_ptr: *mut i64 = 8 as *mut _;
776+
/// ```
777+
/// Use instead:
778+
/// ```no_run
779+
/// let ptr = std::ptr::dangling::<u32>();
780+
/// let aligned = std::ptr::dangling::<u32>();
781+
/// let mut_ptr: *mut i64 = std::ptr::dangling_mut();
782+
/// ```
783+
#[clippy::version = "1.87.0"]
784+
pub MANUAL_DANGLING_PTR,
785+
style,
786+
"casting small constant literals to pointers to create dangling pointers"
787+
}
788+
762789
pub struct Casts {
763790
msrv: Msrv,
764791
}
@@ -795,6 +822,7 @@ impl_lint_pass!(Casts => [
795822
ZERO_PTR,
796823
REF_AS_PTR,
797824
AS_POINTER_UNDERSCORE,
825+
MANUAL_DANGLING_PTR,
798826
]);
799827

800828
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -823,6 +851,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
823851
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
824852
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
825853

854+
if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) {
855+
manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
856+
}
857+
826858
if cast_to.is_numeric() {
827859
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
828860
if cast_from.is_numeric() {

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
9797
crate::casts::FN_TO_NUMERIC_CAST_INFO,
9898
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
9999
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
100+
crate::casts::MANUAL_DANGLING_PTR_INFO,
100101
crate::casts::PTR_AS_PTR_INFO,
101102
crate::casts::PTR_CAST_CONSTNESS_INFO,
102103
crate::casts::REF_AS_PTR_INFO,

clippy_utils/src/msrvs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ macro_rules! msrv_aliases {
2424
msrv_aliases! {
2525
1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT }
2626
1,85,0 { UINT_FLOAT_MIDPOINT }
27-
1,84,0 { CONST_OPTION_AS_SLICE }
27+
1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
2828
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
2929
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
3030
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION }

clippy_utils/src/paths.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]
3030
pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
3131
pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"];
3232
pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"];
33+
pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"];
3334

3435
// Paths in clippy itself
3536
pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"];

tests/ui/manual_dangling_ptr.fixed

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#![warn(clippy::manual_dangling_ptr)]
2+
use std::mem;
3+
4+
pub fn foo(_const: *const f32, _mut: *mut i32) {}
5+
6+
fn main() {
7+
let _: *const u8 = std::ptr::dangling();
8+
//~^ manual_dangling_ptr
9+
let _ = std::ptr::dangling::<u32>();
10+
//~^ manual_dangling_ptr
11+
let _ = std::ptr::dangling_mut::<f32>();
12+
//~^ manual_dangling_ptr
13+
14+
let _ = std::ptr::dangling::<u8>();
15+
//~^ manual_dangling_ptr
16+
let _ = std::ptr::dangling::<u32>();
17+
//~^ manual_dangling_ptr
18+
let _ = std::ptr::dangling::<usize>();
19+
//~^ manual_dangling_ptr
20+
21+
foo(std::ptr::dangling(), std::ptr::dangling_mut());
22+
//~^ manual_dangling_ptr
23+
//~| manual_dangling_ptr
24+
}
25+
26+
fn should_not_lint() {
27+
let _ = 0x10 as *mut i32;
28+
let _ = mem::align_of::<u32>() as *const u8;
29+
30+
foo(0 as _, 0 as _);
31+
}
32+
33+
#[clippy::msrv = "1.83"]
34+
fn _msrv_1_83() {
35+
// `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this
36+
foo(4 as *const _, 4 as *mut _);
37+
}
38+
39+
#[clippy::msrv = "1.84"]
40+
fn _msrv_1_84() {
41+
foo(std::ptr::dangling(), std::ptr::dangling_mut());
42+
//~^ manual_dangling_ptr
43+
//~| manual_dangling_ptr
44+
}

tests/ui/manual_dangling_ptr.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#![warn(clippy::manual_dangling_ptr)]
2+
use std::mem;
3+
4+
pub fn foo(_const: *const f32, _mut: *mut i32) {}
5+
6+
fn main() {
7+
let _: *const u8 = 1 as *const _;
8+
//~^ manual_dangling_ptr
9+
let _ = 2 as *const u32;
10+
//~^ manual_dangling_ptr
11+
let _ = 4 as *mut f32;
12+
//~^ manual_dangling_ptr
13+
14+
let _ = mem::align_of::<u8>() as *const u8;
15+
//~^ manual_dangling_ptr
16+
let _ = mem::align_of::<u32>() as *const u32;
17+
//~^ manual_dangling_ptr
18+
let _ = mem::align_of::<usize>() as *const usize;
19+
//~^ manual_dangling_ptr
20+
21+
foo(4 as *const _, 4 as *mut _);
22+
//~^ manual_dangling_ptr
23+
//~| manual_dangling_ptr
24+
}
25+
26+
fn should_not_lint() {
27+
let _ = 0x10 as *mut i32;
28+
let _ = mem::align_of::<u32>() as *const u8;
29+
30+
foo(0 as _, 0 as _);
31+
}
32+
33+
#[clippy::msrv = "1.83"]
34+
fn _msrv_1_83() {
35+
// `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this
36+
foo(4 as *const _, 4 as *mut _);
37+
}
38+
39+
#[clippy::msrv = "1.84"]
40+
fn _msrv_1_84() {
41+
foo(4 as *const _, 4 as *mut _);
42+
//~^ manual_dangling_ptr
43+
//~| manual_dangling_ptr
44+
}

tests/ui/manual_dangling_ptr.stderr

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
error: manual creation of a dangling pointer
2+
--> tests/ui/manual_dangling_ptr.rs:7:24
3+
|
4+
LL | let _: *const u8 = 1 as *const _;
5+
| ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()`
6+
|
7+
= note: `-D clippy::manual-dangling-ptr` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::manual_dangling_ptr)]`
9+
10+
error: manual creation of a dangling pointer
11+
--> tests/ui/manual_dangling_ptr.rs:9:13
12+
|
13+
LL | let _ = 2 as *const u32;
14+
| ^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::<u32>()`
15+
16+
error: manual creation of a dangling pointer
17+
--> tests/ui/manual_dangling_ptr.rs:11:13
18+
|
19+
LL | let _ = 4 as *mut f32;
20+
| ^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::<f32>()`
21+
22+
error: manual creation of a dangling pointer
23+
--> tests/ui/manual_dangling_ptr.rs:14:13
24+
|
25+
LL | let _ = mem::align_of::<u8>() as *const u8;
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::<u8>()`
27+
28+
error: manual creation of a dangling pointer
29+
--> tests/ui/manual_dangling_ptr.rs:16:13
30+
|
31+
LL | let _ = mem::align_of::<u32>() as *const u32;
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::<u32>()`
33+
34+
error: manual creation of a dangling pointer
35+
--> tests/ui/manual_dangling_ptr.rs:18:13
36+
|
37+
LL | let _ = mem::align_of::<usize>() as *const usize;
38+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::<usize>()`
39+
40+
error: manual creation of a dangling pointer
41+
--> tests/ui/manual_dangling_ptr.rs:21:9
42+
|
43+
LL | foo(4 as *const _, 4 as *mut _);
44+
| ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()`
45+
46+
error: manual creation of a dangling pointer
47+
--> tests/ui/manual_dangling_ptr.rs:21:24
48+
|
49+
LL | foo(4 as *const _, 4 as *mut _);
50+
| ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()`
51+
52+
error: manual creation of a dangling pointer
53+
--> tests/ui/manual_dangling_ptr.rs:41:9
54+
|
55+
LL | foo(4 as *const _, 4 as *mut _);
56+
| ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()`
57+
58+
error: manual creation of a dangling pointer
59+
--> tests/ui/manual_dangling_ptr.rs:41:24
60+
|
61+
LL | foo(4 as *const _, 4 as *mut _);
62+
| ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()`
63+
64+
error: aborting due to 10 previous errors
65+

tests/ui/transmute.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ fn useless() {
5959
let _: *const usize = std::mem::transmute(5_isize);
6060
//~^ useless_transmute
6161

62-
let _ = 5_isize as *const usize;
62+
let _ = std::ptr::dangling::<usize>();
6363

6464
let _: *const usize = std::mem::transmute(1 + 1usize);
6565
//~^ useless_transmute

tests/ui/transmute_null_to_fn.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![allow(dead_code)]
22
#![warn(clippy::transmute_null_to_fn)]
33
#![allow(clippy::zero_ptr, clippy::missing_transmute_annotations)]
4+
#![allow(clippy::manual_dangling_ptr)]
45

56
// Easy to lint because these only span one line.
67
fn one_liners() {

0 commit comments

Comments
 (0)