Skip to content

Commit e9f7ce1

Browse files
committed
Auto merge of #9247 - clubby789:raw_slice_pointer_cast, r=Alexendoo
New lint: Raw slice pointer cast Adds a lint to check for a raw slice being created and cast back to a pointer, suggesting `ptr::slice_from_raw_parts`, to identify UB such as thomcc/rust-typed-arena#54. ``` changelog: [`cast_slice_from_raw_parts`]: Add lint to check for `slice::from_raw_parts(.., ..) as *const _` ```
2 parents 58bbb1a + cc9f203 commit e9f7ce1

9 files changed

+190
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3627,6 +3627,7 @@ Released 2018-09-13
36273627
[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
36283628
[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
36293629
[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
3630+
[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
36303631
[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
36313632
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
36323633
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_with_applicability;
3+
use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
4+
use if_chain::if_chain;
5+
use rustc_errors::Applicability;
6+
use rustc_hir::{def_id::DefId, Expr, ExprKind};
7+
use rustc_lint::LateContext;
8+
use rustc_middle::ty::{self, Ty};
9+
use rustc_semver::RustcVersion;
10+
11+
use super::CAST_SLICE_FROM_RAW_PARTS;
12+
13+
enum RawPartsKind {
14+
Immutable,
15+
Mutable,
16+
}
17+
18+
fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
19+
if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS) {
20+
Some(RawPartsKind::Immutable)
21+
} else if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS_MUT) {
22+
Some(RawPartsKind::Mutable)
23+
} else {
24+
None
25+
}
26+
}
27+
28+
pub(super) fn check(
29+
cx: &LateContext<'_>,
30+
expr: &Expr<'_>,
31+
cast_expr: &Expr<'_>,
32+
cast_to: Ty<'_>,
33+
msrv: Option<RustcVersion>,
34+
) {
35+
if_chain! {
36+
if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
37+
if let ty::RawPtr(ptrty) = cast_to.kind();
38+
if let ty::Slice(_) = ptrty.ty.kind();
39+
if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
40+
if let ExprKind::Path(ref qpath) = fun.kind;
41+
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
42+
if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
43+
then {
44+
let func = match rpk {
45+
RawPartsKind::Immutable => "from_raw_parts",
46+
RawPartsKind::Mutable => "from_raw_parts_mut"
47+
};
48+
let span = expr.span;
49+
let mut applicability = Applicability::MachineApplicable;
50+
let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
51+
let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
52+
span_lint_and_sugg(
53+
cx,
54+
CAST_SLICE_FROM_RAW_PARTS,
55+
span,
56+
&format!("casting the result of `{func}` to {cast_to}"),
57+
"replace with",
58+
format!("core::ptr::slice_{func}({ptr}, {len})"),
59+
applicability
60+
);
61+
}
62+
}
63+
}

clippy_lints/src/casts/mod.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod cast_ptr_alignment;
1010
mod cast_ref_to_mut;
1111
mod cast_sign_loss;
1212
mod cast_slice_different_sizes;
13+
mod cast_slice_from_raw_parts;
1314
mod char_lit_as_u8;
1415
mod fn_to_numeric_cast;
1516
mod fn_to_numeric_cast_any;
@@ -568,6 +569,32 @@ declare_clippy_lint! {
568569
pedantic,
569570
"borrowing just to cast to a raw pointer"
570571
}
572+
declare_clippy_lint! {
573+
/// ### What it does
574+
/// Checks for a raw slice being cast to a slice pointer
575+
///
576+
/// ### Why is this bad?
577+
/// This can result in multiple `&mut` references to the same location when only a pointer is
578+
/// required.
579+
/// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
580+
/// the same [safety requirements] to be upheld.
581+
///
582+
/// ### Example
583+
/// ```rust,ignore
584+
/// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
585+
/// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
586+
/// ```
587+
/// Use instead:
588+
/// ```rust,ignore
589+
/// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
590+
/// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
591+
/// ```
592+
/// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
593+
#[clippy::version = "1.64.0"]
594+
pub CAST_SLICE_FROM_RAW_PARTS,
595+
suspicious,
596+
"casting a slice created from a pointer and length to a slice pointer"
597+
}
571598

572599
pub struct Casts {
573600
msrv: Option<RustcVersion>,
@@ -600,6 +627,7 @@ impl_lint_pass!(Casts => [
600627
CAST_ABS_TO_UNSIGNED,
601628
AS_UNDERSCORE,
602629
BORROW_AS_PTR,
630+
CAST_SLICE_FROM_RAW_PARTS
603631
]);
604632

605633
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -624,7 +652,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
624652
if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
625653
return;
626654
}
627-
655+
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
628656
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
629657
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
630658
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);

clippy_lints/src/lib.register_all.rs

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
2525
LintId::of(casts::CAST_ENUM_TRUNCATION),
2626
LintId::of(casts::CAST_REF_TO_MUT),
2727
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
28+
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
2829
LintId::of(casts::CHAR_LIT_AS_U8),
2930
LintId::of(casts::FN_TO_NUMERIC_CAST),
3031
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),

clippy_lints/src/lib.register_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ store.register_lints(&[
7777
casts::CAST_REF_TO_MUT,
7878
casts::CAST_SIGN_LOSS,
7979
casts::CAST_SLICE_DIFFERENT_SIZES,
80+
casts::CAST_SLICE_FROM_RAW_PARTS,
8081
casts::CHAR_LIT_AS_U8,
8182
casts::FN_TO_NUMERIC_CAST,
8283
casts::FN_TO_NUMERIC_CAST_ANY,

clippy_lints/src/lib.register_suspicious.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
1111
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
1212
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
1313
LintId::of(casts::CAST_ENUM_TRUNCATION),
14+
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
1415
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
1516
LintId::of(drop_forget_ref::DROP_NON_DROP),
1617
LintId::of(drop_forget_ref::FORGET_NON_DROP),
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run-rustfix
2+
#![warn(clippy::cast_slice_from_raw_parts)]
3+
4+
#[allow(unused_imports, unused_unsafe)]
5+
fn main() {
6+
let mut vec = vec![0u8; 1];
7+
let ptr: *const u8 = vec.as_ptr();
8+
let mptr = vec.as_mut_ptr();
9+
let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) };
10+
let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) };
11+
let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
12+
{
13+
use core::slice;
14+
let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
15+
use slice as one;
16+
let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
17+
}
18+
{
19+
use std::slice;
20+
let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
21+
use slice as one;
22+
let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
23+
}
24+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run-rustfix
2+
#![warn(clippy::cast_slice_from_raw_parts)]
3+
4+
#[allow(unused_imports, unused_unsafe)]
5+
fn main() {
6+
let mut vec = vec![0u8; 1];
7+
let ptr: *const u8 = vec.as_ptr();
8+
let mptr = vec.as_mut_ptr();
9+
let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] };
10+
let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
11+
let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8];
12+
{
13+
use core::slice;
14+
let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
15+
use slice as one;
16+
let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
17+
}
18+
{
19+
use std::slice;
20+
let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
21+
use slice as one;
22+
let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
error: casting the result of `from_raw_parts` to *const [u8]
2+
--> $DIR/cast_raw_slice_pointer_cast.rs:9:35
3+
|
4+
LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
6+
|
7+
= note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings`
8+
9+
error: casting the result of `from_raw_parts_mut` to *mut [u8]
10+
--> $DIR/cast_raw_slice_pointer_cast.rs:10:35
11+
|
12+
LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)`
14+
15+
error: casting the result of `from_raw_parts` to *const [u8]
16+
--> $DIR/cast_raw_slice_pointer_cast.rs:11:26
17+
|
18+
LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8];
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
20+
21+
error: casting the result of `from_raw_parts` to *const [u8]
22+
--> $DIR/cast_raw_slice_pointer_cast.rs:14:30
23+
|
24+
LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
26+
27+
error: casting the result of `from_raw_parts` to *const [u8]
28+
--> $DIR/cast_raw_slice_pointer_cast.rs:16:30
29+
|
30+
LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
32+
33+
error: casting the result of `from_raw_parts` to *const [u8]
34+
--> $DIR/cast_raw_slice_pointer_cast.rs:20:30
35+
|
36+
LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
38+
39+
error: casting the result of `from_raw_parts` to *const [u8]
40+
--> $DIR/cast_raw_slice_pointer_cast.rs:22:30
41+
|
42+
LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
44+
45+
error: aborting due to 7 previous errors
46+

0 commit comments

Comments
 (0)