Skip to content

Commit b91dc03

Browse files
committed
Add as_ptr_cast_mut lint
This lint detects calls to a `&self`-taking `as_ptr` method, where the result is then immediately cast to a `*mut T`. Code like this is probably invalid, as that pointer will not have write permissions, and `*mut T` is usually used to write through.
1 parent 31b1741 commit b91dc03

9 files changed

+144
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3735,6 +3735,7 @@ Released 2018-09-13
37353735
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
37363736
[`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects
37373737
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
3738+
[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut
37383739
[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
37393740
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
37403741
[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_opt;
3+
use rustc_errors::Applicability;
4+
use rustc_hir::{Expr, ExprKind};
5+
use rustc_lint::LateContext;
6+
use rustc_middle::{
7+
mir::Mutability,
8+
ty::{self, Ty, TypeAndMut},
9+
};
10+
11+
use super::AS_PTR_CAST_MUT;
12+
13+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) {
14+
if let ty::RawPtr(ptrty @ TypeAndMut { mutbl: Mutability::Mut, .. }) = cast_to.kind()
15+
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) =
16+
cx.typeck_results().node_type(cast_expr.hir_id).kind()
17+
&& let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind
18+
&& method_name.ident.name == rustc_span::sym::as_ptr
19+
&& let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id)
20+
&& let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did)
21+
&& let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
22+
&& let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
23+
&& let Some(recv) = snippet_opt(cx, receiver.span)
24+
{
25+
// `as_mut_ptr` might not exist
26+
let applicability = Applicability::MaybeIncorrect;
27+
28+
span_lint_and_sugg(
29+
cx,
30+
AS_PTR_CAST_MUT,
31+
expr.span,
32+
&format!("casting the result of `as_ptr` to *{ptrty}"),
33+
"replace with",
34+
format!("{recv}.as_mut_ptr()"),
35+
applicability
36+
);
37+
}
38+
}

clippy_lints/src/casts/mod.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod as_ptr_cast_mut;
12
mod as_underscore;
23
mod borrow_as_ptr;
34
mod cast_abs_to_unsigned;
@@ -596,6 +597,32 @@ declare_clippy_lint! {
596597
"casting a slice created from a pointer and length to a slice pointer"
597598
}
598599

600+
declare_clippy_lint! {
601+
/// ### What it does
602+
/// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer
603+
///
604+
/// ### Why is this bad?
605+
/// Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it
606+
/// unlikely that having it as a mutable pointer is correct.
607+
///
608+
/// ### Example
609+
/// ```rust
610+
/// let string = String::with_capacity(1);
611+
/// let ptr = string.as_ptr() as *mut _;
612+
/// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
613+
/// ```
614+
/// Use instead:
615+
/// ```rust
616+
/// let mut string = String::with_capacity(1);
617+
/// let string = string.as_mut_ptr();
618+
/// unsafe { ptr.write(4) };
619+
/// ```
620+
#[clippy::version = "1.66.0"]
621+
pub AS_PTR_CAST_MUT,
622+
nursery,
623+
"casting the result of the `&self`-taking as_ptr to a mutabe point"
624+
}
625+
599626
pub struct Casts {
600627
msrv: Option<RustcVersion>,
601628
}
@@ -627,7 +654,8 @@ impl_lint_pass!(Casts => [
627654
CAST_ABS_TO_UNSIGNED,
628655
AS_UNDERSCORE,
629656
BORROW_AS_PTR,
630-
CAST_SLICE_FROM_RAW_PARTS
657+
CAST_SLICE_FROM_RAW_PARTS,
658+
AS_PTR_CAST_MUT,
631659
]);
632660

633661
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -653,6 +681,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
653681
return;
654682
}
655683
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
684+
as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
656685
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
657686
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
658687
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ store.register_lints(&[
6666
cargo::NEGATIVE_FEATURE_NAMES,
6767
cargo::REDUNDANT_FEATURE_NAMES,
6868
cargo::WILDCARD_DEPENDENCIES,
69+
casts::AS_PTR_CAST_MUT,
6970
casts::AS_UNDERSCORE,
7071
casts::BORROW_AS_PTR,
7172
casts::CAST_ABS_TO_UNSIGNED,

clippy_lints/src/lib.register_nursery.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
66
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
7+
LintId::of(casts::AS_PTR_CAST_MUT),
78
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
89
LintId::of(copies::BRANCHES_SHARING_CODE),
910
LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),

src/docs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ docs! {
2828
"approx_constant",
2929
"arithmetic_side_effects",
3030
"as_conversions",
31+
"as_ptr_cast_mut",
3132
"as_underscore",
3233
"assertions_on_constants",
3334
"assertions_on_result_states",

src/docs/as_ptr_cast_mut.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
### What it does
2+
Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer
3+
4+
### Why is this bad?
5+
Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it
6+
unlikely that having it as a mutable pointer is correct.
7+
8+
### Example
9+
```
10+
let string = String::with_capacity(1);
11+
let ptr = string.as_ptr() as *mut _;
12+
unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
13+
```
14+
Use instead:
15+
```
16+
let mut string = String::with_capacity(1);
17+
let string = string.as_mut_ptr();
18+
unsafe { ptr.write(4) };
19+
```

tests/ui/as_ptr_cast_mut.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![allow(unused)]
2+
#![warn(clippy::as_ptr_cast_mut)]
3+
#![allow(clippy::wrong_self_convention)]
4+
5+
struct MutPtrWrapper(Vec<u8>);
6+
impl MutPtrWrapper {
7+
fn as_ptr(&mut self) -> *const u8 {
8+
self.0.as_mut_ptr() as *const u8
9+
}
10+
}
11+
12+
struct Covariant<T>(*const T);
13+
impl<T> Covariant<T> {
14+
fn as_ptr(self) -> *const T {
15+
self.0
16+
}
17+
}
18+
19+
fn main() {
20+
let mut string = String::new();
21+
let _ = string.as_ptr() as *mut u8;
22+
let _: *mut i8 = string.as_ptr() as *mut _;
23+
let _ = string.as_ptr() as *const i8;
24+
let _ = string.as_mut_ptr();
25+
let _ = string.as_mut_ptr() as *mut u8;
26+
let _ = string.as_mut_ptr() as *const u8;
27+
28+
let nn = std::ptr::NonNull::new(4 as *mut u8).unwrap();
29+
let _ = nn.as_ptr() as *mut u8;
30+
31+
let mut wrap = MutPtrWrapper(Vec::new());
32+
let _ = wrap.as_ptr() as *mut u8;
33+
34+
let mut local = 4;
35+
let ref_with_write_perm = Covariant(std::ptr::addr_of_mut!(local) as *const _);
36+
let _ = ref_with_write_perm.as_ptr() as *mut u8;
37+
}

tests/ui/as_ptr_cast_mut.stderr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: casting the result of `as_ptr` to *mut u8
2+
--> $DIR/as_ptr_cast_mut.rs:21:13
3+
|
4+
LL | let _ = string.as_ptr() as *mut u8;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()`
6+
|
7+
= note: `-D clippy::as-ptr-cast-mut` implied by `-D warnings`
8+
9+
error: casting the result of `as_ptr` to *mut i8
10+
--> $DIR/as_ptr_cast_mut.rs:22:22
11+
|
12+
LL | let _: *mut i8 = string.as_ptr() as *mut _;
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()`
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)