Skip to content

Commit

Permalink
Use real strict_provenance/exposed_provenance APIs on Rust 1.84+ and …
Browse files Browse the repository at this point in the history
…apply fuzzy_provenance_casts lint
  • Loading branch information
taiki-e committed Jan 5, 2025
1 parent 79fee52 commit 574a720
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 32 deletions.
6 changes: 5 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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/'
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 6 additions & 2 deletions portable-atomic-util/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
);
}

Expand Down Expand Up @@ -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.
Expand Down
38 changes: 35 additions & 3 deletions portable-atomic-util/src/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3020,7 +3020,7 @@ fn abort() -> ! {
}

fn is_dangling<T: ?Sized>(ptr: *const T) -> bool {
ptr as *const () as usize == usize::MAX
(ptr as *const ()).addr() == usize::MAX
}

// Based on unstable alloc::alloc::Global.
Expand Down Expand Up @@ -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<T>(addr: usize) -> *mut T {
Expand All @@ -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))]
Expand Down Expand Up @@ -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<T: ?Sized>: Copy {
#[must_use]
fn addr(self) -> usize;
}
#[cfg(portable_atomic_no_strict_provenance)]
impl<T: ?Sized> PtrExt<T> 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 ())
}
}
}
}
1 change: 1 addition & 0 deletions portable-atomic-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down
4 changes: 2 additions & 2 deletions src/imp/atomic64/arm_linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<i32>(KUSER_HELPER_VERSION).read() };
CACHE.store(v, Ordering::Relaxed);
v
}
Expand All @@ -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
}
}
Expand Down
14 changes: 8 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -2135,7 +2137,7 @@ impl<T> AtomicPtr<T> {
// 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))]
{
Expand Down Expand Up @@ -2179,7 +2181,7 @@ impl<T> AtomicPtr<T> {
// 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))]
{
Expand Down Expand Up @@ -2238,7 +2240,7 @@ impl<T> AtomicPtr<T> {
// 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))]
{
Expand Down Expand Up @@ -2295,7 +2297,7 @@ impl<T> AtomicPtr<T> {
// 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))]
{
Expand Down Expand Up @@ -2351,7 +2353,7 @@ impl<T> AtomicPtr<T> {
// 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))]
{
Expand Down
75 changes: 57 additions & 18 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ type RetInt = u32;
#[allow(dead_code)]
#[inline]
pub(crate) fn create_sub_word_mask_values<T>(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".
Expand All @@ -433,12 +435,12 @@ pub(crate) fn create_sub_word_mask_values<T>(ptr: *mut T) -> (*mut MinWord, RetI
target_arch = "xtensa",
));
let ptr_mask = mem::size_of::<MinWord>() - 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)
Expand All @@ -452,24 +454,61 @@ pub(crate) fn create_sub_word_mask_values<T>(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;

#[cfg(portable_atomic_no_strict_provenance)]
#[must_use]
pub(crate) fn with_addr<T>(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<T>(addr: usize) -> *const T {
addr as *const T
}

#[cfg(miri)]
#[inline]
#[must_use]
pub(crate) fn map_addr<T>(ptr: *mut T, f: impl FnOnce(usize) -> usize) -> *mut T {
with_addr(ptr, f(ptr as usize))
#[cfg(portable_atomic_no_strict_provenance)]
pub(crate) trait PtrExt<T: ?Sized>: 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<T: ?Sized> PtrExt<T> 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
}
}
}
5 changes: 5 additions & 0 deletions tools/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}"
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 574a720

Please sign in to comment.