Skip to content

Commit 87d80f2

Browse files
committed
Add new lint manual_dangling_ptr
1 parent 0fb004d commit 87d80f2

File tree

9 files changed

+243
-1
lines changed

9 files changed

+243
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5787,6 +5787,7 @@ Released 2018-09-13
57875787
[`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
57885788
[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
57895789
[`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains
5790+
[`manual_dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_dangling_ptr
57905791
[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil
57915792
[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
57925793
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
Lines changed: 82 additions & 0 deletions
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

Lines changed: 32 additions & 0 deletions
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
}
@@ -797,6 +824,7 @@ impl_lint_pass!(Casts => [
797824
ZERO_PTR,
798825
REF_AS_PTR,
799826
AS_POINTER_UNDERSCORE,
827+
MANUAL_DANGLING_PTR,
800828
]);
801829

802830
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -825,6 +853,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
825853
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
826854
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
827855

856+
if self.msrv.meets(msrvs::MANUAL_DANGLING_PTR) {
857+
manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
858+
}
859+
828860
if cast_to.is_numeric() {
829861
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
830862
if cast_from.is_numeric() {

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
9696
crate::casts::FN_TO_NUMERIC_CAST_INFO,
9797
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
9898
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
99+
crate::casts::MANUAL_DANGLING_PTR_INFO,
99100
crate::casts::PTR_AS_PTR_INFO,
100101
crate::casts::PTR_CAST_CONSTNESS_INFO,
101102
crate::casts::REF_AS_PTR_INFO,

clippy_utils/src/msrvs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ macro_rules! msrv_aliases {
1919

2020
// names may refer to stabilized feature flags or library items
2121
msrv_aliases! {
22-
1,84,0 { CONST_OPTION_AS_SLICE }
22+
1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
2323
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
2424
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
2525
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION }

clippy_utils/src/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]
3131
pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
3232
pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"];
3333
pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"];
34+
pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"];
3435

3536
// Paths in clippy itself
3637
pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];

tests/ui/manual_dangling_ptr.fixed

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![warn(clippy::manual_dangling_ptr)]
2+
use std::mem;
3+
4+
pub fn foo(_const: *const f32, _mut: *mut i64) {}
5+
6+
fn main() {
7+
// should lint
8+
let _ = std::ptr::dangling::<u32>();
9+
let _ = std::ptr::dangling_mut::<f64>();
10+
let _: *const u8 = std::ptr::dangling();
11+
12+
let _ = std::ptr::dangling::<u32>();
13+
let _ = std::ptr::dangling_mut::<u64>();
14+
15+
foo(std::ptr::dangling(), std::ptr::dangling_mut());
16+
17+
// should not lint
18+
let _ = 0x10 as *mut i64;
19+
let _ = mem::align_of::<u32>() as *const u64;
20+
21+
foo(0 as _, 0 as _);
22+
}
23+
24+
#[clippy::msrv = "1.83"]
25+
fn _msrv_1_83() {
26+
// `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this
27+
foo(8 as *const _, 8 as *mut _);
28+
}
29+
30+
#[clippy::msrv = "1.84"]
31+
fn _msrv_1_84() {
32+
foo(std::ptr::dangling(), std::ptr::dangling_mut());
33+
}

tests/ui/manual_dangling_ptr.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![warn(clippy::manual_dangling_ptr)]
2+
use std::mem;
3+
4+
pub fn foo(_const: *const f32, _mut: *mut i64) {}
5+
6+
fn main() {
7+
// should lint
8+
let _ = 4 as *const u32;
9+
let _ = 8 as *mut f64;
10+
let _: *const u8 = 1 as *const _;
11+
12+
let _ = mem::align_of::<u32>() as *const u32;
13+
let _ = mem::align_of::<u64>() as *mut u64;
14+
15+
foo(4 as *const _, 8 as *mut _);
16+
17+
// should not lint
18+
let _ = 0x10 as *mut i64;
19+
let _ = mem::align_of::<u32>() as *const u64;
20+
21+
foo(0 as _, 0 as _);
22+
}
23+
24+
#[clippy::msrv = "1.83"]
25+
fn _msrv_1_83() {
26+
// `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this
27+
foo(8 as *const _, 8 as *mut _);
28+
}
29+
30+
#[clippy::msrv = "1.84"]
31+
fn _msrv_1_84() {
32+
foo(4 as *const _, 8 as *mut _);
33+
}

tests/ui/manual_dangling_ptr.stderr

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error: manual creation of a dangling pointer
2+
--> tests/ui/manual_dangling_ptr.rs:8:13
3+
|
4+
LL | let _ = 4 as *const u32;
5+
| ^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::<u32>()`
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 _ = 8 as *mut f64;
14+
| ^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::<f64>()`
15+
16+
error: manual creation of a dangling pointer
17+
--> tests/ui/manual_dangling_ptr.rs:10:24
18+
|
19+
LL | let _: *const u8 = 1 as *const _;
20+
| ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()`
21+
22+
error: manual creation of a dangling pointer
23+
--> tests/ui/manual_dangling_ptr.rs:12:13
24+
|
25+
LL | let _ = mem::align_of::<u32>() as *const u32;
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::<u32>()`
27+
28+
error: manual creation of a dangling pointer
29+
--> tests/ui/manual_dangling_ptr.rs:13:13
30+
|
31+
LL | let _ = mem::align_of::<u64>() as *mut u64;
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::<u64>()`
33+
34+
error: manual creation of a dangling pointer
35+
--> tests/ui/manual_dangling_ptr.rs:15:9
36+
|
37+
LL | foo(4 as *const _, 8 as *mut _);
38+
| ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()`
39+
40+
error: manual creation of a dangling pointer
41+
--> tests/ui/manual_dangling_ptr.rs:15:24
42+
|
43+
LL | foo(4 as *const _, 8 as *mut _);
44+
| ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()`
45+
46+
error: manual creation of a dangling pointer
47+
--> tests/ui/manual_dangling_ptr.rs:32:9
48+
|
49+
LL | foo(4 as *const _, 8 as *mut _);
50+
| ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()`
51+
52+
error: manual creation of a dangling pointer
53+
--> tests/ui/manual_dangling_ptr.rs:32:24
54+
|
55+
LL | foo(4 as *const _, 8 as *mut _);
56+
| ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()`
57+
58+
error: aborting due to 9 previous errors
59+

0 commit comments

Comments
 (0)