Rollup merge of #132732 - gavincrawford:as_ptr_attribute, r=Urgau

Use attributes for `dangling_pointers_from_temporaries` lint

Checking for dangling pointers by function name isn't ideal, and leaves out certain pointer-returning methods that don't follow the `as_ptr` naming convention. Using an attribute for this lint cleans things up and allows more thorough coverage of other methods, such as `UnsafeCell::get()`.
This commit is contained in:
Jacob Pratt 2024-11-20 01:54:24 -05:00 committed by GitHub
commit 25dc4d0394
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 83 additions and 32 deletions

View File

@ -879,6 +879,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, EncodeCrossCrate::No, lang_items,
"lang items are subject to change",
),
rustc_attr!(
rustc_as_ptr, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::Yes,
"#[rustc_as_ptr] is used to mark functions returning pointers to their inner allocations."
),
rustc_attr!(
rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::Yes,

View File

@ -43,13 +43,10 @@ declare_lint! {
}
/// FIXME: false negatives (i.e. the lint is not emitted when it should be)
/// 1. Method calls that are not checked for:
/// - [`temporary_unsafe_cell.get()`][`core::cell::UnsafeCell::get()`]
/// - [`temporary_sync_unsafe_cell.get()`][`core::cell::SyncUnsafeCell::get()`]
/// 2. Ways to get a temporary that are not recognized:
/// 1. Ways to get a temporary that are not recognized:
/// - `owning_temporary.field`
/// - `owning_temporary[index]`
/// 3. No checks for ref-to-ptr conversions:
/// 2. No checks for ref-to-ptr conversions:
/// - `&raw [mut] temporary`
/// - `&temporary as *(const|mut) _`
/// - `ptr::from_ref(&temporary)` and friends
@ -133,10 +130,11 @@ impl DanglingPointerSearcher<'_, '_> {
fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind
&& matches!(method.ident.name, sym::as_ptr | sym::as_mut_ptr)
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& cx.tcx.has_attr(fn_id, sym::rustc_as_ptr)
&& is_temporary_rvalue(receiver)
&& let ty = cx.typeck_results().expr_ty(receiver)
&& is_interesting(cx.tcx, ty)
&& owns_allocation(cx.tcx, ty)
{
// FIXME: use `emit_node_lint` when `#[primary_span]` is added.
cx.tcx.emit_node_span_lint(
@ -199,24 +197,25 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool {
}
}
// Array, Vec, String, CString, MaybeUninit, Cell, Box<[_]>, Box<str>, Box<CStr>,
// or any of the above in arbitrary many nested Box'es.
fn is_interesting(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool {
// Array, Vec, String, CString, MaybeUninit, Cell, Box<[_]>, Box<str>, Box<CStr>, UnsafeCell,
// SyncUnsafeCell, or any of the above in arbitrary many nested Box'es.
fn owns_allocation(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool {
if ty.is_array() {
true
} else if let Some(inner) = ty.boxed_ty() {
inner.is_slice()
|| inner.is_str()
|| inner.ty_adt_def().is_some_and(|def| tcx.is_lang_item(def.did(), LangItem::CStr))
|| is_interesting(tcx, inner)
|| owns_allocation(tcx, inner)
} else if let Some(def) = ty.ty_adt_def() {
for lang_item in [LangItem::String, LangItem::MaybeUninit] {
for lang_item in [LangItem::String, LangItem::MaybeUninit, LangItem::UnsafeCell] {
if tcx.is_lang_item(def.did(), lang_item) {
return true;
}
}
tcx.get_diagnostic_name(def.did())
.is_some_and(|name| matches!(name, sym::cstring_type | sym::Vec | sym::Cell))
tcx.get_diagnostic_name(def.did()).is_some_and(|name| {
matches!(name, sym::cstring_type | sym::Vec | sym::Cell | sym::SyncUnsafeCell)
})
} else {
false
}

View File

@ -162,6 +162,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.check_rustc_std_internal_symbol(attr, span, target)
}
[sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs),
[sym::rustc_as_ptr, ..] => {
self.check_applied_to_fn_or_method(hir_id, attr, span, target)
}
[sym::rustc_never_returns_null_ptr, ..] => {
self.check_applied_to_fn_or_method(hir_id, attr, span, target)
}

View File

@ -316,6 +316,7 @@ symbols! {
SubdiagMessage,
Subdiagnostic,
Sync,
SyncUnsafeCell,
T,
Target,
ToOwned,
@ -409,7 +410,6 @@ symbols! {
arm,
arm_target_feature,
array,
as_mut_ptr,
as_ptr,
as_ref,
as_str,
@ -1655,6 +1655,7 @@ symbols! {
rustc_allow_const_fn_unstable,
rustc_allow_incoherent_impl,
rustc_allowed_through_unstable_modules,
rustc_as_ptr,
rustc_attrs,
rustc_autodiff,
rustc_box,

View File

@ -1500,6 +1500,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
/// [`as_ptr`]: Self::as_ptr
#[unstable(feature = "box_as_ptr", issue = "129090")]
#[rustc_never_returns_null_ptr]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[inline]
pub fn as_mut_ptr(b: &mut Self) -> *mut T {
// This is a primitive deref, not going through `DerefMut`, and therefore not materializing
@ -1548,6 +1549,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
/// [`as_ptr`]: Self::as_ptr
#[unstable(feature = "box_as_ptr", issue = "129090")]
#[rustc_never_returns_null_ptr]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[inline]
pub fn as_ptr(b: &Self) -> *const T {
// This is a primitive deref, not going through `DerefMut`, and therefore not materializing

View File

@ -1662,6 +1662,7 @@ impl<T, A: Allocator> Vec<T, A> {
#[stable(feature = "vec_as_ptr", since = "1.37.0")]
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
#[rustc_never_returns_null_ptr]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[inline]
pub const fn as_ptr(&self) -> *const T {
// We shadow the slice method of the same name to avoid going through
@ -1724,6 +1725,7 @@ impl<T, A: Allocator> Vec<T, A> {
#[stable(feature = "vec_as_ptr", since = "1.37.0")]
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
#[rustc_never_returns_null_ptr]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[inline]
pub const fn as_mut_ptr(&mut self) -> *mut T {
// We shadow the slice method of the same name to avoid going through

View File

@ -587,6 +587,7 @@ impl<T: ?Sized> Cell<T> {
#[inline]
#[stable(feature = "cell_as_ptr", since = "1.12.0")]
#[rustc_const_stable(feature = "const_cell_as_ptr", since = "1.32.0")]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[rustc_never_returns_null_ptr]
pub const fn as_ptr(&self) -> *mut T {
self.value.get()
@ -1149,6 +1150,7 @@ impl<T: ?Sized> RefCell<T> {
/// ```
#[inline]
#[stable(feature = "cell_as_ptr", since = "1.12.0")]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[rustc_never_returns_null_ptr]
pub fn as_ptr(&self) -> *mut T {
self.value.get()
@ -2158,6 +2160,7 @@ impl<T: ?Sized> UnsafeCell<T> {
#[inline(always)]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_unsafecell_get", since = "1.32.0")]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[rustc_never_returns_null_ptr]
pub const fn get(&self) -> *mut T {
// We can just cast the pointer from `UnsafeCell<T>` to `T` because of
@ -2271,6 +2274,7 @@ impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<UnsafeCell<U>> for UnsafeCell<T>
/// See [`UnsafeCell`] for details.
#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
#[repr(transparent)]
#[rustc_diagnostic_item = "SyncUnsafeCell"]
#[rustc_pub_transparent]
pub struct SyncUnsafeCell<T: ?Sized> {
value: UnsafeCell<T>,
@ -2304,6 +2308,7 @@ impl<T: ?Sized> SyncUnsafeCell<T> {
/// when casting to `&mut T`, and ensure that there are no mutations
/// or mutable aliases going on when casting to `&T`
#[inline]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[rustc_never_returns_null_ptr]
pub const fn get(&self) -> *mut T {
self.value.get()

View File

@ -500,6 +500,7 @@ impl CStr {
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_str_as_ptr", since = "1.32.0")]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[rustc_never_returns_null_ptr]
pub const fn as_ptr(&self) -> *const c_char {
self.inner.as_ptr()

View File

@ -525,6 +525,7 @@ impl<T> MaybeUninit<T> {
/// until they are, it is advisable to avoid them.)
#[stable(feature = "maybe_uninit", since = "1.36.0")]
#[rustc_const_stable(feature = "const_maybe_uninit_as_ptr", since = "1.59.0")]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[inline(always)]
pub const fn as_ptr(&self) -> *const T {
// `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer.
@ -566,6 +567,7 @@ impl<T> MaybeUninit<T> {
/// until they are, it is advisable to avoid them.)
#[stable(feature = "maybe_uninit", since = "1.36.0")]
#[rustc_const_stable(feature = "const_maybe_uninit_as_mut_ptr", since = "1.83.0")]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[inline(always)]
pub const fn as_mut_ptr(&mut self) -> *mut T {
// `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer.

View File

@ -735,6 +735,7 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")]
#[rustc_never_returns_null_ptr]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[inline(always)]
#[must_use]
pub const fn as_ptr(&self) -> *const T {
@ -765,6 +766,7 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
#[rustc_never_returns_null_ptr]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[inline(always)]
#[must_use]
pub const fn as_mut_ptr(&mut self) -> *mut T {

View File

@ -373,6 +373,7 @@ impl str {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "rustc_str_as_ptr", since = "1.32.0")]
#[rustc_never_returns_null_ptr]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[must_use]
#[inline(always)]
pub const fn as_ptr(&self) -> *const u8 {
@ -390,6 +391,7 @@ impl str {
#[stable(feature = "str_as_mut_ptr", since = "1.36.0")]
#[rustc_const_stable(feature = "const_str_as_mut", since = "1.83.0")]
#[rustc_never_returns_null_ptr]
#[cfg_attr(not(bootstrap), rustc_as_ptr)]
#[must_use]
#[inline(always)]
pub const fn as_mut_ptr(&mut self) -> *mut u8 {

View File

@ -1,6 +1,7 @@
#![deny(dangling_pointers_from_temporaries)]
#![feature(sync_unsafe_cell)]
use std::cell::Cell;
use std::cell::{Cell, SyncUnsafeCell, UnsafeCell};
use std::ffi::{CStr, CString};
use std::mem::MaybeUninit;
@ -47,6 +48,10 @@ fn main() {
//~^ ERROR a dangling pointer will be produced because the temporary `MaybeUninit<u8>` will be dropped
declval::<Vec<AsPtrFake>>().as_ptr();
//~^ ERROR a dangling pointer will be produced because the temporary `Vec<AsPtrFake>` will be dropped
declval::<UnsafeCell<u8>>().get();
//~^ ERROR a dangling pointer will be produced because the temporary `UnsafeCell<u8>` will be dropped
declval::<SyncUnsafeCell<u8>>().get();
//~^ ERROR a dangling pointer will be produced because the temporary `SyncUnsafeCell<u8>` will be dropped
declval::<Box<AsPtrFake>>().as_ptr();
declval::<AsPtrFake>().as_ptr();
}

View File

@ -1,5 +1,5 @@
error: a dangling pointer will be produced because the temporary `CString` will be dropped
--> $DIR/types.rs:20:26
--> $DIR/types.rs:21:26
|
LL | declval::<CString>().as_ptr();
| -------------------- ^^^^^^ this pointer will immediately be invalid
@ -15,7 +15,7 @@ LL | #![deny(dangling_pointers_from_temporaries)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a dangling pointer will be produced because the temporary `String` will be dropped
--> $DIR/types.rs:22:25
--> $DIR/types.rs:23:25
|
LL | declval::<String>().as_ptr();
| ------------------- ^^^^^^ this pointer will immediately be invalid
@ -26,7 +26,7 @@ LL | declval::<String>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Vec<u8>` will be dropped
--> $DIR/types.rs:24:26
--> $DIR/types.rs:25:26
|
LL | declval::<Vec<u8>>().as_ptr();
| -------------------- ^^^^^^ this pointer will immediately be invalid
@ -37,7 +37,7 @@ LL | declval::<Vec<u8>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Box<CString>` will be dropped
--> $DIR/types.rs:26:31
--> $DIR/types.rs:27:31
|
LL | declval::<Box<CString>>().as_ptr();
| ------------------------- ^^^^^^ this pointer will immediately be invalid
@ -48,7 +48,7 @@ LL | declval::<Box<CString>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Box<[u8]>` will be dropped
--> $DIR/types.rs:28:28
--> $DIR/types.rs:29:28
|
LL | declval::<Box<[u8]>>().as_ptr();
| ---------------------- ^^^^^^ this pointer will immediately be invalid
@ -59,7 +59,7 @@ LL | declval::<Box<[u8]>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Box<str>` will be dropped
--> $DIR/types.rs:30:27
--> $DIR/types.rs:31:27
|
LL | declval::<Box<str>>().as_ptr();
| --------------------- ^^^^^^ this pointer will immediately be invalid
@ -70,7 +70,7 @@ LL | declval::<Box<str>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Box<CStr>` will be dropped
--> $DIR/types.rs:32:28
--> $DIR/types.rs:33:28
|
LL | declval::<Box<CStr>>().as_ptr();
| ---------------------- ^^^^^^ this pointer will immediately be invalid
@ -81,7 +81,7 @@ LL | declval::<Box<CStr>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `[u8; 10]` will be dropped
--> $DIR/types.rs:34:27
--> $DIR/types.rs:35:27
|
LL | declval::<[u8; 10]>().as_ptr();
| --------------------- ^^^^^^ this pointer will immediately be invalid
@ -92,7 +92,7 @@ LL | declval::<[u8; 10]>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Box<[u8; 10]>` will be dropped
--> $DIR/types.rs:36:32
--> $DIR/types.rs:37:32
|
LL | declval::<Box<[u8; 10]>>().as_ptr();
| -------------------------- ^^^^^^ this pointer will immediately be invalid
@ -103,7 +103,7 @@ LL | declval::<Box<[u8; 10]>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Box<Vec<u8>>` will be dropped
--> $DIR/types.rs:38:31
--> $DIR/types.rs:39:31
|
LL | declval::<Box<Vec<u8>>>().as_ptr();
| ------------------------- ^^^^^^ this pointer will immediately be invalid
@ -114,7 +114,7 @@ LL | declval::<Box<Vec<u8>>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Box<String>` will be dropped
--> $DIR/types.rs:40:30
--> $DIR/types.rs:41:30
|
LL | declval::<Box<String>>().as_ptr();
| ------------------------ ^^^^^^ this pointer will immediately be invalid
@ -125,7 +125,7 @@ LL | declval::<Box<String>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Box<Box<Box<Box<[u8]>>>>` will be dropped
--> $DIR/types.rs:42:43
--> $DIR/types.rs:43:43
|
LL | declval::<Box<Box<Box<Box<[u8]>>>>>().as_ptr();
| ------------------------------------- ^^^^^^ this pointer will immediately be invalid
@ -136,7 +136,7 @@ LL | declval::<Box<Box<Box<Box<[u8]>>>>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Cell<u8>` will be dropped
--> $DIR/types.rs:44:27
--> $DIR/types.rs:45:27
|
LL | declval::<Cell<u8>>().as_ptr();
| --------------------- ^^^^^^ this pointer will immediately be invalid
@ -147,7 +147,7 @@ LL | declval::<Cell<u8>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `MaybeUninit<u8>` will be dropped
--> $DIR/types.rs:46:34
--> $DIR/types.rs:47:34
|
LL | declval::<MaybeUninit<u8>>().as_ptr();
| ---------------------------- ^^^^^^ this pointer will immediately be invalid
@ -158,7 +158,7 @@ LL | declval::<MaybeUninit<u8>>().as_ptr();
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `Vec<AsPtrFake>` will be dropped
--> $DIR/types.rs:48:33
--> $DIR/types.rs:49:33
|
LL | declval::<Vec<AsPtrFake>>().as_ptr();
| --------------------------- ^^^^^^ this pointer will immediately be invalid
@ -168,5 +168,27 @@ LL | declval::<Vec<AsPtrFake>>().as_ptr();
= note: pointers do not have a lifetime; when calling `as_ptr` the `Vec<AsPtrFake>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: aborting due to 15 previous errors
error: a dangling pointer will be produced because the temporary `UnsafeCell<u8>` will be dropped
--> $DIR/types.rs:51:33
|
LL | declval::<UnsafeCell<u8>>().get();
| --------------------------- ^^^ this pointer will immediately be invalid
| |
| this `UnsafeCell<u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
|
= note: pointers do not have a lifetime; when calling `get` the `UnsafeCell<u8>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: a dangling pointer will be produced because the temporary `SyncUnsafeCell<u8>` will be dropped
--> $DIR/types.rs:53:37
|
LL | declval::<SyncUnsafeCell<u8>>().get();
| ------------------------------- ^^^ this pointer will immediately be invalid
| |
| this `SyncUnsafeCell<u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
|
= note: pointers do not have a lifetime; when calling `get` the `SyncUnsafeCell<u8>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
= help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
error: aborting due to 17 previous errors