mirror of
https://github.com/urbit/ares.git
synced 2024-12-23 13:25:03 +03:00
guard: experiment with frame boundary callbacks
This commit is contained in:
parent
3620c7b894
commit
07ee849175
@ -21,7 +21,7 @@ use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters, permit_alloc};
|
||||
use bitvec::prelude::{BitSlice, Lsb0};
|
||||
use either::*;
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::c_void;
|
||||
use std::ffi::{c_void, c_ulonglong};
|
||||
use std::result;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
@ -394,20 +394,61 @@ fn debug_assertions(stack: &mut NockStack, noun: Noun) {
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct CCallback<'closure> {
|
||||
pub function: unsafe extern "C" fn(*mut c_void) -> *mut c_void,
|
||||
/// See: https://users.rust-lang.org/t/passing-a-closure-to-an-external-c-ffi-library/100271/2
|
||||
pub struct BoundsCallback<'closure> {
|
||||
pub function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong,
|
||||
pub bounds_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> BoundsCallback<'closure> {
|
||||
pub fn new<F>(closure: &'closure mut F) -> Self
|
||||
where
|
||||
F: FnMut(*mut c_void) -> *const c_ulonglong,
|
||||
{
|
||||
let function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong = 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,
|
||||
bounds_data: closure as *mut F as *mut c_void,
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_closure<F>(bounds_data: *mut c_void, context_p: *mut c_void) -> *const c_ulonglong
|
||||
where
|
||||
F: FnMut(*mut c_void) -> *const c_ulonglong,
|
||||
{
|
||||
let cb: &mut F = bounds_data.cast::<F>().as_mut().unwrap();
|
||||
(*cb)(context_p)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkCallback<'closure> {
|
||||
pub function: unsafe extern "C" fn(*mut c_void, *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> {
|
||||
impl<'closure> WorkCallback<'closure> {
|
||||
pub fn new<F>(closure: &'closure mut F) -> Self
|
||||
where
|
||||
F: FnMut() -> Result,
|
||||
F: FnMut(*mut c_void) -> Result,
|
||||
{
|
||||
let function: unsafe extern "C" fn(*mut c_void) -> *mut c_void = Self::call_closure::<F>;
|
||||
let function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut c_void = Self::call_closure::<F>;
|
||||
|
||||
debug_assert_eq!(
|
||||
std::mem::size_of::<&'closure mut F>(),
|
||||
@ -425,12 +466,12 @@ impl<'closure> CCallback<'closure> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_closure<F>(user_data: *mut c_void) -> *mut c_void
|
||||
unsafe extern "C" fn call_closure<F>(user_data: *mut c_void, context_p: *mut c_void) -> *mut c_void
|
||||
where
|
||||
F: FnMut() -> Result,
|
||||
F: FnMut(*mut c_void) -> Result,
|
||||
{
|
||||
let cb: &mut F = user_data.cast::<F>().as_mut().unwrap();
|
||||
let v = (*cb)();
|
||||
let v = (*cb)(context_p);
|
||||
permit_alloc(|| {
|
||||
let v_box = Box::new(v);
|
||||
let v_ptr = Box::into_raw(v_box);
|
||||
@ -439,22 +480,29 @@ impl<'closure> CCallback<'closure> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_with_guard<F: FnMut() -> Result>(
|
||||
pub fn call_with_guard<F: FnMut(*mut c_void) -> Result, G: FnMut(*mut c_void) -> *const c_ulonglong, H: FnMut(*mut c_void) -> *const c_ulonglong>(
|
||||
f: &mut F,
|
||||
stack: *const *mut c_void,
|
||||
alloc: *const *mut c_void,
|
||||
low_f: &mut G,
|
||||
high_f: &mut H,
|
||||
context: &mut Context,
|
||||
) -> Result {
|
||||
let c = CCallback::new(f);
|
||||
let work = WorkCallback::new(f);
|
||||
let low = BoundsCallback::new(low_f);
|
||||
let high = BoundsCallback::new(high_f);
|
||||
let context_p = context as *mut Context as *mut c_void;
|
||||
|
||||
let mut ret: Result = Ok(D(0));
|
||||
let ret_p = &mut ret as *mut _ as *mut c_void;
|
||||
let ret_pp = &ret_p as *const *mut c_void;
|
||||
|
||||
unsafe {
|
||||
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,
|
||||
Some(work.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut c_void),
|
||||
work.user_data as *mut c_void,
|
||||
Some(low.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong),
|
||||
Some(high.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong),
|
||||
high.bounds_data as *mut c_void,
|
||||
context_p,
|
||||
ret_pp,
|
||||
);
|
||||
|
||||
@ -488,15 +536,12 @@ pub fn call_with_guard<F: FnMut() -> Result>(
|
||||
|
||||
/** Interpret nock */
|
||||
pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Result {
|
||||
// print the addresses of the context.stack.stack_pointer and context.stack.alloc_pointer
|
||||
let terminator = Arc::clone(&TERMINATOR);
|
||||
let orig_subject = subject; // for debugging
|
||||
let snapshot = context.save();
|
||||
let virtual_frame: *const u64 = context.stack.get_frame_pointer();
|
||||
let mut res: Noun = D(0);
|
||||
let stack_p = context.stack.get_stack_pointer() as *mut c_void;
|
||||
let alloc_p = context.stack.get_alloc_pointer() as *mut c_void;
|
||||
let stack_pp = &stack_p as *const *mut c_void;
|
||||
let alloc_pp = &alloc_p as *const *mut c_void;
|
||||
|
||||
// Setup stack for Nock computation
|
||||
unsafe {
|
||||
@ -523,7 +568,24 @@ 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(|| {
|
||||
let work_closure = &mut || unsafe {
|
||||
let low_f = &mut |context_p: *mut c_void| {
|
||||
let context = unsafe { &mut *(context_p as *mut Context) };
|
||||
if context.stack.is_west() {
|
||||
context.stack.get_stack_pointer() as *const c_ulonglong
|
||||
} else {
|
||||
context.stack.get_alloc_pointer() as *const c_ulonglong
|
||||
}
|
||||
};
|
||||
let high_f = &mut |context_p: *mut c_void| {
|
||||
let context = unsafe { &mut *(context_p as *mut Context) };
|
||||
if context.stack.is_west() {
|
||||
context.stack.get_alloc_pointer() as *const c_ulonglong
|
||||
} else {
|
||||
context.stack.get_stack_pointer() as *const c_ulonglong
|
||||
}
|
||||
};
|
||||
let work_closure = &mut |context_p: *mut c_void| unsafe {
|
||||
let context = &mut *(context_p as *mut Context);
|
||||
push_formula(&mut context.stack, formula, true)?;
|
||||
|
||||
loop {
|
||||
@ -1072,7 +1134,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
|
||||
};
|
||||
}
|
||||
};
|
||||
call_with_guard(work_closure, stack_pp, alloc_pp)
|
||||
call_with_guard(work_closure, low_f, high_f, context)
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -147,11 +147,21 @@ impl NockStack {
|
||||
self.stack_pointer
|
||||
}
|
||||
|
||||
/** Current stack pointer's address */
|
||||
pub fn get_stack_pointer_pointer(&self) -> *const *mut u64 {
|
||||
&self.stack_pointer as *const *mut u64
|
||||
}
|
||||
|
||||
/** Current alloc pointer of this NockStack */
|
||||
pub fn get_alloc_pointer(&self) -> *const u64 {
|
||||
self.alloc_pointer
|
||||
}
|
||||
|
||||
/** Current alloc pointer's address */
|
||||
pub fn get_alloc_pointer_pointer(&self) -> *const *mut u64 {
|
||||
&self.alloc_pointer as *const *mut u64
|
||||
}
|
||||
|
||||
/** Start of the memory range for this NockStack */
|
||||
pub fn get_start(&self) -> *const u64 {
|
||||
self.start
|
||||
@ -255,7 +265,7 @@ impl NockStack {
|
||||
}
|
||||
}
|
||||
|
||||
/** Pointer to where the previous stack pointer is saved in a frame */
|
||||
/** Pointer to where the previous alloc pointer is saved in a frame */
|
||||
unsafe fn prev_alloc_pointer_pointer(&self) -> *mut *mut u64 {
|
||||
if !self.pc {
|
||||
self.slot_pointer(ALLOC) as *mut *mut u64
|
||||
|
@ -11,23 +11,30 @@
|
||||
#define GD_PAGESIZE (1ULL << GD_PAGEBITS) /* 16K */
|
||||
|
||||
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;
|
||||
static void (*prev_sigsegv_handler)(int, siginfo_t *, void *);
|
||||
|
||||
static const uint64_t *(*low)(void *, void *) = 0;
|
||||
static const uint64_t *(*high)(void *, void *) = 0;
|
||||
static void *bounds = 0;
|
||||
static void *context = 0;
|
||||
|
||||
// Center the guard page.
|
||||
static guard_err
|
||||
_focus_guard()
|
||||
{
|
||||
uint64_t *stack_p = *stack;
|
||||
uint64_t *alloc_p = *alloc;
|
||||
const uint64_t *low_p = low(bounds, context);
|
||||
const uint64_t *high_p = high(bounds, context);
|
||||
|
||||
// Check if we're spent already.
|
||||
if (low_p == high_p || low_p > high_p) {
|
||||
return guard_spent;
|
||||
}
|
||||
|
||||
// Check for strange situations.
|
||||
if (stack_p == 0 || alloc_p == 0) {
|
||||
fprintf(stderr, "guard: stack or alloc pointer is null\r\n");
|
||||
if (low_p == 0 || high_p == 0) {
|
||||
fprintf(stderr, "guard: low or high bound pointer is null\r\n");
|
||||
return guard_weird;
|
||||
}
|
||||
|
||||
@ -38,18 +45,9 @@ _focus_guard()
|
||||
return guard_armor;
|
||||
}
|
||||
|
||||
// Place the new guard page in the center.
|
||||
if (stack_p > alloc_p) {
|
||||
guard_p = stack_p - ((stack_p - alloc_p) / 2);
|
||||
}
|
||||
else if (stack_p < alloc_p) {
|
||||
guard_p = stack_p + ((alloc_p - stack_p) / 2);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "guard: weird; stack and alloc pointers are equal\r\n");
|
||||
return guard_weird;
|
||||
}
|
||||
guard_p = (void *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1));
|
||||
// Place the new guard page in the low-aligned center.
|
||||
guard_p = (uint64_t *)low_p + ((high_p - low_p) / 2);
|
||||
guard_p = (uint64_t *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1));
|
||||
|
||||
// Mark the new guard page.
|
||||
if (guard_p != old_guard_p) {
|
||||
@ -60,8 +58,6 @@ _focus_guard()
|
||||
return guard_spent;
|
||||
}
|
||||
|
||||
fprintf(stderr, "guard: installed guard page at %p\r\n", (void *) guard_p);
|
||||
|
||||
return guard_sound;
|
||||
}
|
||||
|
||||
@ -73,12 +69,12 @@ _signal_handler(int sig, siginfo_t *si, void *unused)
|
||||
if (si->si_addr >= (void *)guard_p &&
|
||||
si->si_addr < (void *)guard_p + GD_PAGESIZE)
|
||||
{
|
||||
fprintf(stderr, "guard: fault in guard\r\n");
|
||||
fprintf(stderr, "guard: fault in guard: %p\r\n", si->si_addr);
|
||||
err = _focus_guard();
|
||||
break;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "guard: fault outside guard\r\n");
|
||||
fprintf(stderr, "guard: fault outside guard %p\r\n", si->si_addr);
|
||||
if (NULL != prev_sigsegv_handler) {
|
||||
prev_sigsegv_handler(sig, si, unused);
|
||||
break;
|
||||
@ -124,26 +120,34 @@ _register_handler()
|
||||
|
||||
guard_err
|
||||
guard(
|
||||
void *(*f)(void *),
|
||||
void *user_data,
|
||||
void *const *stack_pp,
|
||||
void *const *alloc_pp,
|
||||
void *(*work_f)(void *, void *),
|
||||
void *work_data,
|
||||
const uint64_t *(*low_f)(void *, void *),
|
||||
const uint64_t *(*high_f)(void *, void *),
|
||||
void *bounds_data,
|
||||
void *context_p,
|
||||
void *const *ret
|
||||
)
|
||||
{
|
||||
stack = (uint64_t**) stack_pp;
|
||||
alloc = (uint64_t**) alloc_pp;
|
||||
// Set globals.
|
||||
low = low_f;
|
||||
high = high_f;
|
||||
bounds= bounds_data;
|
||||
context = context_p;
|
||||
|
||||
// fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack);
|
||||
// fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc);
|
||||
const uint64_t *low_p = low_f(bounds_data, context_p);
|
||||
const uint64_t *high_p = high_f(bounds_data, context_p);
|
||||
|
||||
if (guard_p == 0) {
|
||||
guard_err install_err = _focus_guard();
|
||||
if (install_err != guard_sound) {
|
||||
fprintf(stderr, "guard: failed to install guard page\r\n");
|
||||
err = install_err;
|
||||
goto fail;
|
||||
}
|
||||
// fprintf(stderr, "guard: low: %p high: %p\r\n", (void *)low_p, (void *)high_p);
|
||||
|
||||
const unsigned long free_mb = (unsigned long)(high_p - low_p) / 1024 / 1024;
|
||||
// fprintf(stderr, "guard: free space: %lu MB\r\n", free_mb);
|
||||
|
||||
guard_err focus_err = _focus_guard();
|
||||
if (focus_err != guard_sound && focus_err != guard_spent) {
|
||||
fprintf(stderr, "guard: failed to install guard page\r\n");
|
||||
err = focus_err;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (_register_handler() != guard_sound) {
|
||||
@ -153,7 +157,7 @@ guard(
|
||||
|
||||
void *result;
|
||||
if (sigsetjmp(env_buffer, 1) == 0) {
|
||||
result = f(user_data);
|
||||
result = work_f(work_data, context_p);
|
||||
}
|
||||
else {
|
||||
if (err != guard_sound) {
|
||||
@ -167,15 +171,15 @@ guard(
|
||||
err = guard_armor;
|
||||
goto fail;
|
||||
}
|
||||
// fprintf(stderr, "guard: sound; uninstalled guard page\r\n");
|
||||
|
||||
return guard_sound;
|
||||
|
||||
fail:
|
||||
if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) {
|
||||
if (guard_p != NULL &&
|
||||
mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1)
|
||||
{
|
||||
fprintf(stderr, "guard: failed to uninstall guard page\r\n");
|
||||
}
|
||||
fprintf(stderr, "guard: fail; uninstalled guard page\r\n");
|
||||
switch (err) {
|
||||
case guard_armor:
|
||||
fprintf(stderr, "guard: armor error\r\n");
|
||||
|
@ -42,10 +42,12 @@ typedef enum {
|
||||
*/
|
||||
guard_err
|
||||
guard(
|
||||
void *(*f)(void *),
|
||||
void *user_data,
|
||||
void *const *stack_pp,
|
||||
void *const *alloc_pp,
|
||||
void *(*work_f)(void *, void *),
|
||||
void *work_data,
|
||||
const uint64_t *(*low_f)(void *, void *),
|
||||
const uint64_t *(*high_f)(void *, void *),
|
||||
void *bounds_data,
|
||||
void *context_p,
|
||||
void *const *ret
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user