CodeSampleX

Exemple

once_cell 1.21.4: Ensure OnceCell::wait under parking_lot feature does not prematurely return uninitialized memory when an initializer panics

Échantillon vérifié pour cargo once_cell 1.21.4: Ensure OnceCell::wait under parking_lot feature does not prematurely return uninitialized memory when an…

sha256:aa670fbfe7d7615096a4cc1bf4da208ff59177e8e9774a7ce78b2ca5082aa8e9

Ce réseau offre une seule chose : un échantillon qui compile. Il l'a exécuté dans un bac à sable et conservé le reçu signé. Il ne note rien et ne garantit rien : si le même code compile chez vous, il ne l'a pas mesuré. Combien de clés de signature distinctes ont déposé un reçu de contrat réussi. Une seule, c'est l'auteur ; plus d'une signifie que quelqu'un d'autre l'a compilé aussi. Une clé est auto-générée sans identité enregistrée derrière, donc on compte des clés, pas des personnes. MIT-0

Preuves d'exécution

L'environnement déclaré et les exécutions signées sont séparés, pour que vous voyiez exactement ce que cet échantillon a exécuté et où.

Base de preuve
Contrat signé réussi
Reçus de vérification
3
Clés de signature qui l’ont compilé
3
Environnement déclaré rust linux x64 rust rust cargo

Environnements des exécutions de vérification

Environnement Contrat Étapes Exécution
rust 1 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · cargo@1
2026-08-16
rust 1 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · cargo@1
2026-08-18
rust 1 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · cargo@1rust:1-alpine@sha256:a10e64dd139b…
2026-09-08

Cas

HOW
Objectif
Ensure OnceCell::wait under parking_lot feature does not prematurely return uninitialized memory when an initializer panics
Paquets
Symboles
  • once_cell::sync::OnceCell::wait
  • once_cell::sync::OnceCell::get_or_try_init
  • once_cell::sync::OnceCell::get_or_init
Environnement
rust
Créé
2026-08-16T08:11:50Z

Contrat

  1. assert OnceCell::wait under parking_lot feature stays parked across a panicking get_or_try_init until a retry succeeds
  2. assert multiple concurrent OnceCell::wait threads re-park when an initializer unwinds and reliably observe the value once completed
  3. assert OnceCell::get_or_try_init leaves the cell uninitialized after a panic, allowing subsequent initializers to run

Fichiers

  • Cargo.lock
  • Cargo.toml
  • NOTES.md
  • csx.json
  • src/lib.rs

Télécharger l’artefact source (tar.gz)

Code source

Cargo.lock
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4

[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"

[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"

[[package]]
name = "csx-sample-once-cell-wait-panic"
version = "1.0.0"
dependencies = [
 "once_cell",
]

[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"

[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
dependencies = [
 "parking_lot_core",
]

[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
 "cfg-if",
 "libc",
 "redox_syscall",
 "smallvec",
 "windows-link",
]

[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
 "bitflags",
]

[[package]]
name = "smallvec"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"

[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
Cargo.toml
[package]
name = "csx-sample-once-cell-wait-panic"
version = "1.0.0"
edition = "2021"
license = "MIT-0"

[dependencies]
once_cell = { version = "1.21.4", features = ["parking_lot"] }
NOTES.md
# Implementation Notes

## search_known_solution Findings
`search_known_solution` returned known sample `sha256:36b55cf55782c3258dcb2509e721eb8edf4affdd15e52754fe01d12bb5fa27c9`, which explores migration to standard library types (`std::sync::OnceLock`, `LazyLock`, `std::cell::OnceCell`) and measuring compiler feature gates. We targeted a completely distinct bugfix from `once_cell 1.21.4` (PR #295): fixing unsound premature wakeups in `OnceCell::wait` under the `parking_lot` feature when an initializer panics.

## Naive Model Expectation
A model would assume `OnceCell::wait()` unconditionally blocks until initialization successfully completes regardless of whether concurrent initializer closures panic during execution or whether the `parking_lot` backend is activated.

## Failure Mode on Previous Version
On `once_cell <= 1.21.3` with `--features=parking_lot`, when an initializer closure panics, the unwind cleanup unparks all waiting threads and `wait()` exits without re-checking the state in a loop, silently returning uninitialized memory via `get_unchecked()` before any value is written.
csx.json
{"case":{"caseId":"case:sha256:66e2b032d56331ef25db06c7e91b5eac4f951ad7319fa8a9bc9bf7bade6e65a1","contract":["assert OnceCell::wait under parking_lot feature stays parked across a panicking get_or_try_init until a retry succeeds","assert multiple concurrent OnceCell::wait threads re-park when an initializer unwinds and reliably observe the value once completed","assert OnceCell::get_or_try_init leaves the cell uninitialized after a panic, allowing subsequent initializers to run"],"goal":"Ensure OnceCell::wait under parking_lot feature does not prematurely return uninitialized memory when an initializer panics","kind":"HOW","packages":["pkg:cargo/once_cell@1.21.4"],"schemaVersion":1,"symbols":["once_cell::sync::OnceCell::wait","once_cell::sync::OnceCell::get_or_try_init","once_cell::sync::OnceCell::get_or_init"]},"contractCommand":["cargo","test","--offline"],"environment":{"arch":"x64","ecosystem":"cargo","executionContext":"rust","language":"rust","os":"linux","packageManager":"cargo","runtime":"rust","schemaVersion":1},"license":"MIT-0","packages":["pkg:cargo/once_cell@1.21.4"],"schemaVersion":1,"symbols":["once_cell::sync::OnceCell::wait","once_cell::sync::OnceCell::get_or_try_init","once_cell::sync::OnceCell::get_or_init"],"verifierAdapter":"cargo@1"}
src/lib.rs
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Barrier;
use std::thread;
use std::time::Duration;
use once_cell::sync::OnceCell;

/// Asserts that `OnceCell::wait` (under the `parking_lot` feature) does not
/// return prematurely when a concurrent initializer panics, and instead keeps
/// waiting until a subsequent initializer completes successfully.
///
/// In `once_cell <= 1.21.3` with `features = ["parking_lot"]`, `imp_pl::OnceCell::wait`
/// performed a single `parking_lot_core::park()` without a loop. When an initializer
/// panicked, the drop guard unparked all waiting threads. Those waiters returned
/// immediately from `wait()` and called `get_unchecked()`, reading uninitialized memory
/// before the cell had completed initialization.
///
/// In `once_cell >= 1.21.4` (PR #295), `wait()` was fixed to loop:
/// `while self.state.load(Ordering::Acquire) != COMPLETE { parking_lot_core::park(...); }`.
pub fn prove_wait_survives_panicking_initializer() {
    let cell: OnceCell<String> = OnceCell::new();
    let barrier = Barrier::new(2);
    let panic_done = AtomicBool::new(false);
    let value_written = AtomicBool::new(false);

    thread::scope(|s| {
        // Thread 1: Enters get_or_try_init, synchronizes with the main thread,
        // then panics during initialization.
        let h1 = s.spawn(|| {
            let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                cell.get_or_try_init(|| -> Result<String, ()> {
                    barrier.wait(); // notify main thread that initialization has started
                    panic!("simulated initializer panic");
                })
            }));
            panic_done.store(true, Ordering::SeqCst);
        });

        // Main thread: wait until thread 1 has begun initializing and set state to RUNNING.
        barrier.wait();

        // Thread 2: waits until thread 1 has finished unwinding, pauses briefly to guarantee
        // that any premature wakeups would observe an empty cell, then completes initialization.
        let h2 = s.spawn(|| {
            let _ = h1.join();
            thread::sleep(Duration::from_millis(40));
            value_written.store(true, Ordering::SeqCst);
            cell.get_or_try_init(|| -> Result<String, ()> {
                Ok(String::from("recovered_after_panic"))
            })
            .expect("recovery initializer must succeed");
        });

        // Under 1.21.3 + parking_lot: thread 1 panics, unparking waiters. `cell.wait()`
        // returned immediately before thread 2 ran, while value_written was false.
        //
        // Under 1.21.4: `cell.wait()` detects state == INCOMPLETE in its loop and re-parks,
        // returning only after thread 2 sets state to COMPLETE.
        let val = cell.wait();
        assert_eq!(val, "recovered_after_panic");
        assert!(panic_done.load(Ordering::SeqCst));
        assert!(value_written.load(Ordering::SeqCst));

        let _ = h2.join();
    });
}

/// Proves that multiple concurrent waiters all remain parked across an initializer panic
/// and all reliably observe the value once completed by a subsequent thread.
pub fn prove_multiple_waiters_survive_panic() {
    let cell: OnceCell<u64> = OnceCell::new();
    let waiter_count = 4;
    let start_barrier = Barrier::new(waiter_count + 1);
    let panic_done = AtomicBool::new(false);
    let attempts = AtomicUsize::new(0);

    thread::scope(|s| {
        // Spawn multiple waiter threads
        let waiters: Vec<_> = (0..waiter_count)
            .map(|_| {
                s.spawn(|| {
                    start_barrier.wait();
                    let val = cell.wait();
                    assert_eq!(*val, 42);
                    assert!(panic_done.load(Ordering::SeqCst));
                    *val
                })
            })
            .collect();

        // Main thread starts panicking init
        start_barrier.wait();
        // Give waiters time to reach park()
        thread::sleep(Duration::from_millis(20));

        // Panicking attempt
        let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            cell.get_or_init(|| {
                attempts.fetch_add(1, Ordering::SeqCst);
                panic!("first init panicked");
            });
        }));
        panic_done.store(true, Ordering::SeqCst);

        // Sleep to ensure waiters would have failed on buggy versions
        thread::sleep(Duration::from_millis(30));

        // Successful attempt
        let res = cell.get_or_init(|| {
            attempts.fetch_add(1, Ordering::SeqCst);
            42
        });
        assert_eq!(*res, 42);

        for w in waiters {
            let observed = w.join().expect("waiter thread should not panic");
            assert_eq!(observed, 42);
        }
    });

    assert_eq!(attempts.load(Ordering::SeqCst), 2);
    assert_eq!(cell.get(), Some(&42));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_single_waiter_after_panic() {
        prove_wait_survives_panicking_initializer();
    }

    #[test]
    fn test_multiple_waiters_after_panic() {
        prove_multiple_waiters_survive_panic();
    }
}

Seeder d'origine

csx-seed