Skip to content

Challenge 27: Verify safety of Arc functions#575

Open
Samuelsills wants to merge 2 commits intomodel-checking:mainfrom
Samuelsills:challenge-27-arc
Open

Challenge 27: Verify safety of Arc functions#575
Samuelsills wants to merge 2 commits intomodel-checking:mainfrom
Samuelsills:challenge-27-arc

Conversation

@Samuelsills
Copy link
Copy Markdown

Summary

Add Kani proof harnesses for Arc functions specified in Challenge #27:

Unsafe (12/12 — all required):

  • assume_init (single + slice), from_raw, from_raw_in, increment_strong_count, increment_strong_count_in, decrement_strong_count, decrement_strong_count_in, get_mut_unchecked, downcast_unchecked, Weak::from_raw, Weak::from_raw_in

Safe (35/42 — 83%, exceeds 75% threshold):

  • Allocation, conversion, cloning, downcasting, Weak pointer operations, trait implementations

All harnesses verified locally with Kani.

Resolves #383

Samuelsills and others added 2 commits March 28, 2026 00:01
Add Kani proof harnesses for Arc functions specified in Challenge model-checking#27:
12 unsafe functions (assume_init, from_raw, from_raw_in,
increment/decrement_strong_count, get_mut_unchecked,
downcast_unchecked, Weak::from_raw, Weak::from_raw_in) and 35 safe
functions covering allocation, atomic reference counting, conversion,
Weak pointer operations, and trait implementations. Exceeds 75% safe
threshold (35/42 = 83%). Resolves model-checking#383

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Samuelsills Samuelsills marked this pull request as ready for review March 28, 2026 00:45
@Samuelsills Samuelsills requested a review from a team as a code owner March 28, 2026 00:45
@Samuelsills
Copy link
Copy Markdown
Author

Verification Coverage Report

Unsafe Functions (12/12 — 100% ✅)

assume_init (single), assume_init (slice), from_raw, from_raw_in, increment_strong_count, increment_strong_count_in, decrement_strong_count, decrement_strong_count_in, get_mut_unchecked, downcast_unchecked, Weak::from_raw, Weak::from_raw_in

Safe Functions with Unsafe Code (35/42 — 83%, exceeds 75% threshold ✅)

Allocation, conversion, cloning, downcasting, Weak pointer operations, trait implementations.

Total: 47 harnesses (12 unsafe + 35 safe)

UBs Checked

  • ✅ Accessing dangling or misaligned pointers
  • ✅ Invoking UB via compiler intrinsics
  • ✅ Mutating immutable bytes
  • ✅ Producing an invalid value

Note on Data Races

In single-threaded Kani verification, data races cannot occur (no concurrent threads). Pointer validity, alignment, and initialization are verified for all operations.

Verification Approach

  • Tool: Kani Rust Verifier
  • Generic T limited to primitive types (i32) per spec allowance
  • Allocators limited to Global per spec allowance

@feliperodri feliperodri added the Challenge Used to tag a challenge label Mar 29, 2026
@feliperodri feliperodri requested a review from Copilot March 31, 2026 22:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds Kani verification harnesses to alloc::sync (Arc/Weak) as part of Challenge #27 / tracking issue #383, aiming to model-check the safety contracts of selected APIs (including all required unsafe ones).

Changes:

  • Introduces a #[cfg(kani)] verification module in library/alloc/src/sync.rs.
  • Adds Kani proof harnesses covering 12 required unsafe Arc/Weak functions plus a broad set of safe Arc/Weak operations.

Comment on lines +4581 to +4596
let ptr = Arc::as_ptr(&a);
unsafe {
Arc::increment_strong_count(ptr);
}
let a2 = unsafe { Arc::from_raw(ptr) };
assert!(*a2 == 42);
}

#[kani::proof]
fn verify_increment_strong_count_in() {
let a = Arc::new_in(42i32, Global);
let ptr = Arc::as_ptr(&a);
unsafe {
Arc::increment_strong_count_in(ptr, Global);
}
let a2 = unsafe { Arc::from_raw_in(ptr, Global) };
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.

Arc::increment_strong_count’s safety contract requires that the pointer comes from Arc::into_raw. This harness uses Arc::as_ptr(&a) instead, which means the proof is not exercising the API under its documented preconditions (and could be unsound under strict provenance assumptions). Consider obtaining ptr via Arc::into_raw(...) (and then balancing the extra strong count via Arc::from_raw/Arc::decrement_strong_count as in the docs).

Suggested change
let ptr = Arc::as_ptr(&a);
unsafe {
Arc::increment_strong_count(ptr);
}
let a2 = unsafe { Arc::from_raw(ptr) };
assert!(*a2 == 42);
}
#[kani::proof]
fn verify_increment_strong_count_in() {
let a = Arc::new_in(42i32, Global);
let ptr = Arc::as_ptr(&a);
unsafe {
Arc::increment_strong_count_in(ptr, Global);
}
let a2 = unsafe { Arc::from_raw_in(ptr, Global) };
let ptr = Arc::into_raw(a);
unsafe {
Arc::increment_strong_count(ptr);
}
let a1 = unsafe { Arc::from_raw(ptr) };
let a2 = unsafe { Arc::from_raw(ptr) };
assert!(*a1 == 42);
assert!(*a2 == 42);
}
#[kani::proof]
fn verify_increment_strong_count_in() {
let a = Arc::new_in(42i32, Global);
let (ptr, alloc) = Arc::into_raw_with_allocator(a);
unsafe {
Arc::increment_strong_count_in(ptr, alloc);
}
let a1 = unsafe { Arc::from_raw_in(ptr, alloc) };
let a2 = unsafe { Arc::from_raw_in(ptr, alloc) };
assert!(*a1 == 42);

Copilot uses AI. Check for mistakes.
Comment on lines +4581 to +4597
let ptr = Arc::as_ptr(&a);
unsafe {
Arc::increment_strong_count(ptr);
}
let a2 = unsafe { Arc::from_raw(ptr) };
assert!(*a2 == 42);
}

#[kani::proof]
fn verify_increment_strong_count_in() {
let a = Arc::new_in(42i32, Global);
let ptr = Arc::as_ptr(&a);
unsafe {
Arc::increment_strong_count_in(ptr, Global);
}
let a2 = unsafe { Arc::from_raw_in(ptr, Global) };
assert!(*a2 == 42);
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.

Arc::increment_strong_count_in requires the pointer be obtained from Arc::into_raw/Arc::into_raw_with_allocator for the same A. Using Arc::as_ptr(&a) here means the harness is not respecting the documented safety preconditions, weakening/invalidating the proof. Please switch to Arc::into_raw_with_allocator (or Arc::into_raw for Global) and ensure the strong-count balance is handled as intended by the API.

Suggested change
let ptr = Arc::as_ptr(&a);
unsafe {
Arc::increment_strong_count(ptr);
}
let a2 = unsafe { Arc::from_raw(ptr) };
assert!(*a2 == 42);
}
#[kani::proof]
fn verify_increment_strong_count_in() {
let a = Arc::new_in(42i32, Global);
let ptr = Arc::as_ptr(&a);
unsafe {
Arc::increment_strong_count_in(ptr, Global);
}
let a2 = unsafe { Arc::from_raw_in(ptr, Global) };
assert!(*a2 == 42);
let ptr = Arc::into_raw(a);
unsafe {
Arc::increment_strong_count(ptr);
}
let a1 = unsafe { Arc::from_raw(ptr) };
let a2 = unsafe { Arc::from_raw(ptr) };
assert!(*a1 == 42 && *a2 == 42);
}
#[kani::proof]
fn verify_increment_strong_count_in() {
let a = Arc::new_in(42i32, Global);
let (ptr, alloc) = Arc::into_raw_with_allocator(a);
unsafe {
Arc::increment_strong_count_in(ptr, alloc);
}
let a1 = unsafe { Arc::from_raw_in(ptr, alloc) };
let a2 = unsafe { Arc::from_raw_in(ptr, alloc) };
assert!(*a1 == 42 && *a2 == 42);

Copilot uses AI. Check for mistakes.
Comment on lines +4602 to +4608
let a = Arc::new(42i32);
let a2 = a.clone();
let ptr = Arc::as_ptr(&a2);
core::mem::forget(a2);
unsafe {
Arc::decrement_strong_count(ptr);
}
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.

Arc::decrement_strong_count’s safety contract requires that ptr was obtained through Arc::into_raw. This harness derives ptr via Arc::as_ptr(&a2) and then forgets the Arc, which doesn’t match the API’s specified preconditions and may undermine the proof. Prefer let ptr = Arc::into_raw(a2); (no forget needed) and then call decrement_strong_count(ptr).

Copilot uses AI. Check for mistakes.
Comment on lines +4613 to +4619
let a = Arc::new_in(42i32, Global);
let a2 = a.clone();
let ptr = Arc::as_ptr(&a2);
core::mem::forget(a2);
unsafe {
Arc::decrement_strong_count_in(ptr, Global);
}
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.

Arc::decrement_strong_count_in requires ptr to originate from Arc::into_raw_with_allocator for the same allocator passed in. This harness uses Arc::as_ptr + forget, which doesn’t satisfy the documented preconditions and can make the proof misleading. Please obtain ptr with Arc::into_raw_with_allocator(a2) (or Arc::into_raw_in-equivalent pattern) and then call decrement_strong_count_in with the returned allocator.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Challenge Used to tag a challenge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Challenge 27: Verify atomically reference-counted Cell implementation

3 participants