mirror of
https://github.com/urbit/ares.git
synced 2024-11-23 00:25:49 +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 bitvec::prelude::{BitSlice, Lsb0};
|
||||||
use either::*;
|
use either::*;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ffi::c_void;
|
use std::ffi::{c_void, c_ulonglong};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -394,20 +394,61 @@ fn debug_assertions(stack: &mut NockStack, noun: Noun) {
|
|||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub struct CCallback<'closure> {
|
/// See: https://users.rust-lang.org/t/passing-a-closure-to-an-external-c-ffi-library/100271/2
|
||||||
pub function: unsafe extern "C" fn(*mut c_void) -> *mut c_void,
|
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,
|
pub user_data: *mut c_void,
|
||||||
|
|
||||||
// without this it's too easy to accidentally drop the closure too soon
|
// without this it's too easy to accidentally drop the closure too soon
|
||||||
_lifetime: PhantomData<&'closure mut c_void>,
|
_lifetime: PhantomData<&'closure mut c_void>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'closure> CCallback<'closure> {
|
impl<'closure> WorkCallback<'closure> {
|
||||||
pub fn new<F>(closure: &'closure mut F) -> Self
|
pub fn new<F>(closure: &'closure mut F) -> Self
|
||||||
where
|
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!(
|
debug_assert_eq!(
|
||||||
std::mem::size_of::<&'closure mut F>(),
|
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
|
where
|
||||||
F: FnMut() -> Result,
|
F: FnMut(*mut c_void) -> Result,
|
||||||
{
|
{
|
||||||
let cb: &mut F = user_data.cast::<F>().as_mut().unwrap();
|
let cb: &mut F = user_data.cast::<F>().as_mut().unwrap();
|
||||||
let v = (*cb)();
|
let v = (*cb)(context_p);
|
||||||
permit_alloc(|| {
|
permit_alloc(|| {
|
||||||
let v_box = Box::new(v);
|
let v_box = Box::new(v);
|
||||||
let v_ptr = Box::into_raw(v_box);
|
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,
|
f: &mut F,
|
||||||
stack: *const *mut c_void,
|
low_f: &mut G,
|
||||||
alloc: *const *mut c_void,
|
high_f: &mut H,
|
||||||
|
context: &mut Context,
|
||||||
) -> Result {
|
) -> 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 mut ret: Result = Ok(D(0));
|
||||||
let ret_p = &mut ret as *mut _ as *mut c_void;
|
let ret_p = &mut ret as *mut _ as *mut c_void;
|
||||||
let ret_pp = &ret_p as *const *mut c_void;
|
let ret_pp = &ret_p as *const *mut c_void;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let err = guard(
|
let err = guard(
|
||||||
Some(c.function as unsafe extern "C" fn(*mut c_void) -> *mut c_void),
|
Some(work.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut c_void),
|
||||||
c.user_data as *mut c_void,
|
work.user_data as *mut c_void,
|
||||||
stack,
|
Some(low.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong),
|
||||||
alloc,
|
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,
|
ret_pp,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -488,15 +536,12 @@ pub fn call_with_guard<F: FnMut() -> Result>(
|
|||||||
|
|
||||||
/** Interpret nock */
|
/** Interpret nock */
|
||||||
pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Result {
|
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 terminator = Arc::clone(&TERMINATOR);
|
||||||
let orig_subject = subject; // for debugging
|
let orig_subject = subject; // for debugging
|
||||||
let snapshot = context.save();
|
let snapshot = context.save();
|
||||||
let virtual_frame: *const u64 = context.stack.get_frame_pointer();
|
let virtual_frame: *const u64 = context.stack.get_frame_pointer();
|
||||||
let mut res: Noun = D(0);
|
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
|
// Setup stack for Nock computation
|
||||||
unsafe {
|
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)
|
// (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(|| {
|
||||||
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)?;
|
push_formula(&mut context.stack, formula, true)?;
|
||||||
|
|
||||||
loop {
|
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
|
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 */
|
/** Current alloc pointer of this NockStack */
|
||||||
pub fn get_alloc_pointer(&self) -> *const u64 {
|
pub fn get_alloc_pointer(&self) -> *const u64 {
|
||||||
self.alloc_pointer
|
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 */
|
/** Start of the memory range for this NockStack */
|
||||||
pub fn get_start(&self) -> *const u64 {
|
pub fn get_start(&self) -> *const u64 {
|
||||||
self.start
|
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 {
|
unsafe fn prev_alloc_pointer_pointer(&self) -> *mut *mut u64 {
|
||||||
if !self.pc {
|
if !self.pc {
|
||||||
self.slot_pointer(ALLOC) as *mut *mut u64
|
self.slot_pointer(ALLOC) as *mut *mut u64
|
||||||
|
@ -11,23 +11,30 @@
|
|||||||
#define GD_PAGESIZE (1ULL << GD_PAGEBITS) /* 16K */
|
#define GD_PAGESIZE (1ULL << GD_PAGEBITS) /* 16K */
|
||||||
|
|
||||||
static uint64_t *guard_p = 0;
|
static uint64_t *guard_p = 0;
|
||||||
static uint64_t **stack = 0;
|
|
||||||
static uint64_t **alloc = 0;
|
|
||||||
static jmp_buf env_buffer;
|
static jmp_buf env_buffer;
|
||||||
volatile sig_atomic_t err = guard_sound;
|
volatile sig_atomic_t err = guard_sound;
|
||||||
static void (*prev_sigsegv_handler)(int, siginfo_t *, void *);
|
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.
|
// Center the guard page.
|
||||||
static guard_err
|
static guard_err
|
||||||
_focus_guard()
|
_focus_guard()
|
||||||
{
|
{
|
||||||
uint64_t *stack_p = *stack;
|
const uint64_t *low_p = low(bounds, context);
|
||||||
uint64_t *alloc_p = *alloc;
|
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.
|
// Check for strange situations.
|
||||||
if (stack_p == 0 || alloc_p == 0) {
|
if (low_p == 0 || high_p == 0) {
|
||||||
fprintf(stderr, "guard: stack or alloc pointer is null\r\n");
|
fprintf(stderr, "guard: low or high bound pointer is null\r\n");
|
||||||
return guard_weird;
|
return guard_weird;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,18 +45,9 @@ _focus_guard()
|
|||||||
return guard_armor;
|
return guard_armor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place the new guard page in the center.
|
// Place the new guard page in the low-aligned center.
|
||||||
if (stack_p > alloc_p) {
|
guard_p = (uint64_t *)low_p + ((high_p - low_p) / 2);
|
||||||
guard_p = stack_p - ((stack_p - alloc_p) / 2);
|
guard_p = (uint64_t *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1));
|
||||||
}
|
|
||||||
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));
|
|
||||||
|
|
||||||
// Mark the new guard page.
|
// Mark the new guard page.
|
||||||
if (guard_p != old_guard_p) {
|
if (guard_p != old_guard_p) {
|
||||||
@ -60,8 +58,6 @@ _focus_guard()
|
|||||||
return guard_spent;
|
return guard_spent;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "guard: installed guard page at %p\r\n", (void *) guard_p);
|
|
||||||
|
|
||||||
return guard_sound;
|
return guard_sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,12 +69,12 @@ _signal_handler(int sig, siginfo_t *si, void *unused)
|
|||||||
if (si->si_addr >= (void *)guard_p &&
|
if (si->si_addr >= (void *)guard_p &&
|
||||||
si->si_addr < (void *)guard_p + GD_PAGESIZE)
|
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();
|
err = _focus_guard();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
if (NULL != prev_sigsegv_handler) {
|
||||||
prev_sigsegv_handler(sig, si, unused);
|
prev_sigsegv_handler(sig, si, unused);
|
||||||
break;
|
break;
|
||||||
@ -124,27 +120,35 @@ _register_handler()
|
|||||||
|
|
||||||
guard_err
|
guard_err
|
||||||
guard(
|
guard(
|
||||||
void *(*f)(void *),
|
void *(*work_f)(void *, void *),
|
||||||
void *user_data,
|
void *work_data,
|
||||||
void *const *stack_pp,
|
const uint64_t *(*low_f)(void *, void *),
|
||||||
void *const *alloc_pp,
|
const uint64_t *(*high_f)(void *, void *),
|
||||||
|
void *bounds_data,
|
||||||
|
void *context_p,
|
||||||
void *const *ret
|
void *const *ret
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
stack = (uint64_t**) stack_pp;
|
// Set globals.
|
||||||
alloc = (uint64_t**) alloc_pp;
|
low = low_f;
|
||||||
|
high = high_f;
|
||||||
|
bounds= bounds_data;
|
||||||
|
context = context_p;
|
||||||
|
|
||||||
// fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack);
|
const uint64_t *low_p = low_f(bounds_data, context_p);
|
||||||
// fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc);
|
const uint64_t *high_p = high_f(bounds_data, context_p);
|
||||||
|
|
||||||
if (guard_p == 0) {
|
// fprintf(stderr, "guard: low: %p high: %p\r\n", (void *)low_p, (void *)high_p);
|
||||||
guard_err install_err = _focus_guard();
|
|
||||||
if (install_err != guard_sound) {
|
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");
|
fprintf(stderr, "guard: failed to install guard page\r\n");
|
||||||
err = install_err;
|
err = focus_err;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (_register_handler() != guard_sound) {
|
if (_register_handler() != guard_sound) {
|
||||||
err = guard_weird;
|
err = guard_weird;
|
||||||
@ -153,7 +157,7 @@ guard(
|
|||||||
|
|
||||||
void *result;
|
void *result;
|
||||||
if (sigsetjmp(env_buffer, 1) == 0) {
|
if (sigsetjmp(env_buffer, 1) == 0) {
|
||||||
result = f(user_data);
|
result = work_f(work_data, context_p);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (err != guard_sound) {
|
if (err != guard_sound) {
|
||||||
@ -167,15 +171,15 @@ guard(
|
|||||||
err = guard_armor;
|
err = guard_armor;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
// fprintf(stderr, "guard: sound; uninstalled guard page\r\n");
|
|
||||||
|
|
||||||
return guard_sound;
|
return guard_sound;
|
||||||
|
|
||||||
fail:
|
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: failed to uninstall guard page\r\n");
|
||||||
}
|
}
|
||||||
fprintf(stderr, "guard: fail; uninstalled guard page\r\n");
|
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case guard_armor:
|
case guard_armor:
|
||||||
fprintf(stderr, "guard: armor error\r\n");
|
fprintf(stderr, "guard: armor error\r\n");
|
||||||
|
@ -42,10 +42,12 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
guard_err
|
guard_err
|
||||||
guard(
|
guard(
|
||||||
void *(*f)(void *),
|
void *(*work_f)(void *, void *),
|
||||||
void *user_data,
|
void *work_data,
|
||||||
void *const *stack_pp,
|
const uint64_t *(*low_f)(void *, void *),
|
||||||
void *const *alloc_pp,
|
const uint64_t *(*high_f)(void *, void *),
|
||||||
|
void *bounds_data,
|
||||||
|
void *context_p,
|
||||||
void *const *ret
|
void *const *ret
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user