diff --git a/bench/benches/imp/spinlock_fallback.rs b/bench/benches/imp/spinlock_fallback.rs index 9f173444..971836e8 100644 --- a/bench/benches/imp/spinlock_fallback.rs +++ b/bench/benches/imp/spinlock_fallback.rs @@ -15,6 +15,8 @@ use core::{ }; use super::fallback::utils::{Backoff, CachePadded}; +#[cfg(portable_atomic_no_strict_provenance)] +use crate::utils::ptr::PtrExt; struct Spinlock { state: AtomicUsize, @@ -106,7 +108,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); self.v.get().read() } } @@ -118,7 +120,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); self.v.get().write(val) } } @@ -128,7 +130,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(val); prev @@ -148,7 +150,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); if prev == current { self.v.get().write(new); @@ -176,7 +178,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(prev.wrapping_add(val)); prev @@ -188,7 +190,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(prev.wrapping_sub(val)); prev @@ -200,7 +202,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(prev & val); prev @@ -212,7 +214,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(!(prev & val)); prev @@ -224,7 +226,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(prev | val); prev @@ -236,7 +238,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(prev ^ val); prev @@ -248,7 +250,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(core::cmp::max(prev, val)); prev @@ -260,7 +262,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(core::cmp::min(prev, val)); prev @@ -272,7 +274,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(!prev); prev @@ -288,7 +290,7 @@ macro_rules! atomic_int { // SAFETY: any data races are prevented by the lock and the raw // pointer passed in is valid because we got it from a reference. unsafe { - let _guard = lock(self.v.get() as usize); + let _guard = lock(self.v.get().addr()); let prev = self.v.get().read(); self.v.get().write(prev.wrapping_neg()); prev diff --git a/build.rs b/build.rs index a9f6fb9f..b66cb569 100644 --- a/build.rs +++ b/build.rs @@ -53,7 +53,7 @@ fn main() { // Custom cfgs set by build script. Not public API. // grep -F 'cargo:rustc-cfg=' build.rs | grep -Ev '^ *//' | sed -E 's/^.*cargo:rustc-cfg=//; s/(=\\)?".*$//' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/' println!( - "cargo:rustc-check-cfg=cfg(portable_atomic_disable_fiq,portable_atomic_force_amo,portable_atomic_ll_sc_rmw,portable_atomic_new_atomic_intrinsics,portable_atomic_no_asm,portable_atomic_no_asm_maybe_uninit,portable_atomic_no_atomic_64,portable_atomic_no_atomic_cas,portable_atomic_no_atomic_load_store,portable_atomic_no_atomic_min_max,portable_atomic_no_cfg_target_has_atomic,portable_atomic_no_cmpxchg16b_intrinsic,portable_atomic_no_cmpxchg16b_target_feature,portable_atomic_no_const_mut_refs,portable_atomic_no_const_raw_ptr_deref,portable_atomic_no_const_transmute,portable_atomic_no_core_unwind_safe,portable_atomic_no_diagnostic_namespace,portable_atomic_no_offset_of,portable_atomic_no_stronger_failure_ordering,portable_atomic_no_track_caller,portable_atomic_no_unsafe_op_in_unsafe_fn,portable_atomic_pre_llvm_15,portable_atomic_pre_llvm_16,portable_atomic_pre_llvm_18,portable_atomic_s_mode,portable_atomic_sanitize_thread,portable_atomic_target_feature,portable_atomic_unsafe_assume_single_core,portable_atomic_unstable_asm,portable_atomic_unstable_asm_experimental_arch,portable_atomic_unstable_cfg_target_has_atomic,portable_atomic_unstable_isa_attribute)" + "cargo:rustc-check-cfg=cfg(portable_atomic_disable_fiq,portable_atomic_force_amo,portable_atomic_ll_sc_rmw,portable_atomic_new_atomic_intrinsics,portable_atomic_no_asm,portable_atomic_no_asm_maybe_uninit,portable_atomic_no_atomic_64,portable_atomic_no_atomic_cas,portable_atomic_no_atomic_load_store,portable_atomic_no_atomic_min_max,portable_atomic_no_cfg_target_has_atomic,portable_atomic_no_cmpxchg16b_intrinsic,portable_atomic_no_cmpxchg16b_target_feature,portable_atomic_no_const_mut_refs,portable_atomic_no_const_raw_ptr_deref,portable_atomic_no_const_transmute,portable_atomic_no_core_unwind_safe,portable_atomic_no_diagnostic_namespace,portable_atomic_no_offset_of,portable_atomic_no_strict_provenance,portable_atomic_no_stronger_failure_ordering,portable_atomic_no_track_caller,portable_atomic_no_unsafe_op_in_unsafe_fn,portable_atomic_pre_llvm_15,portable_atomic_pre_llvm_16,portable_atomic_pre_llvm_18,portable_atomic_s_mode,portable_atomic_sanitize_thread,portable_atomic_target_feature,portable_atomic_unsafe_assume_single_core,portable_atomic_unstable_asm,portable_atomic_unstable_asm_experimental_arch,portable_atomic_unstable_cfg_target_has_atomic,portable_atomic_unstable_isa_attribute)" ); // TODO: handle multi-line target_feature_fallback // grep -F 'target_feature_fallback("' build.rs | grep -Ev '^ *//' | sed -E 's/^.*target_feature_fallback\(//; s/",.*$/"/' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/' @@ -126,6 +126,10 @@ fn main() { if !version.probe(83, 2024, 9, 15) { println!("cargo:rustc-cfg=portable_atomic_no_const_mut_refs"); } + // strict_provenance/exposed_provenance APIs stabilized in Rust 1.84 (nightly-2024-10-22): https://github.com/rust-lang/rust/pull/130350 + if !version.probe(84, 2024, 10, 21) { + println!("cargo:rustc-cfg=portable_atomic_no_strict_provenance"); + } // asm! on AArch64, Arm, RISC-V, x86, and x86_64 stabilized in Rust 1.59 (nightly-2021-12-16): https://github.com/rust-lang/rust/pull/91728 let no_asm = !version.probe(59, 2021, 12, 15); diff --git a/portable-atomic-util/build.rs b/portable-atomic-util/build.rs index b0638d1e..0c214132 100644 --- a/portable-atomic-util/build.rs +++ b/portable-atomic-util/build.rs @@ -31,9 +31,9 @@ fn main() { if version.minor >= 80 { // Custom cfgs set by build script. Not public API. - // grep -F 'cargo:rustc-cfg=' build.rs | grep -Ev '^ *//' | sed -E 's/^.*cargo:rustc-cfg=//; s/(=\\)?".*$//' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/' + // grep -F 'cargo:rustc-cfg=' portable-atomic-util/build.rs | grep -Ev '^ *//' | sed -E 's/^.*cargo:rustc-cfg=//; s/(=\\)?".*$//' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/' println!( - "cargo:rustc-check-cfg=cfg(portable_atomic_no_alloc,portable_atomic_no_alloc_layout_extras,portable_atomic_no_core_unwind_safe,portable_atomic_no_error_in_core,portable_atomic_no_futures_api,portable_atomic_no_io_safety,portable_atomic_no_io_vec,portable_atomic_no_maybe_uninit,portable_atomic_no_min_const_generics,portable_atomic_no_track_caller,portable_atomic_no_unsafe_op_in_unsafe_fn,portable_atomic_sanitize_thread)" + "cargo:rustc-check-cfg=cfg(portable_atomic_no_alloc,portable_atomic_no_alloc_layout_extras,portable_atomic_no_core_unwind_safe,portable_atomic_no_error_in_core,portable_atomic_no_futures_api,portable_atomic_no_io_safety,portable_atomic_no_io_vec,portable_atomic_no_maybe_uninit,portable_atomic_no_min_const_generics,portable_atomic_no_strict_provenance,portable_atomic_no_track_caller,portable_atomic_no_unsafe_op_in_unsafe_fn,portable_atomic_sanitize_thread)" ); } @@ -85,6 +85,10 @@ fn main() { if !version.probe(81, 2024, 6, 8) { println!("cargo:rustc-cfg=portable_atomic_no_error_in_core"); } + // strict_provenance/exposed_provenance APIs stabilized in Rust 1.84 (nightly-2024-10-22): https://github.com/rust-lang/rust/pull/130350 + if !version.probe(84, 2024, 10, 21) { + println!("cargo:rustc-cfg=portable_atomic_no_strict_provenance"); + } if version.nightly { // `cfg(sanitize = "..")` is not stabilized. diff --git a/portable-atomic-util/src/arc.rs b/portable-atomic-util/src/arc.rs index 56f4c4b7..18ad0fb9 100644 --- a/portable-atomic-util/src/arc.rs +++ b/portable-atomic-util/src/arc.rs @@ -3020,7 +3020,7 @@ fn abort() -> ! { } fn is_dangling(ptr: *const T) -> bool { - ptr as *const () as usize == usize::MAX + (ptr as *const ()).addr() == usize::MAX } // Based on unstable alloc::alloc::Global. @@ -3061,8 +3061,18 @@ impl Global { } } -// TODO: use stabilized core::ptr strict_provenance helpers https://github.com/rust-lang/rust/pull/130350 +#[cfg(portable_atomic_no_strict_provenance)] +use self::strict::PtrExt; + +// strict_provenance polyfill for pre-1.84 rustc. mod strict { + #[cfg(portable_atomic_no_strict_provenance)] + use core::mem; + #[cfg(not(portable_atomic_no_strict_provenance))] + #[allow(unused_imports)] + pub(crate) use core::ptr::without_provenance_mut; + + #[cfg(portable_atomic_no_strict_provenance)] #[inline(always)] #[must_use] pub(super) const fn without_provenance_mut(addr: usize) -> *mut T { @@ -3073,7 +3083,7 @@ mod strict { // pointer). #[cfg(miri)] unsafe { - core::mem::transmute(addr) + mem::transmute(addr) } // const transmute requires Rust 1.56. #[cfg(not(miri))] @@ -3111,4 +3121,26 @@ mod strict { // SAFETY: the caller must uphold the safety contract for `sub`. unsafe { with_metadata_of((ptr as *mut u8).sub(count), ptr) } } + + #[cfg(portable_atomic_no_strict_provenance)] + pub(crate) trait PtrExt: Copy { + #[must_use] + fn addr(self) -> usize; + } + #[cfg(portable_atomic_no_strict_provenance)] + impl PtrExt for *const T { + #[must_use] + #[inline(always)] + fn addr(self) -> usize { + // A pointer-to-integer transmute currently has exactly the right semantics: it returns the + // address without exposing the provenance. Note that this is *not* a stable guarantee about + // transmute semantics, it relies on sysroot crates having special status. + // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the + // provenance). + #[allow(clippy::transmutes_expressible_as_ptr_casts)] + unsafe { + mem::transmute(self as *const ()) + } + } + } } diff --git a/portable-atomic-util/src/lib.rs b/portable-atomic-util/src/lib.rs index 64d9ab59..ae200e61 100644 --- a/portable-atomic-util/src/lib.rs +++ b/portable-atomic-util/src/lib.rs @@ -84,6 +84,7 @@ RUSTFLAGS="--cfg portable_atomic_unstable_coerce_unsized" cargo ... clippy::std_instead_of_alloc, clippy::std_instead_of_core, )] +#![cfg_attr(portable_atomic_no_strict_provenance, allow(unstable_name_collisions))] #![allow(clippy::inline_always)] // docs.rs only (cfg is enabled by docs.rs, not build script) #![cfg_attr(docsrs, feature(doc_cfg))] diff --git a/src/imp/atomic64/arm_linux.rs b/src/imp/atomic64/arm_linux.rs index 44561155..fda63ad4 100644 --- a/src/imp/atomic64/arm_linux.rs +++ b/src/imp/atomic64/arm_linux.rs @@ -42,7 +42,7 @@ fn __kuser_helper_version() -> i32 { // SAFETY: core assumes that at least __kuser_memory_barrier (__kuser_helper_version >= 3, // kernel version 2.6.15+) is available on this platform. __kuser_helper_version // is always available on such a platform. - v = unsafe { (KUSER_HELPER_VERSION as *const i32).read() }; + v = unsafe { crate::utils::ptr::with_exposed_provenance::(KUSER_HELPER_VERSION).read() }; CACHE.store(v, Ordering::Relaxed); v } @@ -61,7 +61,7 @@ unsafe fn __kuser_cmpxchg64(old_val: *const u64, new_val: *const u64, ptr: *mut // SAFETY: the caller must uphold the safety contract. unsafe { let f: extern "C" fn(*const u64, *const u64, *mut u64) -> u32 = - mem::transmute(KUSER_CMPXCHG64 as *const ()); + mem::transmute(crate::utils::ptr::with_exposed_provenance::<()>(KUSER_CMPXCHG64)); f(old_val, new_val, ptr) == 0 } } diff --git a/src/imp/fallback/mod.rs b/src/imp/fallback/mod.rs index c2286d0e..bef5a328 100644 --- a/src/imp/fallback/mod.rs +++ b/src/imp/fallback/mod.rs @@ -112,6 +112,8 @@ use self::{ seq_lock::{SeqLock, SeqLockWriteGuard}, utils::CachePadded, }; +#[cfg(portable_atomic_no_strict_provenance)] +use crate::utils::ptr::PtrExt; // Some 64-bit architectures have ABI with 32-bit pointer width (e.g., x86_64 X32 ABI, // AArch64 ILP32 ABI, mips64 N32 ABI). On those targets, AtomicU64 is fast, @@ -234,7 +236,7 @@ macro_rules! atomic { #[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)] pub(crate) fn load(&self, order: Ordering) -> $int_type { crate::utils::assert_load_ordering(order); - let lock = lock(self.v.get() as usize); + let lock = lock(self.v.get().addr()); // Try doing an optimistic read first. if let Some(stamp) = lock.optimistic_read() { @@ -257,13 +259,13 @@ macro_rules! atomic { #[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)] pub(crate) fn store(&self, val: $int_type, order: Ordering) { crate::utils::assert_store_ordering(order); - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); self.write(val, &guard) } #[inline] pub(crate) fn swap(&self, val: $int_type, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(val, &guard); prev @@ -279,7 +281,7 @@ macro_rules! atomic { failure: Ordering, ) -> Result<$int_type, $int_type> { crate::utils::assert_compare_exchange_ordering(success, failure); - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); if prev == current { self.write(new, &guard); @@ -305,7 +307,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_add(&self, val: $int_type, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(prev.wrapping_add(val), &guard); prev @@ -313,7 +315,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_sub(&self, val: $int_type, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(prev.wrapping_sub(val), &guard); prev @@ -321,7 +323,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_and(&self, val: $int_type, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(prev & val, &guard); prev @@ -329,7 +331,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_nand(&self, val: $int_type, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(!(prev & val), &guard); prev @@ -337,7 +339,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_or(&self, val: $int_type, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(prev | val, &guard); prev @@ -345,7 +347,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_xor(&self, val: $int_type, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(prev ^ val, &guard); prev @@ -353,7 +355,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_max(&self, val: $int_type, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(core::cmp::max(prev, val), &guard); prev @@ -361,7 +363,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_min(&self, val: $int_type, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(core::cmp::min(prev, val), &guard); prev @@ -369,7 +371,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_not(&self, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(!prev, &guard); prev @@ -381,7 +383,7 @@ macro_rules! atomic { #[inline] pub(crate) fn fetch_neg(&self, _order: Ordering) -> $int_type { - let guard = lock(self.v.get() as usize).write(); + let guard = lock(self.v.get().addr()).write(); let prev = self.read(&guard); self.write(prev.wrapping_neg(), &guard); prev diff --git a/src/lib.rs b/src/lib.rs index 48fa4c14..79cc38c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -212,6 +212,7 @@ RUSTFLAGS="--cfg portable_atomic_no_outline_atomics" cargo ... clippy::float_arithmetic, )] #![cfg_attr(not(portable_atomic_no_asm), warn(missing_docs))] // module-level #![allow(missing_docs)] doesn't work for macros on old rustc +#![cfg_attr(portable_atomic_no_strict_provenance, allow(unstable_name_collisions))] #![allow(clippy::inline_always, clippy::used_underscore_items)] // asm_experimental_arch // AVR, MSP430, and Xtensa are tier 3 platforms and require nightly anyway. @@ -525,8 +526,9 @@ pub mod hint { use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst}; use core::{fmt, ptr}; +#[cfg(portable_atomic_no_strict_provenance)] #[cfg(miri)] -use crate::utils::strict; +use crate::utils::ptr::PtrExt; cfg_has_atomic_8! { /// A boolean type which can be safely shared between threads. @@ -2135,11 +2137,13 @@ impl AtomicPtr { // use AtomicPtr::fetch_* in all cases from the version in which it is stabilized. #[cfg(miri)] { - self.fetch_update_(order, |x| strict::map_addr(x, |x| x.wrapping_add(val))) + self.fetch_update_(order, |x| x.with_addr(x.addr().wrapping_add(val))) } #[cfg(not(miri))] { - self.as_atomic_usize().fetch_add(val, order) as *mut T + crate::utils::ptr::with_exposed_provenance_mut( + self.as_atomic_usize().fetch_add(val, order) + ) } } @@ -2179,11 +2183,13 @@ impl AtomicPtr { // use AtomicPtr::fetch_* in all cases from the version in which it is stabilized. #[cfg(miri)] { - self.fetch_update_(order, |x| strict::map_addr(x, |x| x.wrapping_sub(val))) + self.fetch_update_(order, |x| x.with_addr(x.addr().wrapping_sub(val))) } #[cfg(not(miri))] { - self.as_atomic_usize().fetch_sub(val, order) as *mut T + crate::utils::ptr::with_exposed_provenance_mut( + self.as_atomic_usize().fetch_sub(val, order) + ) } } @@ -2238,11 +2244,13 @@ impl AtomicPtr { // use AtomicPtr::fetch_* in all cases from the version in which it is stabilized. #[cfg(miri)] { - self.fetch_update_(order, |x| strict::map_addr(x, |x| x | val)) + self.fetch_update_(order, |x| x.with_addr(x.addr() | val)) } #[cfg(not(miri))] { - self.as_atomic_usize().fetch_or(val, order) as *mut T + crate::utils::ptr::with_exposed_provenance_mut( + self.as_atomic_usize().fetch_or(val, order) + ) } } @@ -2295,11 +2303,13 @@ impl AtomicPtr { // use AtomicPtr::fetch_* in all cases from the version in which it is stabilized. #[cfg(miri)] { - self.fetch_update_(order, |x| strict::map_addr(x, |x| x & val)) + self.fetch_update_(order, |x| x.with_addr(x.addr() & val)) } #[cfg(not(miri))] { - self.as_atomic_usize().fetch_and(val, order) as *mut T + crate::utils::ptr::with_exposed_provenance_mut( + self.as_atomic_usize().fetch_and(val, order) + ) } } @@ -2351,11 +2361,13 @@ impl AtomicPtr { // use AtomicPtr::fetch_* in all cases from the version in which it is stabilized. #[cfg(miri)] { - self.fetch_update_(order, |x| strict::map_addr(x, |x| x ^ val)) + self.fetch_update_(order, |x| x.with_addr(x.addr() ^ val)) } #[cfg(not(miri))] { - self.as_atomic_usize().fetch_xor(val, order) as *mut T + crate::utils::ptr::with_exposed_provenance_mut( + self.as_atomic_usize().fetch_xor(val, order) + ) } } @@ -2399,7 +2411,7 @@ impl AtomicPtr { #[cfg(miri)] { let mask = 1_usize.wrapping_shl(bit); - self.fetch_or(mask, order) as usize & mask != 0 + self.fetch_or(mask, order).addr() & mask != 0 } #[cfg(not(miri))] { @@ -2444,7 +2456,7 @@ impl AtomicPtr { #[cfg(miri)] { let mask = 1_usize.wrapping_shl(bit); - self.fetch_and(!mask, order) as usize & mask != 0 + self.fetch_and(!mask, order).addr() & mask != 0 } #[cfg(not(miri))] { @@ -2489,7 +2501,7 @@ impl AtomicPtr { #[cfg(miri)] { let mask = 1_usize.wrapping_shl(bit); - self.fetch_xor(mask, order) as usize & mask != 0 + self.fetch_xor(mask, order).addr() & mask != 0 } #[cfg(not(miri))] { diff --git a/src/utils.rs b/src/utils.rs index 5bc06464..0ea60c26 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -413,6 +413,8 @@ type RetInt = u32; #[allow(dead_code)] #[inline] pub(crate) fn create_sub_word_mask_values(ptr: *mut T) -> (*mut MinWord, RetInt, RetInt) { + #[cfg(portable_atomic_no_strict_provenance)] + use self::ptr::PtrExt; use core::mem; // RISC-V, MIPS, SPARC, LoongArch, Xtensa: shift amount of 32-bit shift instructions is 5 bits unsigned (0-31). // PowerPC, C-SKY: shift amount of 32-bit shift instructions is 6 bits unsigned (0-63) and shift amount 32-63 means "clear". @@ -433,12 +435,12 @@ pub(crate) fn create_sub_word_mask_values(ptr: *mut T) -> (*mut MinWord, RetI target_arch = "xtensa", )); let ptr_mask = mem::size_of::() - 1; - let aligned_ptr = strict::with_addr(ptr, ptr as usize & !ptr_mask) as *mut MinWord; + let aligned_ptr = ptr.with_addr(ptr.addr() & !ptr_mask) as *mut MinWord; let ptr_lsb = if SHIFT_MASK { - ptr as usize & ptr_mask + ptr.addr() & ptr_mask } else { // We use 32-bit wrapping shift instructions in asm on these platforms. - ptr as usize + ptr.addr() }; let shift = if cfg!(any(target_endian = "little", target_arch = "s390x")) { ptr_lsb.wrapping_mul(8) @@ -452,24 +454,68 @@ pub(crate) fn create_sub_word_mask_values(ptr: *mut T) -> (*mut MinWord, RetI (aligned_ptr, shift as RetInt, mask) } -// TODO: use stabilized core::ptr strict_provenance helpers https://github.com/rust-lang/rust/pull/130350 -#[cfg(any(miri, target_arch = "riscv32", target_arch = "riscv64"))] +// strict_provenance polyfill for pre-1.84 rustc. #[allow(dead_code)] -pub(crate) mod strict { - #[inline] +pub(crate) mod ptr { + #[cfg(portable_atomic_no_strict_provenance)] + use core::mem; + #[cfg(not(portable_atomic_no_strict_provenance))] + #[allow(unused_imports)] + pub(crate) use core::ptr::{with_exposed_provenance, with_exposed_provenance_mut}; + + #[cfg(portable_atomic_no_strict_provenance)] #[must_use] - pub(crate) fn with_addr(ptr: *mut T, addr: usize) -> *mut T { - // This should probably be an intrinsic to avoid doing any sort of arithmetic, but - // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's - // provenance. - let offset = addr.wrapping_sub(ptr as usize); - (ptr as *mut u8).wrapping_add(offset) as *mut T + #[inline(always)] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub(crate) fn with_exposed_provenance(addr: usize) -> *const T { + addr as *const T } - - #[cfg(miri)] - #[inline] + #[cfg(portable_atomic_no_strict_provenance)] #[must_use] - pub(crate) fn map_addr(ptr: *mut T, f: impl FnOnce(usize) -> usize) -> *mut T { - with_addr(ptr, f(ptr as usize)) + #[inline(always)] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub(crate) fn with_exposed_provenance_mut(addr: usize) -> *mut T { + addr as *mut T + } + + #[cfg(portable_atomic_no_strict_provenance)] + pub(crate) trait PtrExt: Copy { + #[must_use] + fn addr(self) -> usize; + #[must_use] + fn with_addr(self, addr: usize) -> Self + where + T: Sized; + } + #[cfg(portable_atomic_no_strict_provenance)] + impl PtrExt for *mut T { + #[must_use] + #[inline(always)] + fn addr(self) -> usize { + // A pointer-to-integer transmute currently has exactly the right semantics: it returns the + // address without exposing the provenance. Note that this is *not* a stable guarantee about + // transmute semantics, it relies on sysroot crates having special status. + // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the + // provenance). + #[allow(clippy::transmutes_expressible_as_ptr_casts)] + unsafe { + mem::transmute(self as *mut ()) + } + } + #[allow(clippy::cast_possible_wrap)] + #[must_use] + #[inline] + fn with_addr(self, addr: usize) -> Self + where + T: Sized, + { + // This should probably be an intrinsic to avoid doing any sort of arithmetic, but + // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's + // provenance. + let self_addr = self.addr() as isize; + let dest_addr = addr as isize; + let offset = dest_addr.wrapping_sub(self_addr); + (self as *mut u8).wrapping_offset(offset) as *mut T + } } } diff --git a/tools/build.sh b/tools/build.sh index 15e7889c..f1e6b896 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -237,6 +237,7 @@ else fi nightly='' base_rustflags="${RUSTFLAGS:-}" +strict_provenance_lints='' if [[ "${rustc_version}" =~ nightly|dev ]]; then nightly=1 if [[ -z "${is_custom_toolchain}" ]]; then @@ -248,6 +249,7 @@ if [[ "${rustc_version}" =~ nightly|dev ]]; then retry rustup ${pre_args[@]+"${pre_args[@]}"} component add clippy &>/dev/null base_args=(hack "${subcmd}") base_rustflags+=' -Z crate-attr=feature(unqualified_local_imports) -W unqualified_local_imports' + strict_provenance_lints=' -Z crate-attr=feature(strict_provenance_lints) -W fuzzy_provenance_casts' fi fi export CARGO_TARGET_DIR="${target_dir}" @@ -283,6 +285,9 @@ build() { if grep -Eq "^${target}$" <<<"${rustup_target_list}"; then cfgs=$(RUSTC_BOOTSTRAP=1 rustc ${pre_args[@]+"${pre_args[@]}"} --print cfg "${target_flags[@]}") retry rustup ${pre_args[@]+"${pre_args[@]}"} target add "${target}" &>/dev/null + # core/alloc/std sets feature(strict_provenance_lints), so we cannot use + # -Z crate-attr=feature(strict_provenance_lints) when -Z build-std is needed. + target_rustflags+="${strict_provenance_lints}" elif [[ -n "${nightly}" ]]; then # -Z build-std requires 1.39.0-nightly: https://github.com/rust-lang/cargo/pull/7216 if [[ "${rustc_minor_version}" -lt 39 ]]; then