Split fuzzy_provenance_casts
into lossy and fuzzy, feature gate and test it
* split `fuzzy_provenance_casts` into a ptr2int and a int2ptr lint * feature gate both lints * update documentation to be more realistic short term * add tests for these lints
This commit is contained in:
parent
1040cab53b
commit
98a4834237
11 changed files with 281 additions and 35 deletions
|
@ -505,6 +505,8 @@ declare_features! (
|
|||
(active, static_nobundle, "1.16.0", Some(37403), None),
|
||||
/// Allows attributes on expressions and non-item statements.
|
||||
(active, stmt_expr_attributes, "1.6.0", Some(15701), None),
|
||||
/// Allows lints part of the strict provenance effort.
|
||||
(active, strict_provenance, "1.61.0", Some(95228), None),
|
||||
/// Allows the use of `#[target_feature]` on safe functions.
|
||||
(active, target_feature_11, "1.45.0", Some(69098), None),
|
||||
/// Allows using `#[thread_local]` on `static` items.
|
||||
|
|
|
@ -2654,9 +2654,12 @@ declare_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(strict_provenance)]
|
||||
/// #![warn(fuzzy_provenance_casts)]
|
||||
///
|
||||
/// fn main() {
|
||||
/// let my_ref = &0;
|
||||
/// let my_addr = my_ref as usize;
|
||||
/// let _dangling = 16_usize as *const u8;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
@ -2664,23 +2667,75 @@ declare_lint! {
|
|||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Casting a pointer to an integer or an integer to a pointer is a lossy operation,
|
||||
/// because beyond just an *address* a pointer may be associated with a particular
|
||||
/// *provenance* and *segment*. This information is required by both the compiler
|
||||
/// and the hardware to correctly execute your code. If you need to do this kind
|
||||
/// of operation, use ptr::addr and ptr::with_addr.
|
||||
/// This lint is part of the strict provenance effort, see [issue #95228].
|
||||
/// Casting an integer to a pointer is considered bad style, as a pointer
|
||||
/// contains, besides the *address* also a *provenance*, indicating what
|
||||
/// memory the pointer is allowed to read/write. Casting an integer, which
|
||||
/// doesn't have provenance, to a pointer requires the compiler to assign
|
||||
/// (guess) provenance. The compiler assigns "all exposed valid" (see the
|
||||
/// docs of [`ptr::from_exposed_addr`] for more information about this
|
||||
/// "exposing"). This penalizes the optimiser and is not well suited for
|
||||
/// dynamic analysis/dynamic program verification (e.g. Miri or CHERI
|
||||
/// platforms).
|
||||
///
|
||||
/// This is a [future-incompatible] lint to transition this to a hard error
|
||||
/// in the future. See [issue #9999999] for more details.
|
||||
/// It is much better to use [`ptr::with_addr`] instead to specify the
|
||||
/// provenance you want. If using this function is not possible because the
|
||||
/// code relies on exposed provenance then there is as an escape hatch
|
||||
/// [`ptr::from_exposed_addr`].
|
||||
///
|
||||
/// [future-incompatible]: ../index.md#future-incompatible-lints
|
||||
/// [issue #9999999]: https://github.com/rust-lang/rust/issues/9999999
|
||||
/// [issue #95228]: https://github.com/rust-lang/rust/issues/95228
|
||||
/// [`ptr::with_addr`]: https://doc.rust-lang.org/core/ptr/fn.with_addr
|
||||
/// [`ptr::from_exposed_addr`]: https://doc.rust-lang.org/core/ptr/fn.from_exposed_addr
|
||||
pub FUZZY_PROVENANCE_CASTS,
|
||||
Warn,
|
||||
"A lossy pointer-integer integer cast is used",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reference: "issue #9999999 <https://github.com/rust-lang/rust/issues/9999999>",
|
||||
};
|
||||
Allow,
|
||||
"a fuzzy integer to pointer cast is used",
|
||||
@feature_gate = sym::strict_provenance;
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `lossy_provenance_casts` lint detects an `as` cast between a pointer
|
||||
/// and an integer.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(strict_provenance)]
|
||||
/// #![warn(lossy_provenance_casts)]
|
||||
///
|
||||
/// fn main() {
|
||||
/// let x: u8 = 37;
|
||||
/// let _addr: usize = &x as *const u8 as usize;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// This lint is part of the strict provenance effort, see [issue #95228].
|
||||
/// Casting a pointer to an integer is a lossy operation, because beyond
|
||||
/// just an *address* a pointer may be associated with a particular
|
||||
/// *provenance*. This information is used by the optimiser and for dynamic
|
||||
/// analysis/dynamic program verification (e.g. Miri or CHERI platforms).
|
||||
///
|
||||
/// Since this cast is lossy, it is considered good style to use the
|
||||
/// [`ptr::addr`] method instead, which has a similar effect, but doesn't
|
||||
/// "expose" the pointer provenance. This improves optimisation potential.
|
||||
/// See the docs of [`ptr::addr`] and [`ptr::expose_addr`] for more information
|
||||
/// about exposing pointer provenance.
|
||||
///
|
||||
/// If your code can't comply with strict provenance and needs to expose
|
||||
/// the provenance, then there is [`ptr::expose_addr`] as an escape hatch,
|
||||
/// which preserves the behaviour of `as usize` casts while being explicit
|
||||
/// about the semantics.
|
||||
///
|
||||
/// [issue #95228]: https://github.com/rust-lang/rust/issues/95228
|
||||
/// [`ptr::addr`]: https://doc.rust-lang.org/core/ptr/fn.addr
|
||||
/// [`ptr::expose_addr`]: https://doc.rust-lang.org/core/ptr/fn.expose_addr
|
||||
pub LOSSY_PROVENANCE_CASTS,
|
||||
Allow,
|
||||
"a lossy pointer to integer cast is used",
|
||||
@feature_gate = sym::strict_provenance;
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
|
@ -3137,6 +3192,7 @@ declare_lint_pass! {
|
|||
INCOMPLETE_INCLUDE,
|
||||
CENUM_IMPL_DROP_CAST,
|
||||
FUZZY_PROVENANCE_CASTS,
|
||||
LOSSY_PROVENANCE_CASTS,
|
||||
CONST_EVALUATABLE_UNCHECKED,
|
||||
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
|
||||
MUST_NOT_SUSPEND,
|
||||
|
|
|
@ -1348,6 +1348,7 @@ symbols! {
|
|||
str_trim,
|
||||
str_trim_end,
|
||||
str_trim_start,
|
||||
strict_provenance,
|
||||
stringify,
|
||||
stringify_macro,
|
||||
struct_field_attributes,
|
||||
|
|
|
@ -809,12 +809,12 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
(Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast
|
||||
|
||||
// ptr-addr-cast
|
||||
(Ptr(m_expr), Int(_)) => {
|
||||
self.fuzzy_provenance_ptr2int_lint(fcx, t_from);
|
||||
(Ptr(m_expr), Int(t_c)) => {
|
||||
self.lossy_provenance_ptr2int_lint(fcx, t_c);
|
||||
self.check_ptr_addr_cast(fcx, m_expr)
|
||||
}
|
||||
(FnPtr, Int(_)) => {
|
||||
self.fuzzy_provenance_ptr2int_lint(fcx, t_from);
|
||||
// FIXME(#95489): there should eventually be a lint for these casts
|
||||
Ok(CastKind::FnPtrAddrCast)
|
||||
}
|
||||
// addr-ptr-cast
|
||||
|
@ -945,7 +945,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
fcx: &FnCtxt<'a, 'tcx>,
|
||||
m_cast: TypeAndMut<'tcx>,
|
||||
) -> Result<CastKind, CastError> {
|
||||
self.fuzzy_provenance_int2ptr_lint(fcx);
|
||||
// ptr-addr cast. pointer must be thin.
|
||||
match fcx.pointer_kind(m_cast.ty, self.span)? {
|
||||
None => Err(CastError::UnknownCastPtrKind),
|
||||
|
@ -986,25 +985,36 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fuzzy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_from: CastTy<'tcx>) {
|
||||
fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
|
||||
fcx.tcx.struct_span_lint_hir(
|
||||
lint::builtin::FUZZY_PROVENANCE_CASTS,
|
||||
lint::builtin::LOSSY_PROVENANCE_CASTS,
|
||||
self.expr.hir_id,
|
||||
self.span,
|
||||
|err| {
|
||||
let mut err = err.build(&format!(
|
||||
"strict provenance disallows casting pointer `{}` to integer `{}`",
|
||||
"under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`",
|
||||
self.expr_ty, self.cast_ty
|
||||
));
|
||||
|
||||
if let CastTy::FnPtr = t_from {
|
||||
err.help(
|
||||
"use `(... as *const u8).addr()` to obtain \
|
||||
the address of a function pointer",
|
||||
let msg = "use `.addr()` to obtain the address of a pointer";
|
||||
if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) {
|
||||
let scalar_cast = match t_c {
|
||||
ty::cast::IntTy::U(ty::UintTy::Usize) => String::new(),
|
||||
_ => format!(" as {}", self.cast_ty),
|
||||
};
|
||||
err.span_suggestion(
|
||||
self.span,
|
||||
msg,
|
||||
format!("({}).addr(){}", snippet, scalar_cast),
|
||||
Applicability::MaybeIncorrect
|
||||
);
|
||||
} else {
|
||||
err.help("use `.addr()` to obtain the address of a pointer");
|
||||
err.help(msg);
|
||||
}
|
||||
err.help(
|
||||
"if you can't comply with strict provenance and need to expose the pointer\
|
||||
provenance you can use `.expose_addr()` instead"
|
||||
);
|
||||
|
||||
err.emit();
|
||||
},
|
||||
|
@ -1017,15 +1027,28 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
self.expr.hir_id,
|
||||
self.span,
|
||||
|err| {
|
||||
err.build(&format!(
|
||||
|
||||
let mut err = err.build(&format!(
|
||||
"strict provenance disallows casting integer `{}` to pointer `{}`",
|
||||
self.expr_ty, self.cast_ty
|
||||
))
|
||||
.help(
|
||||
"use `.with_addr(...)` to adjust a valid pointer \
|
||||
in the same allocation, to this address",
|
||||
)
|
||||
.emit();
|
||||
));
|
||||
let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address";
|
||||
if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) {
|
||||
err.span_suggestion(
|
||||
self.span,
|
||||
msg,
|
||||
format!("(...).with_addr({})", snippet),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
err.help(msg);
|
||||
}
|
||||
err.help(
|
||||
"if you can't comply with strict provenance and don't have a pointer with \
|
||||
the correct provenance you can use `std::ptr::from_exposed_addr()` instead"
|
||||
);
|
||||
|
||||
err.emit();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue