//! From https://github.com/AdrienChampion/hashconsing/issues/1. #![forbid(unsafe_code)] use hashconsing::{HConsign, HashConsign}; use crossbeam_utils::thread; use std::cell::Cell; use std::hash::{Hash, Hasher}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum RefOrInt<'a> { Ref(&'a u64), Int(u64), } #[derive(PartialEq, Eq)] struct HashableCell { cell: Cell, } // Fake hashing function just so we can get a HConsed going. impl Hash for HashableCell { fn hash(&self, state: &mut H) { 1024.hash(state); } } static SOME_INT: u64 = 123; fn main() { let cell = Cell::new(RefOrInt::Ref(&SOME_INT)); let hashable_cell = HashableCell { cell: cell }; let mut factory: HConsign<_> = HConsign::empty(); let hcons_cell_ref = factory.mk(&hashable_cell); thread::scope(|s| { s.spawn(move |_| { let smuggled_cell = &hcons_cell_ref.get().cell; loop { // Repeatedly write Ref(&addr) and Int(0xdeadbeef) into the cell. smuggled_cell.set(RefOrInt::Ref(&SOME_INT)); smuggled_cell.set(RefOrInt::Int(0xdeadbeef)); } }); loop { if let RefOrInt::Ref(addr) = hashable_cell.cell.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; } // Due to the data race, obtaining Ref(0xdeadbeef) is possible println!("Pointer is now: {:p}", addr); println!("Dereferencing addr will now segfault: {}", *addr); } } }); }