Rollup merge of #115100 - Urgau:invalid_ref_casting-ptr-writes, r=est31
Add support for `ptr::write`s for the `invalid_reference_casting` lint This PR adds support for `ptr::write` and others for the `invalid_reference_casting` lint. Detecting instances where instead of using the deref (`*`) operator to assign someone uses `ptr::write`, `ptr::write_unaligned` or `ptr::write_volatile`. ```rust let data_len = 5u64; std::ptr::write( std::mem::transmute::<*const u64, *mut u64>(&data_len), new_data_len, ); ``` r? ``@est31``
This commit is contained in:
commit
7257e9c2de
@ -56,20 +56,7 @@ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tc
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
// &mut <expr>
|
||||
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
|
||||
expr
|
||||
// <expr> = ...
|
||||
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
|
||||
expr
|
||||
// <expr> += ...
|
||||
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
|
||||
expr
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else {
|
||||
let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -86,15 +73,58 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
cx.emit_spanned_lint(
|
||||
INVALID_REFERENCE_CASTING,
|
||||
expr.span,
|
||||
if matches!(expr.kind, ExprKind::AddrOf(..)) {
|
||||
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
|
||||
} else {
|
||||
if is_assignment {
|
||||
InvalidReferenceCastingDiag::AssignToRef { orig_cast }
|
||||
} else {
|
||||
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_operation_we_care_about<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'tcx>,
|
||||
) -> Option<(bool, &'tcx Expr<'tcx>)> {
|
||||
fn deref_assign_or_addr_of<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(bool, &'tcx Expr<'tcx>)> {
|
||||
// &mut <expr>
|
||||
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
|
||||
expr
|
||||
// <expr> = ...
|
||||
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
|
||||
expr
|
||||
// <expr> += ...
|
||||
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
|
||||
expr
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if let ExprKind::Unary(UnOp::Deref, e) = &inner.kind {
|
||||
Some((!matches!(expr.kind, ExprKind::AddrOf(..)), e))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn ptr_write<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'tcx>,
|
||||
) -> Option<(bool, &'tcx Expr<'tcx>)> {
|
||||
if let ExprKind::Call(path, [arg_ptr, _arg_val]) = e.kind
|
||||
&& let ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_write | sym::ptr_write_volatile | sym::ptr_write_unaligned))
|
||||
{
|
||||
Some((true, arg_ptr))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
|
||||
}
|
||||
|
||||
fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||
let e = e.peel_blocks();
|
||||
|
||||
|
@ -1179,6 +1179,9 @@
|
||||
ptr_offset_from,
|
||||
ptr_offset_from_unsigned,
|
||||
ptr_unique,
|
||||
ptr_write,
|
||||
ptr_write_unaligned,
|
||||
ptr_write_volatile,
|
||||
pub_macro_rules,
|
||||
pub_restricted,
|
||||
public,
|
||||
|
@ -1357,6 +1357,7 @@ macro_rules! attempt_swap_as_chunks {
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
|
||||
#[rustc_diagnostic_item = "ptr_write"]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub const unsafe fn write<T>(dst: *mut T, src: T) {
|
||||
// Semantically, it would be fine for this to be implemented as a
|
||||
@ -1459,6 +1460,7 @@ macro_rules! attempt_swap_as_chunks {
|
||||
#[inline]
|
||||
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
|
||||
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
|
||||
#[rustc_diagnostic_item = "ptr_write_unaligned"]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
|
||||
// SAFETY: the caller must guarantee that `dst` is valid for writes.
|
||||
@ -1607,6 +1609,7 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "volatile", since = "1.9.0")]
|
||||
#[rustc_diagnostic_item = "ptr_write_volatile"]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
||||
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
|
||||
|
@ -71,6 +71,11 @@ unsafe fn assign_to_ref() {
|
||||
//~^ ERROR assigning to `&T` is undefined behavior
|
||||
*std::mem::transmute::<_, *mut i32>(num) += 1;
|
||||
//~^ ERROR assigning to `&T` is undefined behavior
|
||||
std::ptr::write(
|
||||
//~^ ERROR assigning to `&T` is undefined behavior
|
||||
std::mem::transmute::<*const i32, *mut i32>(num),
|
||||
-1i32,
|
||||
);
|
||||
|
||||
let value = num as *const i32 as *mut i32;
|
||||
*value = 1;
|
||||
@ -79,6 +84,12 @@ unsafe fn assign_to_ref() {
|
||||
//~^ ERROR assigning to `&T` is undefined behavior
|
||||
*(num as *const _ as usize as *mut i32) = 2;
|
||||
//~^ ERROR assigning to `&T` is undefined behavior
|
||||
std::ptr::write(value, 2);
|
||||
//~^ ERROR assigning to `&T` is undefined behavior
|
||||
std::ptr::write_unaligned(value, 2);
|
||||
//~^ ERROR assigning to `&T` is undefined behavior
|
||||
std::ptr::write_volatile(value, 2);
|
||||
//~^ ERROR assigning to `&T` is undefined behavior
|
||||
|
||||
unsafe fn generic_assign_to_ref<T>(this: &T, a: T) {
|
||||
*(this as *const _ as *mut _) = a;
|
||||
|
@ -131,7 +131,17 @@ LL | *std::mem::transmute::<_, *mut i32>(num) += 1;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||
--> $DIR/reference_casting.rs:76:5
|
||||
--> $DIR/reference_casting.rs:74:5
|
||||
|
|
||||
LL | / std::ptr::write(
|
||||
LL | |
|
||||
LL | | std::mem::transmute::<*const i32, *mut i32>(num),
|
||||
LL | | -1i32,
|
||||
LL | | );
|
||||
| |_____^
|
||||
|
||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||
--> $DIR/reference_casting.rs:81:5
|
||||
|
|
||||
LL | let value = num as *const i32 as *mut i32;
|
||||
| ----------------------------- casting happend here
|
||||
@ -139,22 +149,49 @@ LL | *value = 1;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||
--> $DIR/reference_casting.rs:78:5
|
||||
--> $DIR/reference_casting.rs:83:5
|
||||
|
|
||||
LL | *(num as *const i32).cast::<i32>().cast_mut() = 2;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||
--> $DIR/reference_casting.rs:80:5
|
||||
--> $DIR/reference_casting.rs:85:5
|
||||
|
|
||||
LL | *(num as *const _ as usize as *mut i32) = 2;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||
--> $DIR/reference_casting.rs:84:9
|
||||
--> $DIR/reference_casting.rs:87:5
|
||||
|
|
||||
LL | let value = num as *const i32 as *mut i32;
|
||||
| ----------------------------- casting happend here
|
||||
...
|
||||
LL | std::ptr::write(value, 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||
--> $DIR/reference_casting.rs:89:5
|
||||
|
|
||||
LL | let value = num as *const i32 as *mut i32;
|
||||
| ----------------------------- casting happend here
|
||||
...
|
||||
LL | std::ptr::write_unaligned(value, 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||
--> $DIR/reference_casting.rs:91:5
|
||||
|
|
||||
LL | let value = num as *const i32 as *mut i32;
|
||||
| ----------------------------- casting happend here
|
||||
...
|
||||
LL | std::ptr::write_volatile(value, 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||
--> $DIR/reference_casting.rs:95:9
|
||||
|
|
||||
LL | *(this as *const _ as *mut _) = a;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 25 previous errors
|
||||
error: aborting due to 29 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user