Skip to content

Commit 9e54f88

Browse files
committed
Auto merge of #145804 - Zalathar:rollup-nyb71ex, r=Zalathar
Rollup of 6 pull requests Successful merges: - #144531 (Add lint against integer to pointer transmutes) - #144885 (Implement some more checks in `ptr_guaranteed_cmp`. ) - #145307 (Fix `LazyLock` poison panic message) - #145554 (rustc-dev-guide subtree update) - #145798 (Use unnamed lifetime spans as primary spans for `MISMATCHED_LIFETIME_SYNTAXES`) - #145799 (std/src/lib.rs: mention "search button" instead of "search bar") r? `@ghost` `@rustbot` modify labels: rollup
2 parents f6d2341 + 7c9bd95 commit 9e54f88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+999
-291
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -280,22 +280,110 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
280280
interp_ok(match (a, b) {
281281
// Comparisons between integers are always known.
282282
(Scalar::Int(a), Scalar::Int(b)) => (a == b) as u8,
283-
// Comparisons of null with an arbitrary scalar can be known if `scalar_may_be_null`
284-
// indicates that the scalar can definitely *not* be null.
285-
(Scalar::Int(int), ptr) | (ptr, Scalar::Int(int))
286-
if int.is_null() && !self.scalar_may_be_null(ptr)? =>
287-
{
288-
0
283+
// Comparing a pointer `ptr` with an integer `int` is equivalent to comparing
284+
// `ptr-int` with null, so we can reduce this case to a `scalar_may_be_null` test.
285+
(Scalar::Int(int), Scalar::Ptr(ptr, _)) | (Scalar::Ptr(ptr, _), Scalar::Int(int)) => {
286+
let int = int.to_target_usize(*self.tcx);
287+
// The `wrapping_neg` here may produce a value that is not
288+
// a valid target usize any more... but `wrapping_offset` handles that correctly.
289+
let offset_ptr = ptr.wrapping_offset(Size::from_bytes(int.wrapping_neg()), self);
290+
if !self.scalar_may_be_null(Scalar::from_pointer(offset_ptr, self))? {
291+
// `ptr.wrapping_sub(int)` is definitely not equal to `0`, so `ptr != int`
292+
0
293+
} else {
294+
// `ptr.wrapping_sub(int)` could be equal to `0`, but might not be,
295+
// so we cannot know for sure if `ptr == int` or not
296+
2
297+
}
298+
}
299+
(Scalar::Ptr(a, _), Scalar::Ptr(b, _)) => {
300+
let (a_prov, a_offset) = a.prov_and_relative_offset();
301+
let (b_prov, b_offset) = b.prov_and_relative_offset();
302+
let a_allocid = a_prov.alloc_id();
303+
let b_allocid = b_prov.alloc_id();
304+
let a_info = self.get_alloc_info(a_allocid);
305+
let b_info = self.get_alloc_info(b_allocid);
306+
307+
// Check if the pointers cannot be equal due to alignment
308+
if a_info.align > Align::ONE && b_info.align > Align::ONE {
309+
let min_align = Ord::min(a_info.align.bytes(), b_info.align.bytes());
310+
let a_residue = a_offset.bytes() % min_align;
311+
let b_residue = b_offset.bytes() % min_align;
312+
if a_residue != b_residue {
313+
// If the two pointers have a different residue modulo their
314+
// common alignment, they cannot be equal.
315+
return interp_ok(0);
316+
}
317+
// The pointers have the same residue modulo their common alignment,
318+
// so they could be equal. Try the other checks.
319+
}
320+
321+
if let (Some(GlobalAlloc::Static(a_did)), Some(GlobalAlloc::Static(b_did))) = (
322+
self.tcx.try_get_global_alloc(a_allocid),
323+
self.tcx.try_get_global_alloc(b_allocid),
324+
) {
325+
if a_allocid == b_allocid {
326+
debug_assert_eq!(
327+
a_did, b_did,
328+
"different static item DefIds had same AllocId? {a_allocid:?} == {b_allocid:?}, {a_did:?} != {b_did:?}"
329+
);
330+
// Comparing two pointers into the same static. As per
331+
// https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.intro
332+
// a static cannot be duplicated, so if two pointers are into the same
333+
// static, they are equal if and only if their offsets are equal.
334+
(a_offset == b_offset) as u8
335+
} else {
336+
debug_assert_ne!(
337+
a_did, b_did,
338+
"same static item DefId had two different AllocIds? {a_allocid:?} != {b_allocid:?}, {a_did:?} == {b_did:?}"
339+
);
340+
// Comparing two pointers into the different statics.
341+
// We can never determine for sure that two pointers into different statics
342+
// are *equal*, but we can know that they are *inequal* if they are both
343+
// strictly in-bounds (i.e. in-bounds and not one-past-the-end) of
344+
// their respective static, as different non-zero-sized statics cannot
345+
// overlap or be deduplicated as per
346+
// https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.intro
347+
// (non-deduplication), and
348+
// https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.storage-disjointness
349+
// (non-overlapping).
350+
if a_offset < a_info.size && b_offset < b_info.size {
351+
0
352+
} else {
353+
// Otherwise, conservatively say we don't know.
354+
// There are some cases we could still return `0` for, e.g.
355+
// if the pointers being equal would require their statics to overlap
356+
// one or more bytes, but for simplicity we currently only check
357+
// strictly in-bounds pointers.
358+
2
359+
}
360+
}
361+
} else {
362+
// All other cases we conservatively say we don't know.
363+
//
364+
// For comparing statics to non-statics, as per https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.storage-disjointness
365+
// immutable statics can overlap with other kinds of allocations sometimes.
366+
//
367+
// FIXME: We could be more decisive for (non-zero-sized) mutable statics,
368+
// which cannot overlap with other kinds of allocations.
369+
//
370+
// Functions and vtables can be duplicated and deduplicated, so we
371+
// cannot be sure of runtime equality of pointers to the same one, or the
372+
// runtime inequality of pointers to different ones (see e.g. #73722),
373+
// so comparing those should return 2, whether they are the same allocation
374+
// or not.
375+
//
376+
// `GlobalAlloc::TypeId` exists mostly to prevent consteval from comparing
377+
// `TypeId`s, so comparing those should always return 2, whether they are the
378+
// same allocation or not.
379+
//
380+
// FIXME: We could revisit comparing pointers into the same
381+
// `GlobalAlloc::Memory` once https://github.com/rust-lang/rust/issues/128775
382+
// is fixed (but they can be deduplicated, so comparing pointers into different
383+
// ones should return 2).
384+
2
385+
}
289386
}
290-
// Other ways of comparing integers and pointers can never be known for sure.
291-
(Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => 2,
292-
// FIXME: return a `1` for when both sides are the same pointer, *except* that
293-
// some things (like functions and vtables) do not have stable addresses
294-
// so we need to be careful around them (see e.g. #73722).
295-
// FIXME: return `0` for at least some comparisons where we can reliably
296-
// determine the result of runtime inequality tests at compile-time.
297-
// Examples include comparison of addresses in different static items.
298-
(Scalar::Ptr(..), Scalar::Ptr(..)) => 2,
299387
})
300388
}
301389
}

compiler/rustc_lint/messages.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,14 @@ lint_invalid_reference_casting_note_book = for more information, visit <https://
462462
463463
lint_invalid_reference_casting_note_ty_has_interior_mutability = even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`
464464
465+
lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a pointer without provenance
466+
.note = this is dangerous because dereferencing the resulting pointer is undefined behavior
467+
.note_exposed_provenance = exposed provenance semantics can be used to create a pointer based on some previously exposed provenance
468+
.help_transmute = for more information about transmute, see <https://doc.rust-lang.org/std/mem/fn.transmute.html#transmutation-between-pointers-and-integers>
469+
.help_exposed_provenance = for more information about exposed provenance, see <https://doc.rust-lang.org/std/ptr/index.html#exposed-provenance>
470+
.suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance
471+
.suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut`
472+
465473
lint_legacy_derive_helpers = derive helper attribute is used before it is introduced
466474
.label = the attribute is introduced here
467475

compiler/rustc_lint/src/lifetime_syntax.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,9 @@ impl<T> LifetimeSyntaxCategories<Vec<T>> {
214214
}
215215
}
216216

217-
pub fn flatten(&self) -> impl Iterator<Item = &T> {
218-
let Self { hidden, elided, named } = self;
219-
[hidden.iter(), elided.iter(), named.iter()].into_iter().flatten()
217+
pub fn iter_unnamed(&self) -> impl Iterator<Item = &T> {
218+
let Self { hidden, elided, named: _ } = self;
219+
[hidden.iter(), elided.iter()].into_iter().flatten()
220220
}
221221
}
222222

@@ -495,7 +495,7 @@ fn emit_mismatch_diagnostic<'tcx>(
495495

496496
cx.emit_span_lint(
497497
MISMATCHED_LIFETIME_SYNTAXES,
498-
inputs.flatten().copied().collect::<Vec<_>>(),
498+
inputs.iter_unnamed().chain(outputs.iter_unnamed()).copied().collect::<Vec<_>>(),
499499
lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions },
500500
);
501501
}

compiler/rustc_lint/src/lints.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// ignore-tidy-filelength
2+
13
#![allow(rustc::untranslatable_diagnostic)]
24
use std::num::NonZero;
35

@@ -1542,6 +1544,48 @@ impl<'a> LintDiagnostic<'a, ()> for DropGlue<'_> {
15421544
}
15431545
}
15441546

1547+
// transmute.rs
1548+
#[derive(LintDiagnostic)]
1549+
#[diag(lint_int_to_ptr_transmutes)]
1550+
#[note]
1551+
#[note(lint_note_exposed_provenance)]
1552+
#[help(lint_suggestion_without_provenance_mut)]
1553+
#[help(lint_help_transmute)]
1554+
#[help(lint_help_exposed_provenance)]
1555+
pub(crate) struct IntegerToPtrTransmutes<'tcx> {
1556+
#[subdiagnostic]
1557+
pub suggestion: IntegerToPtrTransmutesSuggestion<'tcx>,
1558+
}
1559+
1560+
#[derive(Subdiagnostic)]
1561+
pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> {
1562+
#[multipart_suggestion(
1563+
lint_suggestion_with_exposed_provenance,
1564+
applicability = "machine-applicable",
1565+
style = "verbose"
1566+
)]
1567+
ToPtr {
1568+
dst: Ty<'tcx>,
1569+
suffix: &'static str,
1570+
#[suggestion_part(code = "std::ptr::with_exposed_provenance{suffix}::<{dst}>(")]
1571+
start_call: Span,
1572+
},
1573+
#[multipart_suggestion(
1574+
lint_suggestion_with_exposed_provenance,
1575+
applicability = "machine-applicable",
1576+
style = "verbose"
1577+
)]
1578+
ToRef {
1579+
dst: Ty<'tcx>,
1580+
suffix: &'static str,
1581+
ref_mutbl: &'static str,
1582+
#[suggestion_part(
1583+
code = "&{ref_mutbl}*std::ptr::with_exposed_provenance{suffix}::<{dst}>("
1584+
)]
1585+
start_call: Span,
1586+
},
1587+
}
1588+
15451589
// types.rs
15461590
#[derive(LintDiagnostic)]
15471591
#[diag(lint_range_endpoint_out_of_range)]

compiler/rustc_lint/src/transmute.rs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_ast::LitKind;
12
use rustc_errors::Applicability;
23
use rustc_hir::def::{DefKind, Res};
34
use rustc_hir::def_id::LocalDefId;
@@ -7,6 +8,7 @@ use rustc_middle::ty::{self, Ty};
78
use rustc_session::{declare_lint, impl_lint_pass};
89
use rustc_span::sym;
910

11+
use crate::lints::{IntegerToPtrTransmutes, IntegerToPtrTransmutesSuggestion};
1012
use crate::{LateContext, LateLintPass};
1113

1214
declare_lint! {
@@ -67,9 +69,44 @@ declare_lint! {
6769
"detects transmutes that can also be achieved by other operations"
6870
}
6971

72+
declare_lint! {
73+
/// The `integer_to_ptr_transmutes` lint detects integer to pointer
74+
/// transmutes where the resulting pointers are undefined behavior to dereference.
75+
///
76+
/// ### Example
77+
///
78+
/// ```rust
79+
/// fn foo(a: usize) -> *const u8 {
80+
/// unsafe {
81+
/// std::mem::transmute::<usize, *const u8>(a)
82+
/// }
83+
/// }
84+
/// ```
85+
///
86+
/// {{produces}}
87+
///
88+
/// ### Explanation
89+
///
90+
/// Any attempt to use the resulting pointers are undefined behavior as the resulting
91+
/// pointers won't have any provenance.
92+
///
93+
/// Alternatively, [`std::ptr::with_exposed_provenance`] should be used, as they do not
94+
/// carry the provenance requirement. If wanting to create pointers without provenance
95+
/// [`std::ptr::without_provenance`] should be used instead.
96+
///
97+
/// See [`std::mem::transmute`] in the reference for more details.
98+
///
99+
/// [`std::mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html
100+
/// [`std::ptr::with_exposed_provenance`]: https://doc.rust-lang.org/std/ptr/fn.with_exposed_provenance.html
101+
/// [`std::ptr::without_provenance`]: https://doc.rust-lang.org/std/ptr/fn.without_provenance.html
102+
pub INTEGER_TO_PTR_TRANSMUTES,
103+
Warn,
104+
"detects integer to pointer transmutes",
105+
}
106+
70107
pub(crate) struct CheckTransmutes;
71108

72-
impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES]);
109+
impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES, INTEGER_TO_PTR_TRANSMUTES]);
73110

74111
impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {
75112
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
@@ -94,9 +131,62 @@ impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {
94131

95132
check_ptr_transmute_in_const(cx, expr, body_owner_def_id, const_context, src, dst);
96133
check_unnecessary_transmute(cx, expr, callee, arg, const_context, src, dst);
134+
check_int_to_ptr_transmute(cx, expr, arg, src, dst);
97135
}
98136
}
99137

138+
/// Check for transmutes from integer to pointers (*const/*mut and &/&mut).
139+
///
140+
/// Using the resulting pointers would be undefined behavior.
141+
fn check_int_to_ptr_transmute<'tcx>(
142+
cx: &LateContext<'tcx>,
143+
expr: &'tcx hir::Expr<'tcx>,
144+
arg: &'tcx hir::Expr<'tcx>,
145+
src: Ty<'tcx>,
146+
dst: Ty<'tcx>,
147+
) {
148+
if !matches!(src.kind(), ty::Uint(_) | ty::Int(_)) {
149+
return;
150+
}
151+
let (ty::Ref(_, inner_ty, mutbl) | ty::RawPtr(inner_ty, mutbl)) = dst.kind() else {
152+
return;
153+
};
154+
// bail-out if the argument is literal 0 as we have other lints for those cases
155+
if matches!(arg.kind, hir::ExprKind::Lit(hir::Lit { node: LitKind::Int(v, _), .. }) if v == 0) {
156+
return;
157+
}
158+
// bail-out if the inner type is a ZST
159+
let Ok(layout_inner_ty) = cx.tcx.layout_of(cx.typing_env().as_query_input(*inner_ty)) else {
160+
return;
161+
};
162+
if layout_inner_ty.is_1zst() {
163+
return;
164+
}
165+
166+
let suffix = if mutbl.is_mut() { "_mut" } else { "" };
167+
cx.tcx.emit_node_span_lint(
168+
INTEGER_TO_PTR_TRANSMUTES,
169+
expr.hir_id,
170+
expr.span,
171+
IntegerToPtrTransmutes {
172+
suggestion: if dst.is_ref() {
173+
IntegerToPtrTransmutesSuggestion::ToRef {
174+
dst: *inner_ty,
175+
suffix,
176+
ref_mutbl: mutbl.prefix_str(),
177+
start_call: expr.span.shrink_to_lo().until(arg.span),
178+
}
179+
} else {
180+
IntegerToPtrTransmutesSuggestion::ToPtr {
181+
dst: *inner_ty,
182+
suffix,
183+
start_call: expr.span.shrink_to_lo().until(arg.span),
184+
}
185+
},
186+
},
187+
);
188+
}
189+
100190
/// Check for transmutes that exhibit undefined behavior.
101191
/// For example, transmuting pointers to integers in a const context.
102192
///

library/core/src/ptr/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,7 @@ pub const fn dangling<T>() -> *const T {
914914
#[must_use]
915915
#[stable(feature = "strict_provenance", since = "1.84.0")]
916916
#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")]
917+
#[allow(integer_to_ptr_transmutes)] // Expected semantics here.
917918
pub const fn without_provenance_mut<T>(addr: usize) -> *mut T {
918919
// An int-to-pointer transmute currently has exactly the intended semantics: it creates a
919920
// pointer without provenance. Note that this is *not* a stable guarantee about transmute

library/std/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//!
1616
//! If you already know the name of what you are looking for, the fastest way to
1717
//! find it is to use the <a href="#" onclick="window.searchState.focus();">search
18-
//! bar</a> at the top of the page.
18+
//! button</a> at the top of the page.
1919
//!
2020
//! Otherwise, you may want to jump to one of these useful sections:
2121
//!

library/std/src/sync/lazy_lock.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,11 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
244244
#[inline]
245245
#[stable(feature = "lazy_cell", since = "1.80.0")]
246246
pub fn force(this: &LazyLock<T, F>) -> &T {
247-
this.once.call_once(|| {
247+
this.once.call_once_force(|state| {
248+
if state.is_poisoned() {
249+
panic_poisoned();
250+
}
251+
248252
// SAFETY: `call_once` only runs this closure once, ever.
249253
let data = unsafe { &mut *this.data.get() };
250254
let f = unsafe { ManuallyDrop::take(&mut data.f) };
@@ -257,8 +261,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
257261
// * the closure was called and initialized `value`.
258262
// * the closure was called and panicked, so this point is never reached.
259263
// * the closure was not called, but a previous call initialized `value`.
260-
// * the closure was not called because the Once is poisoned, so this point
261-
// is never reached.
264+
// * the closure was not called because the Once is poisoned, which we handled above.
262265
// So `value` has definitely been initialized and will not be modified again.
263266
unsafe { &*(*this.data.get()).value }
264267
}

0 commit comments

Comments
 (0)