Skip to content

Commit b4486ca

Browse files
committed
Auto merge of #157575 - hanna-kruppe:merge-strict-provenance-lints, r=RalfJung
Merge and reframe strict_provenance_lints There does not seem to be any good reason to have separate lints for ptr2int and int2ptr casts. The main reason to enable this lint is to help with replacing `as` casts with strict provenance APIs (if possible) or explicit provenance APIs (when needed). That applies equally to both directions. Merging the lints requires coming up with new name and lint explanation. This is a good chance to reconsider the purpose and messaging of the lints which have been essentially unchanged since the early days of the "strict provenance experiment". For reasons described below, I called the merged lint `implicit_provenance_casts`, rewrote its explanation from scratch, and made some changes to diagnostics. First, the lint is not (only) about strict provenance any more. While strict provenance is often the best fix, migrating `as` casts to exposed provenance APIs also silences the lint. The exposed provenance APIs aren't any better for Miri and CHERI, but at least provenance considerations are made *explicit*, not picked up as side effect of the `as` keyword. (Banning use of exposed provenance APIs can be done with clippy's existing `disallowed_methods` lint.) Second, provenance is now officially part of the language and prominently explained in the `std::ptr` documentation. The new lint refers to those docs and is more consistent with them. Third, while strict provenance is encouraged whenever possible, exposed provenance is also part of the language and here to stay. Indeed, if we eventually enable the lint by default and make it `cargo fix`-able to keep the ecosystem migration manageable, we may want to suggest exposed provenance APIs to err on the side of not introducing UB. Thus, I made the diagnostics more neutral and descriptive. I've kept the suggestions that introduce strict provenance APIs, but we may want to reconsider this later. Tracking issue: #130351
2 parents 1182320 + 3d31306 commit b4486ca

28 files changed

Lines changed: 253 additions & 291 deletions

compiler/rustc_lint/src/fuzzy_provenance_casts.rs

Lines changed: 0 additions & 79 deletions
This file was deleted.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use rustc_ast::util::parser::ExprPrecedence;
2+
use rustc_hir as hir;
3+
use rustc_middle::ty::Ty;
4+
use rustc_session::{declare_lint, declare_lint_pass};
5+
6+
use crate::lints::{
7+
ImplicitProvenanceCastsInt2Ptr, ImplicitProvenanceCastsPtr2Int, Int2PtrSuggestion,
8+
Ptr2IntSuggestion,
9+
};
10+
use crate::{LateContext, LateLintPass};
11+
12+
declare_lint! {
13+
/// The `implicit_provenance_casts` lint detects integer-to-pointer and pointer-to-integer
14+
/// casts.
15+
///
16+
/// ### Example
17+
///
18+
/// ```rust
19+
/// #![feature(strict_provenance_lints)]
20+
/// #![warn(implicit_provenance_casts)]
21+
///
22+
/// fn main() {
23+
/// let x: u8 = 37;
24+
/// let addr: usize = &x as *const u8 as usize;
25+
/// let _ptr = addr as *const u8;
26+
/// }
27+
/// ```
28+
///
29+
/// {{produces}}
30+
///
31+
/// ### Explanation
32+
///
33+
/// This lint exists to help migrate code to [*Strict Provenance* APIs][strict-provenance] where
34+
/// possible, and make remaining uses of [*Exposed Provenance*][exposed-provenance] explicit.
35+
/// For more information on pointer provenance, see the [`std::ptr` documentation][provenance].
36+
///
37+
/// Earlier versions of Rust did not have a clear answer how integer-to-pointer and
38+
/// pointer-to-integer casts interact with provenance. Such casts are now defined to use the
39+
/// exposed provenance model, but in many cases the code can be updated to strict provenance
40+
/// APIs, which is preferable as it enables more precise reasoning about unsafe code, both by
41+
/// humans and by tools like [Miri].
42+
///
43+
/// However, there are situations where exposed provenance is required or following the strict
44+
/// provenance model requires major refactorings. In those cases, it's still useful to replace
45+
/// the `as` casts with equivalent explicit use of exposed provenance APIs and a comment
46+
/// explaining why they are needed.
47+
///
48+
/// [provenance]: https://doc.rust-lang.org/core/ptr/index.html#provenance
49+
/// [strict-provenance]: https://doc.rust-lang.org/core/ptr/index.html#strict-provenance
50+
/// [exposed-provenance]: https://doc.rust-lang.org/core/ptr/index.html#exposed-provenance
51+
/// [Miri]: https://github.com/rust-lang/miri
52+
pub IMPLICIT_PROVENANCE_CASTS,
53+
Allow,
54+
"an `as` cast relying on exposed provenance is used",
55+
@feature_gate = strict_provenance_lints;
56+
}
57+
58+
declare_lint_pass!(
59+
/// Lint for int2ptr and ptr2int `as` casts.
60+
ImplicitProvenanceCasts => [IMPLICIT_PROVENANCE_CASTS]
61+
);
62+
63+
impl<'tcx> LateLintPass<'tcx> for ImplicitProvenanceCasts {
64+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
65+
let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return };
66+
67+
let typeck_results = cx.typeck_results();
68+
let cast_from_ty = typeck_results.expr_ty(cast_from_expr);
69+
if cast_from_ty.is_raw_ptr() {
70+
let cast_to_ty = typeck_results.expr_ty(expr);
71+
if cast_to_ty.is_integral() {
72+
lint_ptr2int(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty)
73+
}
74+
} else if cast_from_ty.is_integral() {
75+
let cast_to_ty = typeck_results.expr_ty(expr);
76+
if cast_to_ty.is_raw_ptr() {
77+
lint_int2ptr(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty)
78+
}
79+
}
80+
}
81+
}
82+
83+
fn lint_ptr2int<'tcx>(
84+
cx: &LateContext<'tcx>,
85+
expr: &'tcx hir::Expr<'tcx>,
86+
cast_from_expr: &'tcx hir::Expr<'tcx>,
87+
cast_from_ty: Ty<'tcx>,
88+
cast_to_hir: &'tcx hir::Ty<'tcx>,
89+
cast_to_ty: Ty<'tcx>,
90+
) {
91+
let sugg = expr.span.can_be_used_for_suggestions().then(|| {
92+
let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous;
93+
let needs_cast = !cast_to_ty.is_usize();
94+
let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span);
95+
let expr_span = cast_from_expr.span.shrink_to_lo();
96+
match (needs_parens, needs_cast) {
97+
(true, true) => Ptr2IntSuggestion::NeedsParensCast { expr_span, cast_span, cast_to_ty },
98+
(true, false) => Ptr2IntSuggestion::NeedsParens { expr_span, cast_span },
99+
(false, true) => Ptr2IntSuggestion::NeedsCast { cast_span, cast_to_ty },
100+
(false, false) => Ptr2IntSuggestion::Other { cast_span },
101+
}
102+
});
103+
104+
let lint = ImplicitProvenanceCastsPtr2Int { cast_from_ty, cast_to_ty, sugg };
105+
cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint);
106+
}
107+
108+
fn lint_int2ptr<'tcx>(
109+
cx: &LateContext<'tcx>,
110+
expr: &'tcx hir::Expr<'tcx>,
111+
cast_from_expr: &'tcx hir::Expr<'tcx>,
112+
cast_from_ty: Ty<'tcx>,
113+
cast_to_hir: &'tcx hir::Ty<'tcx>,
114+
cast_to_ty: Ty<'tcx>,
115+
) {
116+
let sugg = expr.span.can_be_used_for_suggestions().then(|| Int2PtrSuggestion {
117+
lo: cast_from_expr.span.shrink_to_lo(),
118+
hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span),
119+
});
120+
let lint = ImplicitProvenanceCastsInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg };
121+
cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint)
122+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ mod expect;
4545
mod for_loops_over_fallibles;
4646
mod foreign_modules;
4747
mod function_cast_as_integer;
48-
mod fuzzy_provenance_casts;
4948
mod gpukernel_abi;
5049
mod if_let_rescope;
5150
mod impl_trait_overcaptures;
51+
mod implicit_provenance_casts;
5252
mod interior_mutable_consts;
5353
mod internal;
5454
mod invalid_from_utf8;
@@ -57,7 +57,6 @@ mod let_underscore;
5757
mod levels;
5858
pub mod lifetime_syntax;
5959
mod lints;
60-
mod lossy_provenance_casts;
6160
mod macro_expr_fragment_specifier_2024_migration;
6261
mod map_unit_fn;
6362
mod multiple_supertrait_upcastable;
@@ -95,16 +94,15 @@ use drop_forget_useless::*;
9594
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
9695
use for_loops_over_fallibles::*;
9796
use function_cast_as_integer::*;
98-
use fuzzy_provenance_casts::FuzzyProvenanceCasts;
9997
use gpukernel_abi::*;
10098
use if_let_rescope::IfLetRescope;
10199
use impl_trait_overcaptures::ImplTraitOvercaptures;
100+
use implicit_provenance_casts::ImplicitProvenanceCasts;
102101
use interior_mutable_consts::*;
103102
use internal::*;
104103
use invalid_from_utf8::*;
105104
use let_underscore::*;
106105
use lifetime_syntax::*;
107-
use lossy_provenance_casts::LossyProvenanceCasts;
108106
use macro_expr_fragment_specifier_2024_migration::*;
109107
use map_unit_fn::*;
110108
use multiple_supertrait_upcastable::*;
@@ -270,8 +268,7 @@ late_lint_methods!(
270268
CheckTransmutes: CheckTransmutes,
271269
LifetimeSyntax: LifetimeSyntax,
272270
InternalEqTraitMethodImpls: InternalEqTraitMethodImpls,
273-
FuzzyProvenanceCasts: FuzzyProvenanceCasts,
274-
LossyProvenanceCasts: LossyProvenanceCasts,
271+
ImplicitProvenanceCasts: ImplicitProvenanceCasts,
275272
]
276273
]
277274
);
@@ -410,6 +407,8 @@ fn register_builtins(store: &mut LintStore) {
410407
store.register_renamed("static_mut_ref", "static_mut_refs");
411408
store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries");
412409
store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes");
410+
store.register_renamed("fuzzy_provenance_casts", "implicit_provenance_casts");
411+
store.register_renamed("lossy_provenance_casts", "implicit_provenance_casts");
413412

414413
// These were moved to tool lints, but rustc still sees them when compiling normally, before
415414
// tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use

compiler/rustc_lint/src/lints.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,45 +2934,43 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
29342934
pub(crate) struct EqInternalMethodImplemented;
29352935

29362936
#[derive(Diagnostic)]
2937-
#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")]
2937+
#[diag("cast from `{$expr_ty}` to `{$cast_ty}` implicitly relies on exposed provenance")]
29382938
#[help(
2939-
"if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead"
2939+
"if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()`"
29402940
)]
2941-
pub(crate) struct LossyProvenanceInt2Ptr<'tcx> {
2941+
#[note("for more information, visit <https://doc.rust-lang.org/std/ptr/index.html#provenance>")]
2942+
pub(crate) struct ImplicitProvenanceCastsInt2Ptr<'tcx> {
29422943
pub expr_ty: Ty<'tcx>,
29432944
pub cast_ty: Ty<'tcx>,
29442945
#[subdiagnostic]
2945-
pub sugg: Option<LossyProvenanceInt2PtrSuggestion>,
2946+
pub sugg: Option<Int2PtrSuggestion>,
29462947
}
29472948

29482949
#[derive(Subdiagnostic)]
29492950
#[multipart_suggestion(
2950-
"use `.with_addr()` to adjust a valid pointer in the same allocation, to this address",
2951+
"use `.with_addr()` to adjust the address of a valid pointer in the same allocation",
29512952
applicability = "has-placeholders"
29522953
)]
2953-
pub(crate) struct LossyProvenanceInt2PtrSuggestion {
2954+
pub(crate) struct Int2PtrSuggestion {
29542955
#[suggestion_part(code = "(...).with_addr(")]
29552956
pub lo: Span,
29562957
#[suggestion_part(code = ")")]
29572958
pub hi: Span,
29582959
}
29592960

29602961
#[derive(Diagnostic)]
2961-
#[diag(
2962-
"under strict provenance it is considered bad style to cast pointer `{$cast_from_ty}` to integer `{$cast_to_ty}`"
2963-
)]
2964-
#[help(
2965-
"if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead"
2966-
)]
2967-
pub(crate) struct LossyProvenancePtr2Int<'tcx> {
2962+
#[diag("cast from `{$cast_from_ty}` to `{$cast_to_ty}` implicitly exposes pointer provenance")]
2963+
#[help("if conforming to strict provenance is not possible, use `.expose_provenance()`")]
2964+
#[note("for more information, visit <https://doc.rust-lang.org/std/ptr/index.html#provenance>")]
2965+
pub(crate) struct ImplicitProvenanceCastsPtr2Int<'tcx> {
29682966
pub cast_from_ty: Ty<'tcx>,
29692967
pub cast_to_ty: Ty<'tcx>,
29702968
#[subdiagnostic]
2971-
pub sugg: Option<LossyProvenancePtr2IntSuggestion<'tcx>>,
2969+
pub sugg: Option<Ptr2IntSuggestion<'tcx>>,
29722970
}
29732971

29742972
#[derive(Subdiagnostic)]
2975-
pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> {
2973+
pub(crate) enum Ptr2IntSuggestion<'tcx> {
29762974
#[multipart_suggestion(
29772975
"use `.addr()` to obtain the address of a pointer",
29782976
applicability = "maybe-incorrect"

0 commit comments

Comments
 (0)