Skip to content

Likely erroneous dangling reference error in scope-lock library #4321

Closed
@zetanumbers

Description

@zetanumbers

I could not easily shrink faulty code, so I am gonna explain how this library works. scope-lock uses parking_lot::RwLock to facilitate safe lifetime extension of closures and futures under a similar to std::thread::scope mechanism. That is we take a read lock for each closure/future with extended lifetime and at the return from the look_scope's scope we take a write lock, blocking until every object with extended lifetime is dropped. However in the example utilizing threads named boxed.rs MIRI assumes basically that the read guards on other threads are unlocked after lock's deallocation. This doesn't seem reasonable to me and happens only if Box was used, so I assume simulation of acquire-release atomic order for some reason no longer respects allocation information.

After a git bisection I have managed to reduce the scope of the issue to 26d3a02 suspect commit.
UPDATE: This error reproduces even on the old versions with -Zmiri-many-seeds enabled.

Steps to reproduce:

git clone https://github.com/zetanumbers/scope-lock.git
cd scope-lock
cargo miri run --example boxed
Error log
hello from the first scoped thread
[examples/boxed.rs:12:17] &a = [
    1hello from the main thread
,
    2,
    3hello from the second scoped thread
,
]
error: Undefined Behavior: constructing invalid value at .<captured-var(0)>: encountered a dangling reference (use-after-free)
   --> /home/zetanumbers/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot_core-0.9.10/src/parking_lot.rs:791:5
    |
791 |     callback(result);
    |     ^^^^^^^^ constructing invalid value at .<captured-var(0)>: encountered a dangling reference (use-after-free)
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
    = note: BACKTRACE on thread `unnamed-1`:
    = note: inside `parking_lot_core::parking_lot::unpark_one::<{closure@parking_lot::raw_rwlock::RawRwLock::unlock_shared_slow::{closure#0}}>` at /home/zetanumbers/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot_core-0.9.10/src/parking_lot.rs:791:5: 791:13
    = note: inside `parking_lot::raw_rwlock::RawRwLock::unlock_shared_slow` at /home/zetanumbers/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.3/src/raw_rwlock.rs:742:13: 742:57
    = note: inside `<parking_lot::raw_rwlock::RawRwLock as lock_api::rwlock::RawRwLock>::unlock_shared` at /home/zetanumbers/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parking_lot-0.12.3/src/raw_rwlock.rs:137:13: 137:38
    = note: inside `<lock_api::rwlock::RwLockReadGuard<'_, parking_lot::raw_rwlock::RawRwLock, ()> as std::ops::Drop>::drop` at /home/zetanumbers/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lock_api-0.4.12/src/rwlock.rs:1375:13: 1375:44
    = note: inside `std::ptr::drop_in_place::<lock_api::rwlock::RwLockReadGuard<'_, parking_lot::raw_rwlock::RawRwLock, ()>> - shim(Some(lock_api::rwlock::RwLockReadGuard<'_, parking_lot::raw_rwlock::RawRwLock, ()>))` at /home/zetanumbers/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:524:1: 524:56
    = note: inside `std::ptr::drop_in_place::<scope_lock::extended::AssociateReference<scope_lock::extended::UnsafeAssertSync<scope_lock::extended::UnsafeAssertSend<{closure@scope_lock::extend_fn_unchecked<'_, std::boxed::Box<{closure@examples/boxed.rs:9:36: 9:40}>, (), ()>::{closure#0}}>>>> - shim(Some(scope_lock::extended::AssociateReference<scope_lock::extended::UnsafeAssertSync<scope_lock::extended::UnsafeAssertSend<{closure@scope_lock::extend_fn_unchecked<'_, std::boxed::Box<{closure@examples/boxed.rs:9:36: 9:40}>, (), ()>::{closure#0}}>>>))` at /home/zetanumbers/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:524:1: 524:56
    = note: inside `std::ptr::drop_in_place::<{closure@scope_lock::extended::func::<impl scope_lock::Extender<'_, '_>>::fn_<'_, std::boxed::Box<{closure@examples/boxed.rs:9:36: 9:40}>, (), ()>::{closure#0}}> - shim(Some({closure@scope_lock::extended::func::<impl scope_lock::Extender<'_, '_>>::fn_<'_, std::boxed::Box<{closure@examples/boxed.rs:9:36: 9:40}>, (), ()>::{closure#0}}))` at /home/zetanumbers/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:524:1: 524:56
    = note: inside `std::ptr::drop_in_place::<{closure@examples/boxed.rs:14:13: 14:20}> - shim(Some({closure@examples/boxed.rs:14:13: 14:20}))` at /home/zetanumbers/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:524:1: 524:56
note: inside closure
   --> examples/boxed.rs:14:25
    |
14  |             move || f(())
    |                         ^

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error
Example's code
use std::thread;

fn main() {
    let mut a = vec![1, 2, 3];
    let mut x = 0;

    scope_lock::lock_scope(|e| {
        thread::spawn({
            let f = e.fn_(Box::new(|()| {
                println!("hello from the first scoped thread");
                // We can borrow `a` here.
                dbg!(&a);
            }));
            move || f(())
        });
        thread::spawn({
            let mut f = e.fn_mut(Box::new(|()| {
                println!("hello from the second scoped thread");
                // We can even mutably borrow `x` here,
                // because no other threads are using it.
                x += a[0] + a[2];
            }));
            move || f(())
        });
        println!("hello from the main thread");
    });

    // After the scope, we can modify and access our variables again:
    a.push(4);
    assert_eq!(x, a.len());
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions