Skip to content

Commit

Permalink
Split fuzzy_provenance_casts into lossy and fuzzy, feature gate and…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
niluxv committed Apr 8, 2022
1 parent 1040cab commit 98a4834
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 35 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
88 changes: 72 additions & 16 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2654,33 +2654,88 @@ 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;
/// }
/// ```
///
/// {{produces}}
///
/// ### 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! {
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1348,6 +1348,7 @@ symbols! {
str_trim,
str_trim_end,
str_trim_start,
strict_provenance,
stringify,
stringify_macro,
struct_field_attributes,
Expand Down
61 changes: 42 additions & 19 deletions compiler/rustc_typeck/src/check/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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();
},
Expand All @@ -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();
},
);
}
Expand Down
22 changes: 22 additions & 0 deletions src/doc/unstable-book/src/language-features/strict-provenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# `strict_provenance`

The tracking issue for this feature is: [#95228]

[#95228]: https://github.com/rust-lang/rust/issues/95228
-----

The `strict_provenance` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints.
These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model.
The same feature gate is also used for the experimental strict provenance API in `std` (actually `core`).

## Example

```rust
#![feature(strict_provenance)]
#![warn(fuzzy_provenance_casts)]

fn main() {
let _dangling = 16_usize as *const u8;
//~^ WARNING: strict provenance disallows casting integer `usize` to pointer `*const u8`
}
```
19 changes: 19 additions & 0 deletions src/test/ui/feature-gates/feature-gate-strict_provenance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// check-pass

#![deny(fuzzy_provenance_casts)]
//~^ WARNING unknown lint: `fuzzy_provenance_casts`
//~| WARNING unknown lint: `fuzzy_provenance_casts`
//~| WARNING unknown lint: `fuzzy_provenance_casts`
#![deny(lossy_provenance_casts)]
//~^ WARNING unknown lint: `lossy_provenance_casts`
//~| WARNING unknown lint: `lossy_provenance_casts`
//~| WARNING unknown lint: `lossy_provenance_casts`

fn main() {
// no warnings emitted since the lints are not activated

let _dangling = 16_usize as *const u8;

let x: u8 = 37;
let _addr: usize = &x as *const u8 as usize;
}
63 changes: 63 additions & 0 deletions src/test/ui/feature-gates/feature-gate-strict_provenance.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
warning: unknown lint: `fuzzy_provenance_casts`
--> $DIR/feature-gate-strict_provenance.rs:3:1
|
LL | #![deny(fuzzy_provenance_casts)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unknown_lints)]` on by default
= note: the `fuzzy_provenance_casts` lint is unstable
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable

warning: unknown lint: `lossy_provenance_casts`
--> $DIR/feature-gate-strict_provenance.rs:7:1
|
LL | #![deny(lossy_provenance_casts)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `lossy_provenance_casts` lint is unstable
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable

warning: unknown lint: `fuzzy_provenance_casts`
--> $DIR/feature-gate-strict_provenance.rs:3:1
|
LL | #![deny(fuzzy_provenance_casts)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `fuzzy_provenance_casts` lint is unstable
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable

warning: unknown lint: `lossy_provenance_casts`
--> $DIR/feature-gate-strict_provenance.rs:7:1
|
LL | #![deny(lossy_provenance_casts)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `lossy_provenance_casts` lint is unstable
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable

warning: unknown lint: `fuzzy_provenance_casts`
--> $DIR/feature-gate-strict_provenance.rs:3:1
|
LL | #![deny(fuzzy_provenance_casts)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `fuzzy_provenance_casts` lint is unstable
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable

warning: unknown lint: `lossy_provenance_casts`
--> $DIR/feature-gate-strict_provenance.rs:7:1
|
LL | #![deny(lossy_provenance_casts)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `lossy_provenance_casts` lint is unstable
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable

warning: 6 warnings emitted

7 changes: 7 additions & 0 deletions src/test/ui/lint/lint-strict-provenance-fuzzy-casts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![feature(strict_provenance)]
#![deny(fuzzy_provenance_casts)]

fn main() {
let dangling = 16_usize as *const u8;
//~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const u8`
}
19 changes: 19 additions & 0 deletions src/test/ui/lint/lint-strict-provenance-fuzzy-casts.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error: strict provenance disallows casting integer `usize` to pointer `*const u8`
--> $DIR/lint-strict-provenance-fuzzy-casts.rs:5:20
|
LL | let dangling = 16_usize as *const u8;
| ^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/lint-strict-provenance-fuzzy-casts.rs:2:9
|
LL | #![deny(fuzzy_provenance_casts)]
| ^^^^^^^^^^^^^^^^^^^^^^
= 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
help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address
|
LL | let dangling = (...).with_addr(16_usize);
| ~~~~~~~~~~~~~~~~~~~~~~~~~

error: aborting due to previous error

11 changes: 11 additions & 0 deletions src/test/ui/lint/lint-strict-provenance-lossy-casts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(strict_provenance)]
#![deny(lossy_provenance_casts)]

fn main() {
let x: u8 = 37;
let addr: usize = &x as *const u8 as usize;
//~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize`

let addr_32bit = &x as *const u8 as u32;
//~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32`
}

0 comments on commit 98a4834

Please sign in to comment.