mirror of
https://github.com/urbit/ares.git
synced 2024-11-22 15:08:54 +03:00
guard: kind of works
This commit is contained in:
parent
7ff1657687
commit
95d4023d72
@ -15,11 +15,12 @@ use crate::noun::{Atom, Cell, IndirectAtom, Noun, Slots, D, T};
|
|||||||
use crate::serf::TERMINATOR;
|
use crate::serf::TERMINATOR;
|
||||||
use crate::trace::{write_nock_trace, TraceInfo, TraceStack};
|
use crate::trace::{write_nock_trace, TraceInfo, TraceStack};
|
||||||
use crate::unifying_equality::unifying_equality;
|
use crate::unifying_equality::unifying_equality;
|
||||||
use ares_guard::{guard, guard_err};
|
use ares_guard::*;
|
||||||
use ares_macros::tas;
|
use ares_macros::tas;
|
||||||
use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters};
|
use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters};
|
||||||
use bitvec::prelude::{BitSlice, Lsb0};
|
use bitvec::prelude::{BitSlice, Lsb0};
|
||||||
use either::Either::*;
|
use either::*;
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
@ -319,11 +320,25 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub enum GuardError {
|
pub enum GuardError {
|
||||||
GuardSound = 0,
|
GuardSound = GUARD_SOUND as isize,
|
||||||
GuardArmor = 1,
|
GuardArmor = GUARD_ARMOR as isize,
|
||||||
GuardWeird = 2,
|
GuardWeird = GUARD_WEIRD as isize,
|
||||||
GuardSpent = 3,
|
GuardSpent = GUARD_SPENT as isize,
|
||||||
GuardErupt = 4,
|
GuardErupt = GUARD_ERUPT as isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u32> for GuardError {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
|
||||||
|
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 {
|
impl Preserve for Error {
|
||||||
@ -367,48 +382,89 @@ fn debug_assertions(stack: &mut NockStack, noun: Noun) {
|
|||||||
assert_no_junior_pointers!(stack, noun);
|
assert_no_junior_pointers!(stack, noun);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn rust_callback(arg: *mut c_void) -> *mut c_void {
|
use std::marker::PhantomData;
|
||||||
let closure: &mut Box<dyn FnMut() -> *mut c_void> =
|
|
||||||
unsafe { &mut *(arg as *mut Box<dyn FnMut() -> *mut c_void>) };
|
pub struct CCallback<'closure> {
|
||||||
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<F>(closure: &'closure mut F) -> Self where F: FnMut() -> Result {
|
||||||
|
let function: unsafe extern "C" fn(*mut c_void) -> *mut c_void = Self::call_closure::<F>;
|
||||||
|
|
||||||
|
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<F>(user_data: *mut c_void) -> *mut c_void where F: FnMut() -> Result {
|
||||||
|
let cb: &mut F = user_data.cast::<F>().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<F: FnMut() -> Result>(
|
pub fn call_with_guard<F: FnMut() -> Result>(
|
||||||
f: F,
|
f: &mut F,
|
||||||
stack: *const *mut c_void,
|
stack: *const *mut c_void,
|
||||||
alloc: *const *mut c_void,
|
alloc: *const *mut c_void,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
let boxed_f = Box::new(f);
|
let c = CCallback::new(f);
|
||||||
let res: Result = Err(Error::Deterministic(D(0)));
|
let mut result: Result = Ok(D(0));
|
||||||
let mut result = Box::new(res);
|
let result_ptr = &mut result as *mut _ as *mut c_void;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let raw_f = Box::into_raw(Box::new(boxed_f));
|
|
||||||
let result_ptr = &mut result as *mut _ as *mut c_void;
|
|
||||||
|
|
||||||
guard(
|
let err = guard(
|
||||||
Some(rust_callback),
|
Some(c.function as unsafe extern "C" fn(*mut c_void) -> *mut c_void),
|
||||||
raw_f as *mut c_void,
|
c.user_data as *mut c_void,
|
||||||
stack,
|
stack,
|
||||||
alloc,
|
alloc,
|
||||||
result_ptr,
|
result_ptr,
|
||||||
);
|
);
|
||||||
|
|
||||||
let _ = Box::from_raw(raw_f);
|
if let Ok(err) = GuardError::try_from(err) {
|
||||||
|
|
||||||
if !*result.is_null() {
|
|
||||||
let err = *(result as *const guard_err);
|
|
||||||
match err {
|
match err {
|
||||||
0 => { // sound
|
GuardError::GuardSound => {
|
||||||
let res = result as *mut Result;
|
return result;
|
||||||
*res
|
}
|
||||||
},
|
GuardError::GuardArmor => {
|
||||||
// TODO: handle other errors explicitly.
|
return Err(Error::Deterministic(D(0)));
|
||||||
_ => 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 {
|
||||||
else {
|
return Err(Error::Deterministic(D(0)));
|
||||||
(Box::from_raw(raw_f))()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
// (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use)
|
||||||
let nock = assert_no_alloc(|| {
|
let nock = assert_no_alloc(|| {
|
||||||
ensure_alloc_counters(|| {
|
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)?;
|
push_formula(&mut context.stack, formula, true)?;
|
||||||
|
eprint!("ares: pushed formula\r\n");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let work: NockWork = *context.stack.top();
|
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,
|
call_with_guard(work_closure, stack_ptr_ptr, alloc_ptr_ptr)
|
||||||
alloc_ptr_ptr,
|
|
||||||
std::ptr::null_mut() as *mut *mut c_void,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <pthread.h>
|
#include <setjmp.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -15,12 +15,13 @@
|
|||||||
static uint64_t *guard_p = 0;
|
static uint64_t *guard_p = 0;
|
||||||
static uint64_t **stack = 0;
|
static uint64_t **stack = 0;
|
||||||
static uint64_t **alloc = 0;
|
static uint64_t **alloc = 0;
|
||||||
|
static jmp_buf env_buffer;
|
||||||
|
|
||||||
volatile sig_atomic_t err = guard_sound;
|
volatile sig_atomic_t err = guard_sound;
|
||||||
|
|
||||||
|
|
||||||
// Center the guard page.
|
// Center the guard page.
|
||||||
guard_err _focus_guard() {
|
guard_err _focus_guard()
|
||||||
|
{
|
||||||
uint64_t *stack_p = *stack;
|
uint64_t *stack_p = *stack;
|
||||||
uint64_t *alloc_p = *alloc;
|
uint64_t *alloc_p = *alloc;
|
||||||
|
|
||||||
@ -61,8 +62,8 @@ guard_err _focus_guard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
guard_err _slash_guard(void *si_addr) {
|
guard_err _slash_guard(void *si_addr) {
|
||||||
// fprintf(stderr, "guard: slash at %p\r\n", 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: guard at %p\r\n", (void *) guard_p);
|
||||||
|
|
||||||
if (si_addr >= (void *)guard_p && si_addr < (void *)guard_p + GD_PAGESIZE) {
|
if (si_addr >= (void *)guard_p && si_addr < (void *)guard_p + GD_PAGESIZE) {
|
||||||
return _focus_guard();
|
return _focus_guard();
|
||||||
@ -71,17 +72,24 @@ guard_err _slash_guard(void *si_addr) {
|
|||||||
return guard_weird;
|
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) {
|
switch (sig) {
|
||||||
case SIGSEGV:
|
case SIGSEGV:
|
||||||
|
fprintf(stderr, "guard: sigsegv\r\n");
|
||||||
err = _slash_guard(si->si_addr);
|
err = _slash_guard(si->si_addr);
|
||||||
break;
|
break;
|
||||||
case SIGINT:
|
case SIGINT:
|
||||||
|
fprintf(stderr, "guard: sigint\r\n");
|
||||||
err = guard_erupt;
|
err = guard_erupt;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err != guard_sound) {
|
||||||
|
longjmp(env_buffer, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard_err _register_handler() {
|
guard_err _register_handler() {
|
||||||
@ -95,18 +103,27 @@ guard_err _register_handler() {
|
|||||||
return guard_weird;
|
return guard_weird;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "guard: registered handler\r\n");
|
||||||
return guard_sound;
|
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;
|
stack = (uint64_t**) stack_pp;
|
||||||
alloc = (uint64_t**) alloc_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: stack at %p\r\n", (void *) stack);
|
||||||
fprintf(stderr, "guard: alloc at %p\r\n", (void *) alloc);
|
fprintf(stderr, "guard: alloc at %p\r\n", (void *) alloc);
|
||||||
|
|
||||||
fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack);
|
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: alloc pointer at %p\r\n", (void *) *alloc);
|
||||||
|
fprintf(stderr, "guard: ret pointer at %p\r\n", (void *) ret);
|
||||||
|
|
||||||
if (guard_p == 0) {
|
if (guard_p == 0) {
|
||||||
fprintf(stderr, "guard: installing guard page\r\n");
|
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;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_t thread;
|
void *result;
|
||||||
int thread_err;
|
if (setjmp(env_buffer) == 0) {
|
||||||
thread_err = pthread_create(&thread, NULL, f, arg);
|
fprintf(stderr, "guard: calling f\r\n");
|
||||||
if (thread_err != 0) {
|
result = f(user_data);
|
||||||
err = guard_weird;
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
while (err == guard_sound) {
|
fprintf(stderr, "guard: jump buffer already set\r\n");
|
||||||
if (pthread_kill(thread, 0) != 0) {
|
if (err != guard_sound) {
|
||||||
|
fprintf(stderr, "guard: not sound\r\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
sleep(1);
|
else {
|
||||||
}
|
fprintf(stderr, "guard: assigning ret\r\n");
|
||||||
|
*(void **)ret = result;
|
||||||
void *thread_result;
|
}
|
||||||
pthread_join(thread, &thread_result);
|
|
||||||
*(void **)ret = thread_result;
|
|
||||||
|
|
||||||
if (err != guard_sound) {
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) {
|
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;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return guard_sound;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) {
|
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);
|
switch (err) {
|
||||||
*(void **)ret = (void *) &err;
|
case guard_armor:
|
||||||
return;
|
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;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,15 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
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
|
* Execute the given closure `f` within the memory arena between the
|
||||||
* `stack` and `alloc` pointers, with guard page protection. Write either
|
* `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
|
* error will be written to the `ret` pointer. The caller is then responsible
|
||||||
* for handling this error and aborting with a `bail:meme`.
|
* 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);
|
guard_err guard(
|
||||||
|
void *(*f)(void *),
|
||||||
typedef enum {
|
void *user_data,
|
||||||
guard_sound = 0, // job's done
|
void *const *stack_pp,
|
||||||
guard_armor = 1, // mprotect
|
void *const *alloc_pp,
|
||||||
guard_weird = 2, // strange state
|
void *ret
|
||||||
guard_spent = 3, // out of memory (bail:meme)
|
);
|
||||||
guard_erupt = 4, // sigint
|
|
||||||
} guard_err;
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -3,3 +3,9 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user