Skip to main content

rustc_lint/
fuzzy_provenance_casts.rs

1use rustc_hir as hir;
2use rustc_session::{declare_lint, declare_lint_pass};
3
4use crate::lints::{LossyProvenanceInt2Ptr, LossyProvenanceInt2PtrSuggestion};
5use crate::{LateContext, LateLintPass};
6
7#[doc =
r" The `fuzzy_provenance_casts` lint detects an `as` cast between an integer"]
#[doc = r" and a pointer."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" #![feature(strict_provenance_lints)]"]
#[doc = r" #![warn(fuzzy_provenance_casts)]"]
#[doc = r""]
#[doc = r" fn main() {"]
#[doc = r"     let _dangling = 16_usize as *const u8;"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" This lint is part of the strict provenance effort, see [issue #95228]."]
#[doc =
r" Casting an integer to a pointer is considered bad style, as a pointer"]
#[doc =
r" contains, besides the *address* also a *provenance*, indicating what"]
#[doc =
r" memory the pointer is allowed to read/write. Casting an integer, which"]
#[doc =
r" doesn't have provenance, to a pointer requires the compiler to assign"]
#[doc =
r#" (guess) provenance. The compiler assigns "all exposed valid" (see the"#]
#[doc =
r" docs of [`ptr::with_exposed_provenance`] for more information about this"]
#[doc =
r#" "exposing"). This penalizes the optimiser and is not well suited for"#]
#[doc = r" dynamic analysis/dynamic program verification (e.g. Miri or CHERI"]
#[doc = r" platforms)."]
#[doc = r""]
#[doc =
r" It is much better to use [`ptr::with_addr`] instead to specify the"]
#[doc =
r" provenance you want. If using this function is not possible because the"]
#[doc =
r" code relies on exposed provenance then there is as an escape hatch"]
#[doc = r" [`ptr::with_exposed_provenance`]."]
#[doc = r""]
#[doc = r" [issue #95228]: https://github.com/rust-lang/rust/issues/95228"]
#[doc =
r" [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr"]
#[doc =
r" [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html"]
pub static FUZZY_PROVENANCE_CASTS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "FUZZY_PROVENANCE_CASTS",
            default_level: ::rustc_lint_defs::Allow,
            desc: "a fuzzy integer to pointer cast is used",
            is_externally_loaded: false,
            feature_gate: Some(rustc_span::sym::strict_provenance_lints),
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
8    /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer
9    /// and a pointer.
10    ///
11    /// ### Example
12    ///
13    /// ```rust
14    /// #![feature(strict_provenance_lints)]
15    /// #![warn(fuzzy_provenance_casts)]
16    ///
17    /// fn main() {
18    ///     let _dangling = 16_usize as *const u8;
19    /// }
20    /// ```
21    ///
22    /// {{produces}}
23    ///
24    /// ### Explanation
25    ///
26    /// This lint is part of the strict provenance effort, see [issue #95228].
27    /// Casting an integer to a pointer is considered bad style, as a pointer
28    /// contains, besides the *address* also a *provenance*, indicating what
29    /// memory the pointer is allowed to read/write. Casting an integer, which
30    /// doesn't have provenance, to a pointer requires the compiler to assign
31    /// (guess) provenance. The compiler assigns "all exposed valid" (see the
32    /// docs of [`ptr::with_exposed_provenance`] for more information about this
33    /// "exposing"). This penalizes the optimiser and is not well suited for
34    /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI
35    /// platforms).
36    ///
37    /// It is much better to use [`ptr::with_addr`] instead to specify the
38    /// provenance you want. If using this function is not possible because the
39    /// code relies on exposed provenance then there is as an escape hatch
40    /// [`ptr::with_exposed_provenance`].
41    ///
42    /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228
43    /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr
44    /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html
45    pub FUZZY_PROVENANCE_CASTS,
46    Allow,
47    "a fuzzy integer to pointer cast is used",
48    @feature_gate = strict_provenance_lints;
49}
50
51#[doc = r" Lint for `as` casts between an integer and a pointer."]
pub struct FuzzyProvenanceCasts;
#[automatically_derived]
impl ::core::marker::Copy for FuzzyProvenanceCasts { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for FuzzyProvenanceCasts { }
#[automatically_derived]
impl ::core::clone::Clone for FuzzyProvenanceCasts {
    #[inline]
    fn clone(&self) -> FuzzyProvenanceCasts { *self }
}
impl ::rustc_lint_defs::LintPass for FuzzyProvenanceCasts {
    fn name(&self) -> &'static str { "FuzzyProvenanceCasts" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [FUZZY_PROVENANCE_CASTS]))
    }
}
impl FuzzyProvenanceCasts {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [FUZZY_PROVENANCE_CASTS]))
    }
}declare_lint_pass!(
52    /// Lint for `as` casts between an integer and a pointer.
53    FuzzyProvenanceCasts => [FUZZY_PROVENANCE_CASTS]
54);
55
56impl<'tcx> LateLintPass<'tcx> for FuzzyProvenanceCasts {
57    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
58        let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return };
59
60        let typeck_results = cx.typeck_results();
61        // Only lint casts from integer to pointer
62        let cast_from_ty = typeck_results.expr_ty(cast_from_expr);
63        if !cast_from_ty.is_integral() {
64            return;
65        }
66        let cast_to_ty = typeck_results.expr_ty(expr);
67        if !cast_to_ty.is_raw_ptr() {
68            return;
69        }
70
71        let sugg =
72            expr.span.can_be_used_for_suggestions().then(|| LossyProvenanceInt2PtrSuggestion {
73                lo: cast_from_expr.span.shrink_to_lo(),
74                hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span),
75            });
76        let lint = LossyProvenanceInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg };
77        cx.tcx.emit_node_span_lint(FUZZY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint)
78    }
79}