Skip to content

Commit 98a4834

Browse files
committed
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
1 parent 1040cab commit 98a4834

File tree

11 files changed

+281
-35
lines changed

11 files changed

+281
-35
lines changed

compiler/rustc_feature/src/active.rs

+2
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,8 @@ declare_features! (
505505
(active, static_nobundle, "1.16.0", Some(37403), None),
506506
/// Allows attributes on expressions and non-item statements.
507507
(active, stmt_expr_attributes, "1.6.0", Some(15701), None),
508+
/// Allows lints part of the strict provenance effort.
509+
(active, strict_provenance, "1.61.0", Some(95228), None),
508510
/// Allows the use of `#[target_feature]` on safe functions.
509511
(active, target_feature_11, "1.45.0", Some(69098), None),
510512
/// Allows using `#[thread_local]` on `static` items.

compiler/rustc_lint_defs/src/builtin.rs

+72-16
Original file line numberDiff line numberDiff line change
@@ -2654,33 +2654,88 @@ declare_lint! {
26542654
///
26552655
/// ### Example
26562656
///
2657+
/// ```rust
2658+
/// #![feature(strict_provenance)]
2659+
/// #![warn(fuzzy_provenance_casts)]
2660+
///
26572661
/// fn main() {
2658-
/// let my_ref = &0;
2659-
/// let my_addr = my_ref as usize;
2662+
/// let _dangling = 16_usize as *const u8;
26602663
/// }
26612664
/// ```
26622665
///
26632666
/// {{produces}}
26642667
///
26652668
/// ### Explanation
26662669
///
2667-
/// Casting a pointer to an integer or an integer to a pointer is a lossy operation,
2668-
/// because beyond just an *address* a pointer may be associated with a particular
2669-
/// *provenance* and *segment*. This information is required by both the compiler
2670-
/// and the hardware to correctly execute your code. If you need to do this kind
2671-
/// of operation, use ptr::addr and ptr::with_addr.
2670+
/// This lint is part of the strict provenance effort, see [issue #95228].
2671+
/// Casting an integer to a pointer is considered bad style, as a pointer
2672+
/// contains, besides the *address* also a *provenance*, indicating what
2673+
/// memory the pointer is allowed to read/write. Casting an integer, which
2674+
/// doesn't have provenance, to a pointer requires the compiler to assign
2675+
/// (guess) provenance. The compiler assigns "all exposed valid" (see the
2676+
/// docs of [`ptr::from_exposed_addr`] for more information about this
2677+
/// "exposing"). This penalizes the optimiser and is not well suited for
2678+
/// dynamic analysis/dynamic program verification (e.g. Miri or CHERI
2679+
/// platforms).
26722680
///
2673-
/// This is a [future-incompatible] lint to transition this to a hard error
2674-
/// in the future. See [issue #9999999] for more details.
2681+
/// It is much better to use [`ptr::with_addr`] instead to specify the
2682+
/// provenance you want. If using this function is not possible because the
2683+
/// code relies on exposed provenance then there is as an escape hatch
2684+
/// [`ptr::from_exposed_addr`].
26752685
///
2676-
/// [future-incompatible]: ../index.md#future-incompatible-lints
2677-
/// [issue #9999999]: https://github.com/rust-lang/rust/issues/9999999
2686+
/// [issue #95228]: https://github.com/rust-lang/rust/issues/95228
2687+
/// [`ptr::with_addr`]: https://doc.rust-lang.org/core/ptr/fn.with_addr
2688+
/// [`ptr::from_exposed_addr`]: https://doc.rust-lang.org/core/ptr/fn.from_exposed_addr
26782689
pub FUZZY_PROVENANCE_CASTS,
2679-
Warn,
2680-
"A lossy pointer-integer integer cast is used",
2681-
@future_incompatible = FutureIncompatibleInfo {
2682-
reference: "issue #9999999 <https://github.com/rust-lang/rust/issues/9999999>",
2683-
};
2690+
Allow,
2691+
"a fuzzy integer to pointer cast is used",
2692+
@feature_gate = sym::strict_provenance;
2693+
}
2694+
2695+
declare_lint! {
2696+
/// The `lossy_provenance_casts` lint detects an `as` cast between a pointer
2697+
/// and an integer.
2698+
///
2699+
/// ### Example
2700+
///
2701+
/// ```rust
2702+
/// #![feature(strict_provenance)]
2703+
/// #![warn(lossy_provenance_casts)]
2704+
///
2705+
/// fn main() {
2706+
/// let x: u8 = 37;
2707+
/// let _addr: usize = &x as *const u8 as usize;
2708+
/// }
2709+
/// ```
2710+
///
2711+
/// {{produces}}
2712+
///
2713+
/// ### Explanation
2714+
///
2715+
/// This lint is part of the strict provenance effort, see [issue #95228].
2716+
/// Casting a pointer to an integer is a lossy operation, because beyond
2717+
/// just an *address* a pointer may be associated with a particular
2718+
/// *provenance*. This information is used by the optimiser and for dynamic
2719+
/// analysis/dynamic program verification (e.g. Miri or CHERI platforms).
2720+
///
2721+
/// Since this cast is lossy, it is considered good style to use the
2722+
/// [`ptr::addr`] method instead, which has a similar effect, but doesn't
2723+
/// "expose" the pointer provenance. This improves optimisation potential.
2724+
/// See the docs of [`ptr::addr`] and [`ptr::expose_addr`] for more information
2725+
/// about exposing pointer provenance.
2726+
///
2727+
/// If your code can't comply with strict provenance and needs to expose
2728+
/// the provenance, then there is [`ptr::expose_addr`] as an escape hatch,
2729+
/// which preserves the behaviour of `as usize` casts while being explicit
2730+
/// about the semantics.
2731+
///
2732+
/// [issue #95228]: https://github.com/rust-lang/rust/issues/95228
2733+
/// [`ptr::addr`]: https://doc.rust-lang.org/core/ptr/fn.addr
2734+
/// [`ptr::expose_addr`]: https://doc.rust-lang.org/core/ptr/fn.expose_addr
2735+
pub LOSSY_PROVENANCE_CASTS,
2736+
Allow,
2737+
"a lossy pointer to integer cast is used",
2738+
@feature_gate = sym::strict_provenance;
26842739
}
26852740

26862741
declare_lint! {
@@ -3137,6 +3192,7 @@ declare_lint_pass! {
31373192
INCOMPLETE_INCLUDE,
31383193
CENUM_IMPL_DROP_CAST,
31393194
FUZZY_PROVENANCE_CASTS,
3195+
LOSSY_PROVENANCE_CASTS,
31403196
CONST_EVALUATABLE_UNCHECKED,
31413197
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
31423198
MUST_NOT_SUSPEND,

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,7 @@ symbols! {
13481348
str_trim,
13491349
str_trim_end,
13501350
str_trim_start,
1351+
strict_provenance,
13511352
stringify,
13521353
stringify_macro,
13531354
struct_field_attributes,

compiler/rustc_typeck/src/check/cast.rs

+42-19
Original file line numberDiff line numberDiff line change
@@ -809,12 +809,12 @@ impl<'a, 'tcx> CastCheck<'tcx> {
809809
(Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast
810810

811811
// ptr-addr-cast
812-
(Ptr(m_expr), Int(_)) => {
813-
self.fuzzy_provenance_ptr2int_lint(fcx, t_from);
812+
(Ptr(m_expr), Int(t_c)) => {
813+
self.lossy_provenance_ptr2int_lint(fcx, t_c);
814814
self.check_ptr_addr_cast(fcx, m_expr)
815815
}
816816
(FnPtr, Int(_)) => {
817-
self.fuzzy_provenance_ptr2int_lint(fcx, t_from);
817+
// FIXME(#95489): there should eventually be a lint for these casts
818818
Ok(CastKind::FnPtrAddrCast)
819819
}
820820
// addr-ptr-cast
@@ -945,7 +945,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
945945
fcx: &FnCtxt<'a, 'tcx>,
946946
m_cast: TypeAndMut<'tcx>,
947947
) -> Result<CastKind, CastError> {
948-
self.fuzzy_provenance_int2ptr_lint(fcx);
949948
// ptr-addr cast. pointer must be thin.
950949
match fcx.pointer_kind(m_cast.ty, self.span)? {
951950
None => Err(CastError::UnknownCastPtrKind),
@@ -986,25 +985,36 @@ impl<'a, 'tcx> CastCheck<'tcx> {
986985
}
987986
}
988987

989-
fn fuzzy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_from: CastTy<'tcx>) {
988+
fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
990989
fcx.tcx.struct_span_lint_hir(
991-
lint::builtin::FUZZY_PROVENANCE_CASTS,
990+
lint::builtin::LOSSY_PROVENANCE_CASTS,
992991
self.expr.hir_id,
993992
self.span,
994993
|err| {
995994
let mut err = err.build(&format!(
996-
"strict provenance disallows casting pointer `{}` to integer `{}`",
995+
"under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`",
997996
self.expr_ty, self.cast_ty
998997
));
999998

1000-
if let CastTy::FnPtr = t_from {
1001-
err.help(
1002-
"use `(... as *const u8).addr()` to obtain \
1003-
the address of a function pointer",
999+
let msg = "use `.addr()` to obtain the address of a pointer";
1000+
if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) {
1001+
let scalar_cast = match t_c {
1002+
ty::cast::IntTy::U(ty::UintTy::Usize) => String::new(),
1003+
_ => format!(" as {}", self.cast_ty),
1004+
};
1005+
err.span_suggestion(
1006+
self.span,
1007+
msg,
1008+
format!("({}).addr(){}", snippet, scalar_cast),
1009+
Applicability::MaybeIncorrect
10041010
);
10051011
} else {
1006-
err.help("use `.addr()` to obtain the address of a pointer");
1012+
err.help(msg);
10071013
}
1014+
err.help(
1015+
"if you can't comply with strict provenance and need to expose the pointer\
1016+
provenance you can use `.expose_addr()` instead"
1017+
);
10081018

10091019
err.emit();
10101020
},
@@ -1017,15 +1027,28 @@ impl<'a, 'tcx> CastCheck<'tcx> {
10171027
self.expr.hir_id,
10181028
self.span,
10191029
|err| {
1020-
err.build(&format!(
1030+
1031+
let mut err = err.build(&format!(
10211032
"strict provenance disallows casting integer `{}` to pointer `{}`",
10221033
self.expr_ty, self.cast_ty
1023-
))
1024-
.help(
1025-
"use `.with_addr(...)` to adjust a valid pointer \
1026-
in the same allocation, to this address",
1027-
)
1028-
.emit();
1034+
));
1035+
let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address";
1036+
if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) {
1037+
err.span_suggestion(
1038+
self.span,
1039+
msg,
1040+
format!("(...).with_addr({})", snippet),
1041+
Applicability::HasPlaceholders,
1042+
);
1043+
} else {
1044+
err.help(msg);
1045+
}
1046+
err.help(
1047+
"if you can't comply with strict provenance and don't have a pointer with \
1048+
the correct provenance you can use `std::ptr::from_exposed_addr()` instead"
1049+
);
1050+
1051+
err.emit();
10291052
},
10301053
);
10311054
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# `strict_provenance`
2+
3+
The tracking issue for this feature is: [#95228]
4+
5+
[#95228]: https://github.com/rust-lang/rust/issues/95228
6+
-----
7+
8+
The `strict_provenance` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints.
9+
These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model.
10+
The same feature gate is also used for the experimental strict provenance API in `std` (actually `core`).
11+
12+
## Example
13+
14+
```rust
15+
#![feature(strict_provenance)]
16+
#![warn(fuzzy_provenance_casts)]
17+
18+
fn main() {
19+
let _dangling = 16_usize as *const u8;
20+
//~^ WARNING: strict provenance disallows casting integer `usize` to pointer `*const u8`
21+
}
22+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// check-pass
2+
3+
#![deny(fuzzy_provenance_casts)]
4+
//~^ WARNING unknown lint: `fuzzy_provenance_casts`
5+
//~| WARNING unknown lint: `fuzzy_provenance_casts`
6+
//~| WARNING unknown lint: `fuzzy_provenance_casts`
7+
#![deny(lossy_provenance_casts)]
8+
//~^ WARNING unknown lint: `lossy_provenance_casts`
9+
//~| WARNING unknown lint: `lossy_provenance_casts`
10+
//~| WARNING unknown lint: `lossy_provenance_casts`
11+
12+
fn main() {
13+
// no warnings emitted since the lints are not activated
14+
15+
let _dangling = 16_usize as *const u8;
16+
17+
let x: u8 = 37;
18+
let _addr: usize = &x as *const u8 as usize;
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
warning: unknown lint: `fuzzy_provenance_casts`
2+
--> $DIR/feature-gate-strict_provenance.rs:3:1
3+
|
4+
LL | #![deny(fuzzy_provenance_casts)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(unknown_lints)]` on by default
8+
= note: the `fuzzy_provenance_casts` lint is unstable
9+
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
10+
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable
11+
12+
warning: unknown lint: `lossy_provenance_casts`
13+
--> $DIR/feature-gate-strict_provenance.rs:7:1
14+
|
15+
LL | #![deny(lossy_provenance_casts)]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
17+
|
18+
= note: the `lossy_provenance_casts` lint is unstable
19+
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
20+
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable
21+
22+
warning: unknown lint: `fuzzy_provenance_casts`
23+
--> $DIR/feature-gate-strict_provenance.rs:3:1
24+
|
25+
LL | #![deny(fuzzy_provenance_casts)]
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
27+
|
28+
= note: the `fuzzy_provenance_casts` lint is unstable
29+
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
30+
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable
31+
32+
warning: unknown lint: `lossy_provenance_casts`
33+
--> $DIR/feature-gate-strict_provenance.rs:7:1
34+
|
35+
LL | #![deny(lossy_provenance_casts)]
36+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
37+
|
38+
= note: the `lossy_provenance_casts` lint is unstable
39+
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
40+
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable
41+
42+
warning: unknown lint: `fuzzy_provenance_casts`
43+
--> $DIR/feature-gate-strict_provenance.rs:3:1
44+
|
45+
LL | #![deny(fuzzy_provenance_casts)]
46+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
47+
|
48+
= note: the `fuzzy_provenance_casts` lint is unstable
49+
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
50+
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable
51+
52+
warning: unknown lint: `lossy_provenance_casts`
53+
--> $DIR/feature-gate-strict_provenance.rs:7:1
54+
|
55+
LL | #![deny(lossy_provenance_casts)]
56+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
57+
|
58+
= note: the `lossy_provenance_casts` lint is unstable
59+
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
60+
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable
61+
62+
warning: 6 warnings emitted
63+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![feature(strict_provenance)]
2+
#![deny(fuzzy_provenance_casts)]
3+
4+
fn main() {
5+
let dangling = 16_usize as *const u8;
6+
//~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const u8`
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: strict provenance disallows casting integer `usize` to pointer `*const u8`
2+
--> $DIR/lint-strict-provenance-fuzzy-casts.rs:5:20
3+
|
4+
LL | let dangling = 16_usize as *const u8;
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/lint-strict-provenance-fuzzy-casts.rs:2:9
9+
|
10+
LL | #![deny(fuzzy_provenance_casts)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^
12+
= 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
13+
help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address
14+
|
15+
LL | let dangling = (...).with_addr(16_usize);
16+
| ~~~~~~~~~~~~~~~~~~~~~~~~~
17+
18+
error: aborting due to previous error
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(strict_provenance)]
2+
#![deny(lossy_provenance_casts)]
3+
4+
fn main() {
5+
let x: u8 = 37;
6+
let addr: usize = &x as *const u8 as usize;
7+
//~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize`
8+
9+
let addr_32bit = &x as *const u8 as u32;
10+
//~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32`
11+
}

0 commit comments

Comments
 (0)