Skip to content

Commit

Permalink
Handle unwinding destructors in conc::Garbage by crashing when they…
Browse files Browse the repository at this point in the history
… panic.
  • Loading branch information
ticki committed Jul 21, 2017
1 parent d2efa72 commit e1a5f92
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 11 deletions.
34 changes: 33 additions & 1 deletion conc/src/garbage.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
//! Literal garbage.

use std::mem;

/// An object to be deleted eventually.
///
/// Garbage refers to objects which are waiting to be destroyed, at some point after all references
/// to them are gone.
///
/// Refer to `Garbage::destroy()` for details on destruction.
///
/// See also: ideology.
pub struct Garbage {
/// The pointer to the object.
Expand All @@ -21,6 +25,8 @@ impl Garbage {
/// This takes the pointer and destructor (which takes pointer as argument) and construct the
/// corresponding garbage item.
pub fn new(ptr: *const u8, dtor: fn(*const u8)) -> Garbage {
// TODO: Add assertion against null pointers.

Garbage {
ptr: ptr,
dtor: dtor,
Expand Down Expand Up @@ -59,9 +65,35 @@ impl Garbage {

/// Destroy the garbage.
///
/// This runs the destructor associated with the data.
/// This runs the destructor associated with the data. That is, it runs the destructor function
/// pointer with the provided data pointer as argument.
///
/// # Panic
///
/// This function should never unwind, even if the destructor does. In particular, any
/// unwinding causes a safe crash, equivalent to double-panicking (i.e. SIGILL). This ought to
/// avoid spurious unwinding through unrelated stacks and messing with the environment within
/// the system.
pub fn destroy(self) {
// TODO: Let this unwind by fixing the bugs in `global`.

/// Stop any unwinding.
///
/// This struct stops unwinding through it by double-panicking in its destructor, thus
/// safely SIGILL-ing the program. It is meant to avoid unwinding.
struct StopUnwind;

impl Drop for StopUnwind {
fn drop(&mut self) {
panic!("Panicking during unwinding to stop unwinding.");
}
}

let guard = StopUnwind;
// Run, but catch any panicks that the dtor might cause.
unsafe { (self.dtor)(self.ptr); }
// Prevent the guard's destructor from running.
mem::forget(guard);
}
}

Expand Down
5 changes: 5 additions & 0 deletions conc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ pub fn gc() {
/// reachable from any data structure: It should be impossible to create _new_ guard representing
/// `ptr` from now on, as such thing can mean that new guards can be created after it is dropped
/// causing use-after-free.
///
/// # Destruction
///
/// Under garbage collection of the garbage, the `Garbage::destroy()` method will be called. Refer
/// to the respective API documentation for the behavior in details.
pub fn add_garbage<T>(ptr: &'static T, dtor: fn(&'static T)) {
local::add_garbage(unsafe {
Garbage::new(ptr as *const T as *const u8 as *mut u8, mem::transmute(dtor))
Expand Down
13 changes: 3 additions & 10 deletions conc/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ thread_local! {
///
/// This garbage is pushed to a thread-local queue. When enough garbage is accumulated in the
/// thread, it is exported to the global state.
///
/// Under garbage collection of the garbage, the `Garbage::destroy()` method will be called. Refer
/// to the respective API documentation for the behavior in details.
pub fn add_garbage(garbage: Garbage) {
// Since this function can trigger a GC, it must not be called inside a guard constructor.
guard::debug_assert_no_create();

debug_assert!(!garbage.ptr().is_null(), "Garbage is a null pointer. If this is intentional, \
consider running the destructor directly instead.");

if STATE.state() == thread::LocalKeyState::Destroyed {
// The state was deinitialized, so we must rely on the global state for queueing garbage.
global::export_garbage(vec![garbage]);
Expand Down Expand Up @@ -247,11 +247,4 @@ mod tests {

free_hazard(writer);
}

#[cfg(debug_assertions)]
#[should_panic]
#[test]
fn debug_add_null_garbage() {
add_garbage(unsafe { Garbage::new_box(ptr::null::<u8>()) });
}
}

0 comments on commit e1a5f92

Please sign in to comment.