Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 259 additions & 1 deletion library/core/src/sync/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ use self::Ordering::*;
use crate::cell::UnsafeCell;
use crate::hint::spin_loop;
use crate::intrinsics::AtomicOrdering as AO;
use crate::{fmt, intrinsics};
#[cfg(kani)]
use crate::kani;
Comment on lines +249 to +250
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two kani imports: #[cfg(kani)] use crate::kani; at the module root and use crate::kani; inside mod verify. Because mod verify defines its own kani name, the root import becomes unused (and can fail builds with -D unused-imports). Prefer one pattern: either keep the root use crate::kani; and rely on use super::*; in verify, or drop the root import and keep the inner one.

Suggested change
#[cfg(kani)]
use crate::kani;

Copilot uses AI. Check for mistakes.
use crate::{fmt, intrinsics, ub_checks};

trait Sealed {}

Expand Down Expand Up @@ -571,6 +573,7 @@ impl AtomicBool {
#[inline]
#[stable(feature = "atomic_from_ptr", since = "1.75.0")]
#[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")]
#[safety::requires(ub_checks::can_dereference(ptr as *const AtomicBool))]
pub const unsafe fn from_ptr<'a>(ptr: *mut bool) -> &'a AtomicBool {
// SAFETY: guaranteed by the caller
unsafe { &*ptr.cast() }
Expand Down Expand Up @@ -1529,6 +1532,7 @@ impl<T> AtomicPtr<T> {
#[inline]
#[stable(feature = "atomic_from_ptr", since = "1.75.0")]
#[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")]
#[safety::requires(ub_checks::can_dereference(ptr as *const AtomicPtr<T>))]
pub const unsafe fn from_ptr<'a>(ptr: *mut *mut T) -> &'a AtomicPtr<T> {
// SAFETY: guaranteed by the caller
unsafe { &*ptr.cast() }
Expand Down Expand Up @@ -2705,6 +2709,7 @@ macro_rules! atomic_int {
#[inline]
#[stable(feature = "atomic_from_ptr", since = "1.75.0")]
#[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")]
#[safety::requires(ub_checks::can_dereference(ptr as *const $atomic_type))]
pub const unsafe fn from_ptr<'a>(ptr: *mut $int_type) -> &'a $atomic_type {
// SAFETY: guaranteed by the caller
unsafe { &*ptr.cast() }
Expand Down Expand Up @@ -3928,6 +3933,7 @@ fn strongest_failure_ordering(order: Ordering) -> Ordering {

#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst))]
unsafe fn atomic_store<T: Copy>(dst: *mut T, val: T, order: Ordering) {
// SAFETY: the caller must uphold the safety contract for `atomic_store`.
unsafe {
Expand All @@ -3943,6 +3949,7 @@ unsafe fn atomic_store<T: Copy>(dst: *mut T, val: T, order: Ordering) {

#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_dereference(dst))]
unsafe fn atomic_load<T: Copy>(dst: *const T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_load`.
unsafe {
Expand All @@ -3959,6 +3966,7 @@ unsafe fn atomic_load<T: Copy>(dst: *const T, order: Ordering) -> T {
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_swap<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_swap`.
unsafe {
Expand All @@ -3976,6 +3984,7 @@ unsafe fn atomic_swap<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_add<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_add`.
unsafe {
Expand All @@ -3993,6 +4002,7 @@ unsafe fn atomic_add<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) ->
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_sub<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_sub`.
unsafe {
Expand All @@ -4012,6 +4022,7 @@ unsafe fn atomic_sub<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) ->
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[unstable(feature = "core_intrinsics", issue = "none")]
#[doc(hidden)]
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
pub unsafe fn atomic_compare_exchange<T: Copy>(
dst: *mut T,
old: T,
Expand Down Expand Up @@ -4077,6 +4088,7 @@ pub unsafe fn atomic_compare_exchange<T: Copy>(
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_compare_exchange_weak<T: Copy>(
dst: *mut T,
old: T,
Expand Down Expand Up @@ -4142,6 +4154,7 @@ unsafe fn atomic_compare_exchange_weak<T: Copy>(
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_and<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_and`
unsafe {
Expand All @@ -4158,6 +4171,7 @@ unsafe fn atomic_and<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) ->
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_nand<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_nand`
unsafe {
Expand All @@ -4174,6 +4188,7 @@ unsafe fn atomic_nand<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) ->
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_or<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_or`
unsafe {
Expand All @@ -4190,6 +4205,7 @@ unsafe fn atomic_or<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) -> T
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_xor<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_xor`
unsafe {
Expand All @@ -4207,6 +4223,7 @@ unsafe fn atomic_xor<T: Copy, U: Copy>(dst: *mut T, val: U, order: Ordering) ->
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_max<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_max`
unsafe {
Expand All @@ -4224,6 +4241,7 @@ unsafe fn atomic_max<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_min<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_min`
unsafe {
Expand All @@ -4241,6 +4259,7 @@ unsafe fn atomic_min<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_umax<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_umax`
unsafe {
Expand All @@ -4258,6 +4277,7 @@ unsafe fn atomic_umax<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
#[inline]
#[cfg(target_has_atomic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[safety::requires(ub_checks::can_write(dst) && ub_checks::can_dereference(dst as *const T))]
unsafe fn atomic_umin<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_umin`
unsafe {
Expand Down Expand Up @@ -4480,3 +4500,241 @@ impl<T> fmt::Pointer for AtomicPtr<T> {
pub fn spin_loop_hint() {
spin_loop()
}

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
use super::*;
use crate::kani;

#[kani::proof]
fn verify_atomic_bool_from_ptr() {
let mut val: bool = kani::any();
Comment on lines +4504 to +4512
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The #[cfg(kani)] mod verify block unconditionally references AtomicBool, AtomicI16, AtomicI64, AtomicPtr, etc., but those types are themselves #[cfg(target_has_atomic_load_store = ...)]-gated. On targets lacking a given atomic width (or pointer atomics), Kani builds will fail to compile. Gate each proof (or submodule) with the same target_has_atomic_load_store cfgs as the atomic types it uses.

Copilot uses AI. Check for mistakes.
let ptr = &mut val as *mut bool;
let atomic = unsafe { AtomicBool::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_i8_from_ptr() {
let mut val: i8 = kani::any();
let ptr = &mut val as *mut i8;
let atomic = unsafe { AtomicI8::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_u8_from_ptr() {
let mut val: u8 = kani::any();
let ptr = &mut val as *mut u8;
let atomic = unsafe { AtomicU8::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_i16_from_ptr() {
let mut val: i16 = kani::any();
let ptr = &mut val as *mut i16;
let atomic = unsafe { AtomicI16::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_u16_from_ptr() {
let mut val: u16 = kani::any();
let ptr = &mut val as *mut u16;
let atomic = unsafe { AtomicU16::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_i32_from_ptr() {
let mut val: i32 = kani::any();
let ptr = &mut val as *mut i32;
let atomic = unsafe { AtomicI32::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_u32_from_ptr() {
let mut val: u32 = kani::any();
let ptr = &mut val as *mut u32;
let atomic = unsafe { AtomicU32::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_i64_from_ptr() {
let mut val: i64 = kani::any();
let ptr = &mut val as *mut i64;
let atomic = unsafe { AtomicI64::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}
Comment on lines +4566 to +4572
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These from_ptr proofs take &mut val where val is the non-atomic primitive type. That does not guarantee the safety precondition ptr is aligned to align_of::<Atomic…>() on targets where the primitive has alignment < its size (the docs for integer atomics explicitly mention this case). Consider using a #[repr(align(N))] wrapper (with N = 2/4/8/16 as appropriate) to ensure the pointed-to storage meets the atomic alignment requirement, especially for 32/64-bit atomics and AtomicPtr.

Copilot uses AI. Check for mistakes.

#[kani::proof]
fn verify_atomic_u64_from_ptr() {
let mut val: u64 = kani::any();
let ptr = &mut val as *mut u64;
let atomic = unsafe { AtomicU64::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

// --- AtomicPtr: multiple T sizes per spec ---

#[kani::proof]
fn verify_atomic_ptr_from_ptr_size0() {
let mut val: *mut () = core::ptr::null_mut();
let ptr = &mut val as *mut *mut ();
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_ptr_from_ptr_size1() {
let mut val: *mut u8 = core::ptr::null_mut();
let ptr = &mut val as *mut *mut u8;
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_ptr_from_ptr_size2() {
let mut val: *mut u16 = core::ptr::null_mut();
let ptr = &mut val as *mut *mut u16;
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_ptr_from_ptr_size4() {
let mut val: *mut u32 = core::ptr::null_mut();
let ptr = &mut val as *mut *mut u32;
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

#[kani::proof]
fn verify_atomic_ptr_from_ptr_size3() {
let mut val: *mut [u8; 3] = core::ptr::null_mut();
let ptr = &mut val as *mut *mut [u8; 3];
Comment on lines +4609 to +4619
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the AtomicPtr::from_ptr proofs are named size0/size1/size2/size4/size3 and the order is non-monotonic (size4 before size3). Consider renaming/reordering to keep the harnesses easy to scan (e.g., size0..size4 in order, or a name that reflects the T used rather than a “size”).

Suggested change
fn verify_atomic_ptr_from_ptr_size4() {
let mut val: *mut u32 = core::ptr::null_mut();
let ptr = &mut val as *mut *mut u32;
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}
#[kani::proof]
fn verify_atomic_ptr_from_ptr_size3() {
let mut val: *mut [u8; 3] = core::ptr::null_mut();
let ptr = &mut val as *mut *mut [u8; 3];
fn verify_atomic_ptr_from_ptr_size3() {
let mut val: *mut [u8; 3] = core::ptr::null_mut();
let ptr = &mut val as *mut *mut [u8; 3];
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}
#[kani::proof]
fn verify_atomic_ptr_from_ptr_size4() {
let mut val: *mut u32 = core::ptr::null_mut();
let ptr = &mut val as *mut *mut u32;

Copilot uses AI. Check for mistakes.
let atomic = unsafe { AtomicPtr::from_ptr(ptr) };
let _ = atomic.load(Ordering::Relaxed);
}

// --- Part 2: Verify atomic helper functions ---

#[kani::proof]
fn verify_atomic_store() {
let mut val: i32 = kani::any();
let new_val: i32 = kani::any();
unsafe { atomic_store(&mut val as *mut i32, new_val, Ordering::SeqCst) };
assert!(val == new_val);
}
Comment on lines +4624 to +4632
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description/title focus on from_ptr safety harnesses, but this change also adds #[safety::requires(...)] contracts for many internal atomic helper functions (atomic_store/load/swap/...) and introduces a large set of Part 2 Kani proofs for those helpers. Please either (a) update the PR description/scope accordingly, or (b) split the helper-function contract + proofs into a separate PR to keep this one narrowly scoped to Challenge #7 Part 1.

Copilot uses AI. Check for mistakes.

#[kani::proof]
fn verify_atomic_load() {
let val: i32 = kani::any();
let loaded = unsafe { atomic_load(&val as *const i32, Ordering::SeqCst) };
assert!(loaded == val);
}

#[kani::proof]
fn verify_atomic_swap() {
let mut val: i32 = kani::any();
let new_val: i32 = kani::any();
let old = unsafe { atomic_swap(&mut val as *mut i32, new_val, Ordering::SeqCst) };
let _ = old;
}

#[kani::proof]
fn verify_atomic_add() {
let mut val: i32 = 10;
let old = unsafe { atomic_add(&mut val as *mut i32, 5i32, Ordering::SeqCst) };
assert!(old == 10);
}

#[kani::proof]
fn verify_atomic_sub() {
let mut val: i32 = 10;
let old = unsafe { atomic_sub(&mut val as *mut i32, 3i32, Ordering::SeqCst) };
assert!(old == 10);
}

#[kani::proof]
fn verify_atomic_compare_exchange() {
let mut val: i32 = 10;
let result = unsafe {
atomic_compare_exchange(
&mut val as *mut i32,
10,
20,
Ordering::SeqCst,
Ordering::SeqCst,
)
};
assert!(result == Ok(10));
}

#[kani::proof]
fn verify_atomic_compare_exchange_weak() {
let mut val: i32 = 10;
let _ = unsafe {
atomic_compare_exchange_weak(
&mut val as *mut i32,
10,
20,
Ordering::SeqCst,
Ordering::SeqCst,
)
};
}

#[kani::proof]
fn verify_atomic_and() {
let mut val: i32 = 0b1111;
let old = unsafe { atomic_and(&mut val as *mut i32, 0b1010i32, Ordering::SeqCst) };
assert!(old == 0b1111);
}

#[kani::proof]
fn verify_atomic_nand() {
let mut val: i32 = 0b1111;
let old = unsafe { atomic_nand(&mut val as *mut i32, 0b1010i32, Ordering::SeqCst) };
assert!(old == 0b1111);
}

#[kani::proof]
fn verify_atomic_or() {
let mut val: i32 = 0b1010;
let old = unsafe { atomic_or(&mut val as *mut i32, 0b0101i32, Ordering::SeqCst) };
assert!(old == 0b1010);
}

#[kani::proof]
fn verify_atomic_xor() {
let mut val: i32 = 0b1111;
let old = unsafe { atomic_xor(&mut val as *mut i32, 0b1010i32, Ordering::SeqCst) };
assert!(old == 0b1111);
}

#[kani::proof]
fn verify_atomic_max() {
let mut val: i32 = 10;
let old = unsafe { atomic_max(&mut val as *mut i32, 20, Ordering::SeqCst) };
assert!(old == 10);
}

#[kani::proof]
fn verify_atomic_umax() {
let mut val: u32 = 10;
let old = unsafe { atomic_umax(&mut val as *mut u32, 20, Ordering::SeqCst) };
assert!(old == 10);
}

#[kani::proof]
fn verify_atomic_umin() {
let mut val: u32 = 20;
let old = unsafe { atomic_umin(&mut val as *mut u32, 10, Ordering::SeqCst) };
assert!(old == 20);
}
}
Loading