diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 329af52..21ed083 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -15,11 +15,12 @@ use crate::noun::{Atom, Cell, IndirectAtom, Noun, Slots, D, T}; use crate::serf::TERMINATOR; use crate::trace::{write_nock_trace, TraceInfo, TraceStack}; use crate::unifying_equality::unifying_equality; -use ares_guard::{guard, guard_err}; +use ares_guard::*; use ares_macros::tas; use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters}; use bitvec::prelude::{BitSlice, Lsb0}; -use either::Either::*; +use either::*; +use std::convert::TryFrom; use std::ffi::c_void; use std::result; use std::sync::atomic::Ordering; @@ -319,11 +320,25 @@ pub enum Error { } pub enum GuardError { - GuardSound = 0, - GuardArmor = 1, - GuardWeird = 2, - GuardSpent = 3, - GuardErupt = 4, + GuardSound = GUARD_SOUND as isize, + GuardArmor = GUARD_ARMOR as isize, + GuardWeird = GUARD_WEIRD as isize, + GuardSpent = GUARD_SPENT as isize, + GuardErupt = GUARD_ERUPT as isize, +} + +impl TryFrom for GuardError { + type Error = (); + fn try_from(value: u32) -> std::result::Result { + match value { + GUARD_SOUND => Ok(GuardError::GuardSound), + GUARD_ARMOR => Ok(GuardError::GuardArmor), + GUARD_WEIRD => Ok(GuardError::GuardWeird), + GUARD_SPENT => Ok(GuardError::GuardSpent), + GUARD_ERUPT => Ok(GuardError::GuardErupt), + _ => Err(()), + } + } } impl Preserve for Error { @@ -367,48 +382,89 @@ fn debug_assertions(stack: &mut NockStack, noun: Noun) { assert_no_junior_pointers!(stack, noun); } -extern "C" fn rust_callback(arg: *mut c_void) -> *mut c_void { - let closure: &mut Box *mut c_void> = - unsafe { &mut *(arg as *mut Box *mut c_void>) }; - closure() +use std::marker::PhantomData; + +pub struct CCallback<'closure> { + pub function: unsafe extern "C" fn(*mut c_void) -> *mut c_void, + pub user_data: *mut c_void, + + // without this it's too easy to accidentally drop the closure too soon + _lifetime: PhantomData<&'closure mut c_void>, } +impl<'closure> CCallback<'closure> { + pub fn new(closure: &'closure mut F) -> Self where F: FnMut() -> Result { + let function: unsafe extern "C" fn(*mut c_void) -> *mut c_void = Self::call_closure::; + + debug_assert_eq!(std::mem::size_of::<&'closure mut F>(), std::mem::size_of::<*const c_void>()); + debug_assert_eq!(std::mem::size_of_val(&function), std::mem::size_of::<*const c_void>()); + + Self { + function, + user_data: closure as *mut F as *mut c_void, + _lifetime: PhantomData, + } + } + + unsafe extern "C" fn call_closure(user_data: *mut c_void) -> *mut c_void where F: FnMut() -> Result { + let cb: &mut F = user_data.cast::().as_mut().unwrap(); + let mut v = (*cb)(); + let v_ptr = &mut v as *mut _ as *mut c_void; + v_ptr + } +} + +// fn main() { +// let mut v = Vec::new(); + +// // must assign to a variable to ensure it lives until the end of scope +// let closure = &mut |x: i32| { v.push(x) }; +// let c = CCallback::new(closure); + +// unsafe { (c.function)(c.user_data, 123) }; + +// assert_eq!(v, [123]); +// } + pub fn call_with_guard Result>( - f: F, + f: &mut F, stack: *const *mut c_void, alloc: *const *mut c_void, ) -> Result { - let boxed_f = Box::new(f); - let res: Result = Err(Error::Deterministic(D(0))); - let mut result = Box::new(res); + let c = CCallback::new(f); + let mut result: Result = Ok(D(0)); + let result_ptr = &mut result as *mut _ as *mut c_void; unsafe { - let raw_f = Box::into_raw(Box::new(boxed_f)); - let result_ptr = &mut result as *mut _ as *mut c_void; - guard( - Some(rust_callback), - raw_f as *mut c_void, + let err = guard( + Some(c.function as unsafe extern "C" fn(*mut c_void) -> *mut c_void), + c.user_data as *mut c_void, stack, alloc, result_ptr, ); - let _ = Box::from_raw(raw_f); - - if !*result.is_null() { - let err = *(result as *const guard_err); + if let Ok(err) = GuardError::try_from(err) { match err { - 0 => { // sound - let res = result as *mut Result; - *res - }, - // TODO: handle other errors explicitly. - _ => Err(Error::Deterministic(D(0))), + GuardError::GuardSound => { + return result; + } + GuardError::GuardArmor => { + return Err(Error::Deterministic(D(0))); + } + GuardError::GuardWeird => { + return Err(Error::Deterministic(D(0))); + } + GuardError::GuardSpent => { + return Err(Error::NonDeterministic(D(0))); + } + GuardError::GuardErupt => { + return Err(Error::NonDeterministic(D(0))); + } } - } - else { - (Box::from_raw(raw_f))() + } else { + return Err(Error::Deterministic(D(0))); } } } @@ -450,8 +506,10 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) let nock = assert_no_alloc(|| { ensure_alloc_counters(|| { - call_with_guard(|| unsafe { + let work_closure = &mut || unsafe { + eprint!("ares: entered closure\r\n"); push_formula(&mut context.stack, formula, true)?; + eprint!("ares: pushed formula\r\n"); loop { let work: NockWork = *context.stack.top(); @@ -989,11 +1047,8 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }, }; } - }, - stack_ptr_ptr, - alloc_ptr_ptr, - std::ptr::null_mut() as *mut *mut c_void, - ) + }; + call_with_guard(work_closure, stack_ptr_ptr, alloc_ptr_ptr) }) }); diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 1704f14..60ee747 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -15,12 +15,13 @@ static uint64_t *guard_p = 0; static uint64_t **stack = 0; static uint64_t **alloc = 0; +static jmp_buf env_buffer; volatile sig_atomic_t err = guard_sound; - // Center the guard page. -guard_err _focus_guard() { +guard_err _focus_guard() +{ uint64_t *stack_p = *stack; uint64_t *alloc_p = *alloc; @@ -61,8 +62,8 @@ guard_err _focus_guard() { } guard_err _slash_guard(void *si_addr) { - // fprintf(stderr, "guard: slash at %p\r\n", si_addr); - // fprintf(stderr, "guard: guard at %p\r\n", (void *) guard_p); + fprintf(stderr, "guard: slash at %p\r\n", si_addr); + fprintf(stderr, "guard: guard at %p\r\n", (void *) guard_p); if (si_addr >= (void *)guard_p && si_addr < (void *)guard_p + GD_PAGESIZE) { return _focus_guard(); @@ -71,17 +72,24 @@ guard_err _slash_guard(void *si_addr) { return guard_weird; } -void _signal_handler(int sig, siginfo_t *si, void *unused) { +void _signal_handler(int sig, siginfo_t *si, void *unused) +{ switch (sig) { case SIGSEGV: + fprintf(stderr, "guard: sigsegv\r\n"); err = _slash_guard(si->si_addr); break; case SIGINT: + fprintf(stderr, "guard: sigint\r\n"); err = guard_erupt; break; default: break; } + + if (err != guard_sound) { + longjmp(env_buffer, 1); + } } guard_err _register_handler() { @@ -95,18 +103,27 @@ guard_err _register_handler() { return guard_weird; } + fprintf(stderr, "guard: registered handler\r\n"); return guard_sound; } -void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *alloc_pp, void *ret) { +guard_err guard( + void *(*f)(void *), + void *user_data, + void *const *stack_pp, + void *const *alloc_pp, + void *ret +) +{ stack = (uint64_t**) stack_pp; alloc = (uint64_t**) alloc_pp; + fprintf(stderr, "guard: f pointer at %p\r\n", (void *) f); fprintf(stderr, "guard: stack at %p\r\n", (void *) stack); fprintf(stderr, "guard: alloc at %p\r\n", (void *) alloc); - fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack); fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc); + fprintf(stderr, "guard: ret pointer at %p\r\n", (void *) ret); if (guard_p == 0) { fprintf(stderr, "guard: installing guard page\r\n"); @@ -124,27 +141,21 @@ void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *al goto fail; } - pthread_t thread; - int thread_err; - thread_err = pthread_create(&thread, NULL, f, arg); - if (thread_err != 0) { - err = guard_weird; - goto fail; + void *result; + if (setjmp(env_buffer) == 0) { + fprintf(stderr, "guard: calling f\r\n"); + result = f(user_data); } - - while (err == guard_sound) { - if (pthread_kill(thread, 0) != 0) { + else { + fprintf(stderr, "guard: jump buffer already set\r\n"); + if (err != guard_sound) { + fprintf(stderr, "guard: not sound\r\n"); goto fail; } - sleep(1); - } - - void *thread_result; - pthread_join(thread, &thread_result); - *(void **)ret = thread_result; - - if (err != guard_sound) { - goto fail; + else { + fprintf(stderr, "guard: assigning ret\r\n"); + *(void **)ret = result; + } } if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { @@ -152,13 +163,25 @@ void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *al goto fail; } - return; + return guard_sound; fail: if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { - fprintf(stderr, "guard: failed to remove guard page\r\n"); + fprintf(stderr, "guard: failed to uninstall guard page\r\n"); } - fprintf(stderr, "guard: error %d\r\n", err); - *(void **)ret = (void *) &err; - return; + switch (err) { + case guard_armor: + fprintf(stderr, "guard: armor error\r\n"); + break; + case guard_weird: + fprintf(stderr, "guard: weird error\r\n"); + break; + case guard_spent: + fprintf(stderr, "guard: spent error\r\n"); + break; + case guard_erupt: + fprintf(stderr, "guard: erupt error\r\n"); + break; + } + return err; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 94ea94e..3b03231 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -3,6 +3,15 @@ #include + +typedef enum { + guard_sound = 0, // job's done + guard_armor = 1, // mprotect + guard_weird = 2, // strange state + guard_spent = 3, // out of memory (bail:meme) + guard_erupt = 4, // sigint +} guard_err; + /** * Execute the given closure `f` within the memory arena between the * `stack` and `alloc` pointers, with guard page protection. Write either @@ -31,15 +40,13 @@ * error will be written to the `ret` pointer. The caller is then responsible * for handling this error and aborting with a `bail:meme`. */ -void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *alloc_pp, void *ret); - -typedef enum { - guard_sound = 0, // job's done - guard_armor = 1, // mprotect - guard_weird = 2, // strange state - guard_spent = 3, // out of memory (bail:meme) - guard_erupt = 4, // sigint -} guard_err; +guard_err guard( + void *(*f)(void *), + void *user_data, + void *const *stack_pp, + void *const *alloc_pp, + void *ret +); #endif diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index a38a13a..1476323 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -3,3 +3,9 @@ #![allow(non_snake_case)] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +pub const GUARD_SOUND: u32 = guard_err_guard_sound; +pub const GUARD_ARMOR: u32 = guard_err_guard_armor; +pub const GUARD_WEIRD: u32 = guard_err_guard_weird; +pub const GUARD_SPENT: u32 = guard_err_guard_spent; +pub const GUARD_ERUPT: u32 = guard_err_guard_erupt;