Skip to content
This repository has been archived by the owner on Mar 20, 2023. It is now read-only.

ArcGuard's Send and Sync should have bounds on RC #33

Closed
ammaraskar opened this issue Dec 8, 2020 · 1 comment · Fixed by #34
Closed

ArcGuard's Send and Sync should have bounds on RC #33

ammaraskar opened this issue Dec 8, 2020 · 1 comment · Fixed by #34
Assignees
Labels
api-breaking Pull request contains (or issue may require) an API-breaking change bug Something isn't working P1 Priority 1

Comments

@ammaraskar
Copy link

Hi there, we (Rust group @sslab-gatech) are scanning crates on crates.io for potential soundness bugs. We noticed that async_coap::arc_guard::ArcGuard implements Send and Sync so long as T: Send and T: Sync.

unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {}
unsafe impl<RC, T: Sync> Sync for ArcGuard<RC, T> {}

However, this should also probably be bounded by RC: Send and RC: Sync, otherwise it's possible to smuggle across non-Send types across thread boundaries. Here's a proof-of-concept that segfaults safe rust code:

#![forbid(unsafe_code)]

use async_coap::arc_guard::ArcGuard;

use std::{cell::Cell, sync::Arc};
use crossbeam_utils::thread;

// A simple tagged union used to demonstrate problems with data races in Cell.
#[derive(Debug, Clone, Copy)]
enum RefOrInt {
    Ref(&'static u64),
    Int(u64),
}
static SOME_INT: u64 = 123;

fn main() {
    let cell = Cell::new(RefOrInt::Ref(&SOME_INT));
    let arc = Arc::new(cell);

    let arc_guard = ArcGuard::new(arc, |_| ());
    thread::scope(|s| {
        s.spawn(|_| {
            let smuggled_arc = (&arc_guard).head();

            loop {
                // Repeatedly write Ref(&addr) and Int(0xdeadbeef) into the cell.
                smuggled_arc.set(RefOrInt::Ref(&SOME_INT));
                smuggled_arc.set(RefOrInt::Int(0xdeadbeef));
            }
        });

        loop {
            if let RefOrInt::Ref(addr) = (**arc_guard.head()).get() {
                // Hope that between the time we pattern match the object as a
                // `Ref`, it gets written to by the other thread.
                if addr as *const u64 == &SOME_INT as *const u64 { continue; }

                println!("Pointer is now: {:p}", addr);
                println!("Dereferencing addr will now segfault: {}", *addr);
            }
        }
    });
}

Output:

Pointer is now: 0xdeadbeef

Terminated with signal 11 (SIGSEGV)
@Shnatsel
Copy link

Heads up: this issue has been included in the RustSec advisory database. It will be surfaced by tools such as cargo-audit or cargo-deny from now on.

Once a fix is released to crates.io, please open a pull request to update the advisory with the patched version, or file an issue on the advisory database repository.

@darconeous darconeous added api-breaking Pull request contains (or issue may require) an API-breaking change bug Something isn't working P1 Priority 1 labels Jun 4, 2021
@darconeous darconeous self-assigned this Jun 4, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-breaking Pull request contains (or issue may require) an API-breaking change bug Something isn't working P1 Priority 1
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants