Files
Sprimo/skills/unsafe-checker/rules/ptr-01-no-thread-share.md
2026-02-12 22:58:33 +08:00

2.9 KiB

id, original_id, level, impact
id original_id level impact
ptr-01 P.UNS.PTR.01 P CRITICAL

Do Not Share Raw Pointers Across Threads

Summary

Raw pointers (*const T, *mut T) are not Send or Sync by default. Do not share them across threads without ensuring proper synchronization.

Rationale

Raw pointers have no synchronization guarantees. Sharing them across threads can lead to data races, which are undefined behavior.

Bad Example

use std::thread;

// DON'T: Share raw pointers across threads
fn bad_sharing() {
    let mut data = 42i32;
    let ptr = &mut data as *mut i32;

    let handle = thread::spawn(move || {
        // This is undefined behavior!
        unsafe { *ptr = 100; }
    });

    // Main thread also accesses - data race!
    unsafe { *ptr = 200; }

    handle.join().unwrap();
}

// DON'T: Wrap in struct and impl Send unsafely
struct UnsafePtr(*mut i32);
unsafe impl Send for UnsafePtr {}  // Unsound without synchronization!

Good Example

use std::sync::{Arc, Mutex, atomic::{AtomicPtr, Ordering}};
use std::thread;

// DO: Use Arc<Mutex<T>> for shared mutable access
fn good_mutex() {
    let data = Arc::new(Mutex::new(42i32));
    let data_clone = Arc::clone(&data);

    let handle = thread::spawn(move || {
        *data_clone.lock().unwrap() = 100;
    });

    *data.lock().unwrap() = 200;
    handle.join().unwrap();
}

// DO: Use AtomicPtr for lock-free pointer sharing
fn good_atomic() {
    let data = Box::into_raw(Box::new(42i32));
    let atomic_ptr = Arc::new(AtomicPtr::new(data));
    let atomic_clone = Arc::clone(&atomic_ptr);

    let handle = thread::spawn(move || {
        let ptr = atomic_clone.load(Ordering::Acquire);
        // SAFETY: We have exclusive access through atomic operations
        unsafe { println!("Value: {}", *ptr); }
    });

    handle.join().unwrap();

    // SAFETY: All threads done, we own the memory
    unsafe { drop(Box::from_raw(atomic_ptr.load(Ordering::Relaxed))); }
}

// DO: If you must use raw pointers, ensure exclusive access
fn good_exclusive() {
    let mut data = vec![1, 2, 3];

    // Send data ownership to thread, not pointer
    let handle = thread::spawn(move || {
        data.push(4);
        data
    });

    let data = handle.join().unwrap();
    println!("{:?}", data);
}

When Raw Pointers Across Threads Are Valid

Only with proper synchronization:

  • Through AtomicPtr with appropriate memory orderings
  • Protected by a Mutex (don't share the pointer, share the Mutex)
  • Using lock-free algorithms with careful memory ordering

Checklist

  • Does my pointer cross thread boundaries?
  • Is there synchronization preventing concurrent access?
  • Can I use a higher-level abstraction (Arc, Mutex)?
  • If implementing Send/Sync, is thread safety proven?
  • safety-05: Consider safety when implementing Send/Sync
  • safety-02: Verify safety invariants