Skip to content

Commit 525438b

Browse files
authored
Rollup merge of #95599 - niluxv:strict-provenance-lint, r=michaelwoerister
Strict provenance lints See #95488. This PR introduces two unstable (allow by default) lints to which lint on int2ptr and ptr2int casts, as the former is not possible in the strict provenance model and the latter can be written nicer using the `.addr()` API. Based on an initial version of the lint by ```@Gankra``` in #95199.
2 parents 9010879 + 98a4834 commit 525438b

File tree

11 files changed

+342
-4
lines changed

11 files changed

+342
-4
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

+92
Original file line numberDiff line numberDiff line change
@@ -2648,6 +2648,96 @@ declare_lint! {
26482648
};
26492649
}
26502650

2651+
declare_lint! {
2652+
/// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer
2653+
/// and a pointer.
2654+
///
2655+
/// ### Example
2656+
///
2657+
/// ```rust
2658+
/// #![feature(strict_provenance)]
2659+
/// #![warn(fuzzy_provenance_casts)]
2660+
///
2661+
/// fn main() {
2662+
/// let _dangling = 16_usize as *const u8;
2663+
/// }
2664+
/// ```
2665+
///
2666+
/// {{produces}}
2667+
///
2668+
/// ### Explanation
2669+
///
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).
2680+
///
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`].
2685+
///
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
2689+
pub FUZZY_PROVENANCE_CASTS,
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;
2739+
}
2740+
26512741
declare_lint! {
26522742
/// The `const_evaluatable_unchecked` lint detects a generic constant used
26532743
/// in a type.
@@ -3101,6 +3191,8 @@ declare_lint_pass! {
31013191
UNSAFE_OP_IN_UNSAFE_FN,
31023192
INCOMPLETE_INCLUDE,
31033193
CENUM_IMPL_DROP_CAST,
3194+
FUZZY_PROVENANCE_CASTS,
3195+
LOSSY_PROVENANCE_CASTS,
31043196
CONST_EVALUATABLE_UNCHECKED,
31053197
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
31063198
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

+83-4
Original file line numberDiff line numberDiff line change
@@ -807,11 +807,22 @@ impl<'a, 'tcx> CastCheck<'tcx> {
807807

808808
// ptr -> *
809809
(Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast
810-
(Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), // ptr-addr-cast
811-
(FnPtr, Int(_)) => Ok(CastKind::FnPtrAddrCast),
812810

813-
// * -> ptr
814-
(Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // addr-ptr-cast
811+
// ptr-addr-cast
812+
(Ptr(m_expr), Int(t_c)) => {
813+
self.lossy_provenance_ptr2int_lint(fcx, t_c);
814+
self.check_ptr_addr_cast(fcx, m_expr)
815+
}
816+
(FnPtr, Int(_)) => {
817+
// FIXME(#95489): there should eventually be a lint for these casts
818+
Ok(CastKind::FnPtrAddrCast)
819+
}
820+
// addr-ptr-cast
821+
(Int(_), Ptr(mt)) => {
822+
self.fuzzy_provenance_int2ptr_lint(fcx);
823+
self.check_addr_ptr_cast(fcx, mt)
824+
}
825+
// fn-ptr-cast
815826
(FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt),
816827

817828
// prim -> prim
@@ -973,6 +984,74 @@ impl<'a, 'tcx> CastCheck<'tcx> {
973984
}
974985
}
975986
}
987+
988+
fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
989+
fcx.tcx.struct_span_lint_hir(
990+
lint::builtin::LOSSY_PROVENANCE_CASTS,
991+
self.expr.hir_id,
992+
self.span,
993+
|err| {
994+
let mut err = err.build(&format!(
995+
"under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`",
996+
self.expr_ty, self.cast_ty
997+
));
998+
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
1010+
);
1011+
} else {
1012+
err.help(msg);
1013+
}
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+
);
1018+
1019+
err.emit();
1020+
},
1021+
);
1022+
}
1023+
1024+
fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
1025+
fcx.tcx.struct_span_lint_hir(
1026+
lint::builtin::FUZZY_PROVENANCE_CASTS,
1027+
self.expr.hir_id,
1028+
self.span,
1029+
|err| {
1030+
1031+
let mut err = err.build(&format!(
1032+
"strict provenance disallows casting integer `{}` to pointer `{}`",
1033+
self.expr_ty, self.cast_ty
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();
1052+
},
1053+
);
1054+
}
9761055
}
9771056

9781057
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize`
2+
--> $DIR/lint-strict-provenance-lossy-casts.rs:6:23
3+
|
4+
LL | let addr: usize = &x as *const u8 as usize;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.addr()` to obtain the address of a pointer: `(&x as *const u8).addr()`
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/lint-strict-provenance-lossy-casts.rs:2:9
9+
|
10+
LL | #![deny(lossy_provenance_casts)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^
12+
= help: if you can't comply with strict provenance and need to expose the pointerprovenance you can use `.expose_addr()` instead
13+
14+
error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32`
15+
--> $DIR/lint-strict-provenance-lossy-casts.rs:9:22
16+
|
17+
LL | let addr_32bit = &x as *const u8 as u32;
18+
| ^^^^^^^^^^^^^^^^^^^^^^ help: use `.addr()` to obtain the address of a pointer: `(&x as *const u8).addr() as u32`
19+
|
20+
= help: if you can't comply with strict provenance and need to expose the pointerprovenance you can use `.expose_addr()` instead
21+
22+
error: aborting due to 2 previous errors
23+

0 commit comments

Comments
 (0)