Merge branch 'as/serf-guard' into msl/guard

- compiles
- tests fail
- doesn't run
This commit is contained in:
Alex Shelkovnykov 2024-02-14 12:52:58 +09:00
commit 1e44565c53
15 changed files with 467 additions and 542 deletions

View File

@ -36,18 +36,18 @@ signal-hook = "0.3"
static_assertions = "1.1.0"
[build-dependencies]
autotools = "0.2.6"
cc = "1.0.79"
autotools = "0.2"
cc = "1.0"
[[bin]]
name = "ares"
path = "src/main.rs"
[profile.dev]
opt-level = 0
opt-level = 3
[profile.dev.package."*"]
opt-level = 0
opt-level = 3
# run with e.g. 'cargo build --features check_forwarding,check_acyclic'
[features]

114
rust/ares/src/guard.rs Normal file
View File

@ -0,0 +1,114 @@
use crate::interpreter::{Error, Mote, Result};
use crate::noun::D;
use ares_guard::*;
use assert_no_alloc::permit_alloc;
use std::convert::TryFrom;
use std::ffi::c_void;
use std::marker::PhantomData;
#[derive(Debug)]
pub enum GuardError {
InvalidSignal,
MemoryProtection,
NullPointer,
OutOfMemory,
Setup,
Unknown,
}
impl From<u32> for GuardError {
fn from(value: u32) -> Self {
match value {
GUARD_NULL => Self::NullPointer,
GUARD_SIGNAL => Self::InvalidSignal,
GUARD_OOM => Self::OutOfMemory,
x if (x & GUARD_MPROTECT) != 0 => Self::MemoryProtection,
x if (x & (GUARD_MALLOC | GUARD_SIGACTION)) != 0 => Self::Setup,
_ => Self::Unknown,
}
}
}
pub struct CCallback<'closure> {
pub function: unsafe extern "C" fn(*mut c_void) -> *mut c_void,
pub input: *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,
input: closure as *mut F as *mut c_void,
_lifetime: PhantomData,
}
}
unsafe extern "C" fn call_closure<F>(input: *mut c_void) -> *mut c_void
where
F: FnMut() -> Result,
{
let cb: &mut F = input.cast::<F>().as_mut().unwrap();
let v = (*cb)();
permit_alloc(|| {
let v_box = Box::new(v);
let v_ptr = Box::into_raw(v_box);
v_ptr as *mut c_void
})
}
}
pub fn call_with_guard<F: FnMut() -> Result>(
stack_pp: *const *const u64,
alloc_pp: *const *const u64,
closure: &mut F,
) -> Result {
let cb = CCallback::new(closure);
let mut ret_p: *mut c_void = std::ptr::null_mut();
let ret_pp = &mut ret_p as *mut *mut c_void;
unsafe {
let res = guard(
Some(cb.function as unsafe extern "C" fn(*mut c_void) -> *mut c_void),
cb.input,
stack_pp as *const usize,
alloc_pp as *const usize,
ret_pp,
);
// eprintln!("\r BEFORE:");
// eprintln!("\r ret = {:?}", ret);
// eprintln!("\r ret_p = {:p}, {:?}", ret_p as *mut Result, *(ret_p as *mut Result));
// eprintln!("\r ret_pp = {:p}, {:p}, {:?}", ret_pp, *ret_pp, **(ret_pp as *mut *mut Result));
if res == 0 {
// TODO: come back to this
permit_alloc(|| {
let result_box = Box::from_raw(ret_p as *mut Result);
*result_box
})
} else {
let err = GuardError::from(u32::try_from(res).unwrap());
match err {
GuardError::OutOfMemory => Err(Error::NonDeterministic(Mote::Meme, D(0))),
_ => {
panic!("serf: guard: unexpected error {:?}", err);
}
}
}
}
}

View File

@ -1,6 +1,7 @@
use crate::assert_acyclic;
use crate::assert_no_forwarding_pointers;
use crate::assert_no_junior_pointers;
use crate::guard::call_with_guard;
use crate::hamt::Hamt;
use crate::jets::cold;
use crate::jets::cold::Cold;
@ -15,13 +16,10 @@ 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::*;
use ares_macros::tas;
use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters, permit_alloc};
use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters};
use bitvec::prelude::{BitSlice, Lsb0};
use either::*;
use std::convert::TryFrom;
use std::ffi::{c_ulonglong, c_void};
use std::result;
use std::sync::atomic::Ordering;
use std::sync::Arc;
@ -327,26 +325,6 @@ pub enum Error {
NonDeterministic(Mote, Noun), // mote, trace
}
pub enum GuardError {
GuardSound = GUARD_SOUND as isize,
GuardArmor = GUARD_ARMOR as isize,
GuardWeird = GUARD_WEIRD as isize,
GuardSpent = GUARD_SPENT 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),
_ => Err(()),
}
}
}
impl Preserve for Error {
unsafe fn preserve(&mut self, stack: &mut NockStack) {
match self {
@ -382,6 +360,7 @@ impl From<cold::Error> for Error {
pub type Result = result::Result<Noun, Error>;
const BAIL_EXIT: Result = Err(Error::Deterministic(Mote::Exit, D(0)));
const BAIL_FAIL: Result = Err(Error::NonDeterministic(Mote::Fail, D(0)));
const BAIL_INTR: Result = Err(Error::NonDeterministic(Mote::Intr, D(0)));
#[allow(unused_variables)]
@ -391,166 +370,8 @@ fn debug_assertions(stack: &mut NockStack, noun: Noun) {
assert_no_junior_pointers!(stack, noun);
}
use std::marker::PhantomData;
/// 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> WorkCallback<'closure> {
pub fn new<F>(closure: &'closure mut F) -> Self
where
F: FnMut(*mut c_void) -> Result,
{
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>(),
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,
context_p: *mut c_void,
) -> *mut c_void
where
F: FnMut(*mut c_void) -> Result,
{
let cb: &mut F = user_data.cast::<F>().as_mut().unwrap();
let v = (*cb)(context_p);
permit_alloc(|| {
let v_box = Box::new(v);
let v_ptr = Box::into_raw(v_box);
v_ptr as *mut c_void
})
}
}
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,
>(
work_f: &mut F,
low_f: &mut G,
high_f: &mut H,
context_p: *mut Context,
) -> Result {
let work = WorkCallback::new(work_f);
let low = BoundsCallback::new(low_f);
let high = BoundsCallback::new(high_f);
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 guard_error = guard(
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 as *mut c_void,
ret_pp,
);
if let Ok(err) = GuardError::try_from(guard_error) {
match err {
GuardError::GuardSound => {
permit_alloc(|| {
let result_box = Box::from_raw(ret_p as *mut Result);
let result = *result_box;
return result;
})
}
GuardError::GuardArmor => {
// XX
panic!("guard: couldn't place guard page\r\n");
}
GuardError::GuardWeird => {
return Err(Error::Deterministic(Mote::Exit, D(0)));
}
GuardError::GuardSpent => {
return Err(Error::NonDeterministic(Mote::Meme, D(0)));
}
}
} else {
return Err(Error::Deterministic(Mote::Exit, D(0)));
}
}
}
/** 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();
@ -559,31 +380,14 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
// Setup stack for Nock computation
unsafe {
(*context).stack.frame_push(2);
context.stack.frame_push(2);
// Bottom of mean stack
*((*context).stack.local_noun_pointer(0)) = D(0);
*(context.stack.local_noun_pointer(0)) = D(0);
// Bottom of trace stack
*((*context).stack.local_noun_pointer(1) as *mut *const TraceStack) = std::ptr::null();
*(context.stack.local_noun_pointer(1) as *mut *const TraceStack) = std::ptr::null();
*((*context).stack.push()) = NockWork::Done;
};
let low_f = &mut |context_p: *mut c_void| {
let bounds_ctx = unsafe { &mut *(context_p as *mut Context) };
if bounds_ctx.stack.is_west() {
bounds_ctx.stack.get_stack_pointer() as *const c_ulonglong
} else {
bounds_ctx.stack.get_alloc_pointer() as *const c_ulonglong
}
};
let high_f = &mut |context_p: *mut c_void| {
let bounds_ctx = unsafe { &mut *(context_p as *mut Context) };
if bounds_ctx.stack.is_west() {
bounds_ctx.stack.get_alloc_pointer() as *const c_ulonglong
} else {
bounds_ctx.stack.get_stack_pointer() as *const c_ulonglong
}
*(context.stack.push()) = NockWork::Done;
};
// DO NOT REMOVE THIS ASSERTION
@ -599,23 +403,25 @@ 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_f = &mut |context_p: *mut c_void| unsafe {
let work_ctx = &mut *(context_p as *mut Context);
push_formula(&mut work_ctx.stack, formula, true)?;
let stack_pp = context.stack.get_stack_pointer_pointer() as *const *const u64;
let alloc_pp = context.stack.get_alloc_pointer_pointer() as *const *const u64;
let work_f = &mut || unsafe {
push_formula(&mut context.stack, formula, true)?;
loop {
let work: NockWork = *work_ctx.stack.top();
let work: NockWork = *context.stack.top();
match work {
NockWork::Done => {
write_trace(work_ctx);
write_trace(context);
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
debug_assertions(stack, orig_subject);
debug_assertions(stack, subject);
debug_assertions(stack, res);
stack.preserve(&mut work_ctx.cache);
stack.preserve(&mut work_ctx.cold);
stack.preserve(&mut work_ctx.warm);
stack.preserve(&mut context.cache);
stack.preserve(&mut context.cold);
stack.preserve(&mut context.warm);
stack.preserve(&mut res);
stack.frame_pop();
@ -625,16 +431,16 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
break Ok(res);
}
NockWork::Ret => {
write_trace(work_ctx);
write_trace(context);
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
debug_assertions(stack, orig_subject);
debug_assertions(stack, subject);
debug_assertions(stack, res);
stack.preserve(&mut work_ctx.cache);
stack.preserve(&mut work_ctx.cold);
stack.preserve(&mut work_ctx.warm);
stack.preserve(&mut context.cache);
stack.preserve(&mut context.cold);
stack.preserve(&mut context.warm);
stack.preserve(&mut res);
stack.frame_pop();
@ -644,17 +450,17 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
NockWork::WorkCons(mut cons) => match cons.todo {
TodoCons::ComputeHead => {
cons.todo = TodoCons::ComputeTail;
*work_ctx.stack.top() = NockWork::WorkCons(cons);
push_formula(&mut work_ctx.stack, cons.head, false)?;
*context.stack.top() = NockWork::WorkCons(cons);
push_formula(&mut context.stack, cons.head, false)?;
}
TodoCons::ComputeTail => {
cons.todo = TodoCons::Cons;
cons.head = res;
*work_ctx.stack.top() = NockWork::WorkCons(cons);
push_formula(&mut work_ctx.stack, cons.tail, false)?;
*context.stack.top() = NockWork::WorkCons(cons);
push_formula(&mut context.stack, cons.tail, false)?;
}
TodoCons::Cons => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
res = T(stack, &[cons.head, res]);
stack.pop::<NockWork>();
}
@ -662,7 +468,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
NockWork::Work0(zero) => {
if let Ok(noun) = subject.slot_atom(zero.axis) {
res = noun;
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
} else {
// Axis invalid for input Noun
break BAIL_EXIT;
@ -670,27 +476,27 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
NockWork::Work1(once) => {
res = once.noun;
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
NockWork::Work2(mut vale) => {
if (*terminator).load(Ordering::Relaxed) {
break Err(Error::NonDeterministic(Mote::Intr, D(0)));
break BAIL_INTR;
}
match vale.todo {
Todo2::ComputeSubject => {
vale.todo = Todo2::ComputeFormula;
*work_ctx.stack.top() = NockWork::Work2(vale);
push_formula(&mut work_ctx.stack, vale.subject, false)?;
*context.stack.top() = NockWork::Work2(vale);
push_formula(&mut context.stack, vale.subject, false)?;
}
Todo2::ComputeFormula => {
vale.todo = Todo2::ComputeResult;
vale.subject = res;
*work_ctx.stack.top() = NockWork::Work2(vale);
push_formula(&mut work_ctx.stack, vale.formula, false)?;
*context.stack.top() = NockWork::Work2(vale);
push_formula(&mut context.stack, vale.formula, false)?;
}
Todo2::ComputeResult => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
if vale.tail {
stack.pop::<NockWork>();
subject = vale.subject;
@ -710,7 +516,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
}
Todo2::RestoreSubject => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
subject = vale.subject;
stack.pop::<NockWork>();
@ -724,24 +530,24 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
NockWork::Work3(mut thee) => match thee.todo {
Todo3::ComputeChild => {
thee.todo = Todo3::ComputeType;
*work_ctx.stack.top() = NockWork::Work3(thee);
push_formula(&mut work_ctx.stack, thee.child, false)?;
*context.stack.top() = NockWork::Work3(thee);
push_formula(&mut context.stack, thee.child, false)?;
}
Todo3::ComputeType => {
res = if res.is_cell() { D(0) } else { D(1) };
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
},
NockWork::Work4(mut four) => match four.todo {
Todo4::ComputeChild => {
four.todo = Todo4::Increment;
*work_ctx.stack.top() = NockWork::Work4(four);
push_formula(&mut work_ctx.stack, four.child, false)?;
*context.stack.top() = NockWork::Work4(four);
push_formula(&mut context.stack, four.child, false)?;
}
Todo4::Increment => {
if let Ok(atom) = res.as_atom() {
res = inc(&mut work_ctx.stack, atom).as_noun();
work_ctx.stack.pop::<NockWork>();
res = inc(&mut context.stack, atom).as_noun();
context.stack.pop::<NockWork>();
} else {
// Cannot increment (Nock 4) a cell
break BAIL_EXIT;
@ -751,17 +557,17 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
NockWork::Work5(mut five) => match five.todo {
Todo5::ComputeLeftChild => {
five.todo = Todo5::ComputeRightChild;
*work_ctx.stack.top() = NockWork::Work5(five);
push_formula(&mut work_ctx.stack, five.left, false)?;
*context.stack.top() = NockWork::Work5(five);
push_formula(&mut context.stack, five.left, false)?;
}
Todo5::ComputeRightChild => {
five.todo = Todo5::TestEquals;
five.left = res;
*work_ctx.stack.top() = NockWork::Work5(five);
push_formula(&mut work_ctx.stack, five.right, false)?;
*context.stack.top() = NockWork::Work5(five);
push_formula(&mut context.stack, five.right, false)?;
}
Todo5::TestEquals => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
let saved_value_ptr = &mut five.left;
res = if unifying_equality(stack, &mut res, saved_value_ptr) {
D(0)
@ -774,11 +580,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
NockWork::Work6(mut cond) => match cond.todo {
Todo6::ComputeTest => {
cond.todo = Todo6::ComputeBranch;
*work_ctx.stack.top() = NockWork::Work6(cond);
push_formula(&mut work_ctx.stack, cond.test, false)?;
*context.stack.top() = NockWork::Work6(cond);
push_formula(&mut context.stack, cond.test, false)?;
}
Todo6::ComputeBranch => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
stack.pop::<NockWork>();
if let Left(direct) = res.as_either_direct_allocated() {
if direct.data() == 0 {
@ -798,11 +604,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
NockWork::Work7(mut pose) => match pose.todo {
Todo7::ComputeSubject => {
pose.todo = Todo7::ComputeResult;
*work_ctx.stack.top() = NockWork::Work7(pose);
push_formula(&mut work_ctx.stack, pose.subject, false)?;
*context.stack.top() = NockWork::Work7(pose);
push_formula(&mut context.stack, pose.subject, false)?;
}
Todo7::ComputeResult => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
if pose.tail {
stack.pop::<NockWork>();
subject = res;
@ -817,17 +623,17 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
Todo7::RestoreSubject => {
subject = pose.subject;
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
},
NockWork::Work8(mut pins) => match pins.todo {
Todo8::ComputeSubject => {
pins.todo = Todo8::ComputeResult;
*work_ctx.stack.top() = NockWork::Work8(pins);
push_formula(&mut work_ctx.stack, pins.pin, false)?;
*context.stack.top() = NockWork::Work8(pins);
push_formula(&mut context.stack, pins.pin, false)?;
}
Todo8::ComputeResult => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
if pins.tail {
subject = T(stack, &[res, subject]);
stack.pop::<NockWork>();
@ -842,32 +648,32 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
Todo8::RestoreSubject => {
subject = pins.pin;
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
},
NockWork::Work9(mut kale) => {
if (*terminator).load(Ordering::Relaxed) {
break Err(Error::NonDeterministic(Mote::Intr, D(0)));
break BAIL_INTR;
}
match kale.todo {
Todo9::ComputeCore => {
kale.todo = Todo9::ComputeResult;
*work_ctx.stack.top() = NockWork::Work9(kale);
push_formula(&mut work_ctx.stack, kale.core, false)?;
*context.stack.top() = NockWork::Work9(kale);
push_formula(&mut context.stack, kale.core, false)?;
}
Todo9::ComputeResult => {
if let Ok(mut formula) = res.slot_atom(kale.axis) {
if !cfg!(feature = "sham_hints") {
if let Some((jet, _path)) = work_ctx.warm.find_jet(
&mut work_ctx.stack,
if let Some((jet, _path)) = context.warm.find_jet(
&mut context.stack,
&mut res,
&mut formula,
) {
match jet(work_ctx, res) {
match jet(context, res) {
Ok(jet_res) => {
res = jet_res;
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
continue;
}
Err(JetErr::Punt) => {}
@ -878,16 +684,16 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
};
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
if kale.tail {
stack.pop::<NockWork>();
// We could trace on 2 as well, but 2 only comes from Hoon via
// '.*', so we can assume it's never directly used to invoke
// jetted code.
if work_ctx.trace_info.is_some() {
if context.trace_info.is_some() {
if let Some(path) =
work_ctx.cold.matches(stack, &mut res)
context.cold.matches(stack, &mut res)
{
append_trace(stack, path);
};
@ -912,9 +718,9 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
// We could trace on 2 as well, but 2 only comes from Hoon via
// '.*', so we can assume it's never directly used to invoke
// jetted code.
if work_ctx.trace_info.is_some() {
if context.trace_info.is_some() {
if let Some(path) =
work_ctx.cold.matches(stack, &mut res)
context.cold.matches(stack, &mut res)
{
append_trace(stack, path);
};
@ -926,7 +732,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
}
Todo9::RestoreSubject => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
subject = kale.core;
stack.pop::<NockWork>();
@ -941,35 +747,35 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
match diet.todo {
Todo10::ComputeTree => {
diet.todo = Todo10::ComputePatch; // should we compute patch then tree?
*work_ctx.stack.top() = NockWork::Work10(diet);
push_formula(&mut work_ctx.stack, diet.tree, false)?;
*context.stack.top() = NockWork::Work10(diet);
push_formula(&mut context.stack, diet.tree, false)?;
}
Todo10::ComputePatch => {
diet.todo = Todo10::Edit;
diet.tree = res;
*work_ctx.stack.top() = NockWork::Work10(diet);
push_formula(&mut work_ctx.stack, diet.patch, false)?;
*context.stack.top() = NockWork::Work10(diet);
push_formula(&mut context.stack, diet.patch, false)?;
}
Todo10::Edit => {
res = edit(
&mut work_ctx.stack,
&mut context.stack,
diet.axis.as_bitslice(),
res,
diet.tree,
);
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
}
}
NockWork::Work11D(mut dint) => match dint.todo {
Todo11D::ComputeHint => {
if let Some(ret) = hint::match_pre_hint(
work_ctx, subject, dint.tag, dint.hint, dint.body,
context, subject, dint.tag, dint.hint, dint.body,
) {
match ret {
Ok(found) => {
res = found;
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
Err(err) => {
break Err(err);
@ -977,13 +783,13 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
} else {
dint.todo = Todo11D::ComputeResult;
*work_ctx.stack.top() = NockWork::Work11D(dint);
push_formula(&mut work_ctx.stack, dint.hint, false)?;
*context.stack.top() = NockWork::Work11D(dint);
push_formula(&mut context.stack, dint.hint, false)?;
}
}
Todo11D::ComputeResult => {
if let Some(ret) = hint::match_pre_nock(
work_ctx,
context,
subject,
dint.tag,
Some((dint.hint, res)),
@ -992,7 +798,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
match ret {
Ok(found) => {
res = found;
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
Err(err) => {
break Err(err);
@ -1000,18 +806,18 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
} else {
if dint.tail {
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
} else {
dint.todo = Todo11D::Done;
dint.hint = res;
*work_ctx.stack.top() = NockWork::Work11D(dint);
*context.stack.top() = NockWork::Work11D(dint);
}
push_formula(&mut work_ctx.stack, dint.body, dint.tail)?;
push_formula(&mut context.stack, dint.body, dint.tail)?;
}
}
Todo11D::Done => {
if let Some(found) = hint::match_post_nock(
work_ctx,
context,
subject,
dint.tag,
Some(dint.hint),
@ -1020,18 +826,18 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
) {
res = found;
}
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
},
NockWork::Work11S(mut sint) => match sint.todo {
Todo11S::ComputeResult => {
if let Some(ret) = hint::match_pre_nock(
work_ctx, subject, sint.tag, None, sint.body,
context, subject, sint.tag, None, sint.body,
) {
match ret {
Ok(found) => {
res = found;
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
Err(err) => {
break Err(err);
@ -1039,46 +845,46 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
} else {
if sint.tail {
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
} else {
sint.todo = Todo11S::Done;
*work_ctx.stack.top() = NockWork::Work11S(sint);
*context.stack.top() = NockWork::Work11S(sint);
}
push_formula(&mut work_ctx.stack, sint.body, sint.tail)?;
push_formula(&mut context.stack, sint.body, sint.tail)?;
}
}
Todo11S::Done => {
if let Some(found) = hint::match_post_nock(
work_ctx, subject, sint.tag, None, sint.body, res,
context, subject, sint.tag, None, sint.body, res,
) {
res = found;
}
work_ctx.stack.pop::<NockWork>();
context.stack.pop::<NockWork>();
}
},
NockWork::Work12(mut scry) => match scry.todo {
Todo12::ComputeReff => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
scry.todo = Todo12::ComputePath;
*stack.top() = NockWork::Work12(scry);
push_formula(stack, scry.reff, false)?;
}
Todo12::ComputePath => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
scry.todo = Todo12::Scry;
scry.reff = res;
*stack.top() = NockWork::Work12(scry);
push_formula(stack, scry.path, false)?;
}
Todo12::Scry => {
if let Some(cell) = work_ctx.scry_stack.cell() {
if let Some(cell) = context.scry_stack.cell() {
scry.path = res;
let scry_stack = work_ctx.scry_stack;
let scry_stack = context.scry_stack;
let scry_handler = cell.head();
let scry_gate = scry_handler.as_cell()?;
let payload = T(&mut work_ctx.stack, &[scry.reff, res]);
let payload = T(&mut context.stack, &[scry.reff, res]);
let scry_core = T(
&mut work_ctx.stack,
&mut context.stack,
&[
scry_gate.head(),
payload,
@ -1086,13 +892,13 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
],
);
let scry_form =
T(&mut work_ctx.stack, &[D(9), D(2), D(1), scry_core]);
T(&mut context.stack, &[D(9), D(2), D(1), scry_core]);
work_ctx.scry_stack = cell.tail();
context.scry_stack = cell.tail();
// Alternately, we could use scry_core as the subject and [9 2 0 1] as
// the formula. It's unclear if performance will be better with a purely
// static formula.
match interpret(work_ctx, D(0), scry_form) {
match interpret(context, D(0), scry_form) {
Ok(noun) => match noun.as_either_atom_cell() {
Left(atom) => {
if atom.as_noun().raw_equals(D(0)) {
@ -1104,7 +910,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
Right(cell) => {
match cell.tail().as_either_atom_cell() {
Left(_) => {
let stack = &mut work_ctx.stack;
let stack = &mut context.stack;
let hunk = T(
stack,
&[
@ -1118,8 +924,8 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
Right(cell) => {
res = cell.tail();
work_ctx.scry_stack = scry_stack;
work_ctx.stack.pop::<NockWork>();
context.scry_stack = scry_stack;
context.stack.pop::<NockWork>();
}
}
}
@ -1133,10 +939,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
break Err(error);
}
Error::ScryBlocked(_) => {
break Err(Error::NonDeterministic(
Mote::Fail,
D(0),
));
break BAIL_FAIL;
}
},
}
@ -1149,18 +952,15 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
};
}
};
call_with_guard(work_f, low_f, high_f, context as *mut Context)
call_with_guard(stack_pp, alloc_pp, work_f)
})
});
let match_f = &mut |context_p: *mut c_void| unsafe {
let match_ctx = &mut *(context_p as *mut Context);
match nock {
Ok(res) => Ok(res),
Err(err) => Err(exit(match_ctx, &snapshot, virtual_frame, err)),
}
};
call_with_guard(match_f, low_f, high_f, context as *mut Context)
match nock {
Ok(res) => Ok(res),
Err(err) => Err(exit(context, &snapshot, virtual_frame, err)),
}
}
fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result {

View File

@ -3,6 +3,7 @@ extern crate num_derive;
extern crate lazy_static;
#[macro_use]
extern crate static_assertions;
pub mod guard;
pub mod hamt;
pub mod interpreter;
pub mod jets;

View File

@ -147,19 +147,19 @@ 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 */
/** Current stack pointer of this NockStack */
pub fn get_stack_pointer_pointer(&self) -> *const *mut u64 {
&self.stack_pointer
}
/** Current alloc pointer of this NockStack */
pub fn get_alloc_pointer_pointer(&self) -> *const *mut u64 {
&self.alloc_pointer as *const *mut u64
&self.alloc_pointer
}
/** Start of the memory range for this NockStack */
@ -288,14 +288,8 @@ impl NockStack {
if self.pc {
panic!("Allocation during cleanup phase is prohibited.");
}
let alloc = self.alloc_pointer.sub(words);
if alloc < self.stack_pointer {
ptr::null_mut()
} else {
self.alloc_pointer = alloc;
alloc
}
self.alloc_pointer = self.alloc_pointer.sub(words);
self.alloc_pointer
}
/** Bump the alloc pointer for an east frame to make space for an allocation */
@ -303,15 +297,9 @@ impl NockStack {
if self.pc {
panic!("Allocation during cleanup phase is prohibited.");
}
let alloc = self.alloc_pointer;
let new_ap = self.alloc_pointer.add(words);
if new_ap > self.stack_pointer {
ptr::null_mut()
} else {
self.alloc_pointer = new_ap;
alloc
}
self.alloc_pointer = self.alloc_pointer.add(words);
alloc
}
/** Allocate space for an indirect pointer in a west frame */
@ -355,24 +343,14 @@ impl NockStack {
unsafe fn raw_alloc_in_previous_frame_west(&mut self, words: usize) -> *mut u64 {
// note that the allocation is on the east frame, and thus resembles raw_alloc_east
let alloc = *self.prev_alloc_pointer_pointer();
let new_prev_ap = (*(self.prev_alloc_pointer_pointer())).add(words);
if new_prev_ap > self.stack_pointer {
ptr::null_mut()
} else {
*(self.prev_alloc_pointer_pointer()) = new_prev_ap;
alloc
}
*(self.prev_alloc_pointer_pointer()) = (*(self.prev_alloc_pointer_pointer())).add(words);
alloc
}
unsafe fn raw_alloc_in_previous_frame_east(&mut self, words: usize) -> *mut u64 {
// note that the allocation is on the west frame, and thus resembles raw_alloc_west
let alloc = (*(self.prev_alloc_pointer_pointer())).sub(words);
if alloc < self.stack_pointer {
ptr::null_mut()
} else {
*(self.prev_alloc_pointer_pointer()) = alloc;
alloc
}
*(self.prev_alloc_pointer_pointer()) = (*(self.prev_alloc_pointer_pointer())).sub(words);
*(self.prev_alloc_pointer_pointer())
}
/** Allocate space in the previous stack frame. This calls pre_copy() first to ensure that the
@ -437,27 +415,16 @@ impl NockStack {
* or not pre_copy() has been called.*/
unsafe fn pre_copy(&mut self) {
if !self.pc {
let old_stack_pointer = self.stack_pointer;
// Change polarity of lightweight stack.
if self.is_west() {
self.stack_pointer = self.alloc_pointer.sub(RESERVED + 1);
if self.stack_pointer < old_stack_pointer {
// OOM
std::ptr::null::<usize>().read_volatile();
}
} else {
self.stack_pointer = self.alloc_pointer.add(RESERVED);
if self.stack_pointer > old_stack_pointer {
// OOM
std::ptr::null::<usize>().read_volatile();
}
}
self.pc = true;
*(self.free_slot(FRAME)) = *(self.slot_pointer(FRAME));
*(self.free_slot(STACK)) = *(self.slot_pointer(STACK));
*(self.free_slot(ALLOC)) = *(self.slot_pointer(ALLOC));
self.pc = true;
// Change polarity of lightweight stack.
if self.is_west() {
self.stack_pointer = self.alloc_pointer.sub(RESERVED + 1);
} else {
self.stack_pointer = self.alloc_pointer.add(RESERVED);
}
}
}
@ -662,22 +629,13 @@ impl NockStack {
let current_stack_pointer = self.stack_pointer;
let current_alloc_pointer = self.alloc_pointer;
unsafe {
if self.is_west() {
self.frame_pointer = current_alloc_pointer.sub(num_locals + RESERVED);
if self.frame_pointer <= current_stack_pointer {
// OOM
std::ptr::null::<usize>().read_volatile();
}
self.frame_pointer = if self.is_west() {
current_alloc_pointer.sub(num_locals + RESERVED)
} else {
self.frame_pointer = current_alloc_pointer.add(num_locals + RESERVED);
if self.frame_pointer >= current_stack_pointer {
// OOM
std::ptr::null::<usize>().read_volatile();
}
}
current_alloc_pointer.add(num_locals + RESERVED)
};
self.alloc_pointer = current_stack_pointer;
self.stack_pointer = self.frame_pointer;
*(self.slot_pointer(FRAME)) = current_frame_pointer as u64;
*(self.slot_pointer(STACK)) = current_stack_pointer as u64;
*(self.slot_pointer(ALLOC)) = current_alloc_pointer as u64;

View File

@ -143,7 +143,7 @@ impl Context {
snapshot: Option<Snapshot>,
constant_hot_state: &[HotEntry],
) -> Self {
let mut stack = NockStack::new(4096 << 10 << 10, 0);
let mut stack = NockStack::new(2048 << 10 << 10, 0);
let newt = Newt::new();
let cache = Hamt::<Noun>::new(&mut stack);
@ -401,6 +401,7 @@ fn slam(context: &mut Context, axis: u64, ovo: Noun) -> Result<Noun, Error> {
let sam = T(stack, &[D(6), D(0), D(7)]);
let fol = T(stack, &[D(8), pul, D(9), D(2), D(10), sam, D(0), D(2)]);
let sub = T(stack, &[arvo, ovo]);
interpret(&mut context.nock_context, sub, fol)
}
@ -604,18 +605,14 @@ fn work_swap(context: &mut Context, job: Noun, goof: Noun) {
context.work_swap(ovo, fec);
}
Err(goof_crud) => {
work_bail(context, &[goof_crud, goof]);
eprintln!("\r serf: bail");
let stack = &mut context.nock_context.stack;
let lud = T(stack, &[goof_crud, goof, D(0)]);
context.work_bail(lud);
}
}
}
fn work_bail(context: &mut Context, goofs: &[Noun]) {
let stack = &mut context.nock_context.stack;
let lest = T(stack, goofs);
let lud = T(stack, &[lest, D(0)]);
context.work_bail(lud);
}
fn work_trace_name(stack: &mut NockStack, wire: Noun, vent: Atom) -> String {
let wpc = path_to_cord(stack, wire);
let wpc_len = met3_usize(wpc);

View File

@ -61,7 +61,6 @@ dependencies = [
"rand",
"sha1",
"sha2",
"urcrypt-sys",
"x25519-dalek",
]
@ -611,16 +610,6 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "urcrypt-sys"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced751f95a527a3458eb67c75e4ae7093d41585edaa7565f5769101502473019"
dependencies = [
"bindgen",
"pkg-config",
]
[[package]]
name = "version_check"
version = "0.9.4"

View File

@ -6,9 +6,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
assert_no_alloc = { path = "../rust-assert-no-alloc" }
# use this when debugging requires allocation (e.g. eprintln)
# assert_no_alloc = { path = "../rust-assert-no-alloc", features=["warn_debug"] }
assert_no_alloc = { path = "../rust-assert-no-alloc" }
ibig = "0.3.6"
# ed25519
@ -25,13 +25,14 @@ sha1 = { version = "0.10.6", default-features = false, optional = true }
sha2 = { version = "0.10.8", default-features = false, optional = true }
# test_vs_urcrypt
# XX: can be removed once stable
rand = { version = "0.8.4", default-features = false, features = ["getrandom"], optional = true }
urcrypt-sys = { version = "0.1.1", optional = true }
[features]
# XX turn off test_vs_urcrypt after development
default = ["aes_siv", "ed25519", "sha"]
aes_siv = ["aes", "aes-siv"]
ed25519 = ["curve25519-dalek", "ed25519-dalek", "x25519-dalek"]
sha = ["sha1", "sha2"]
# XX: can be removed once stable
test_vs_urcrypt = ["urcrypt-sys", "rand"]

View File

@ -8,8 +8,5 @@ edition = "2021"
[dependencies]
[build-dependencies]
bindgen = "0.69.1"
bindgen = "0.69"
cc = "1.0"
[provile.dev]
opt-level = 0

View File

@ -1,169 +1,227 @@
#include <assert.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include "guard.h"
#define GD_PAGE_BITS 14ULL
#define GD_PAGE_SIZE (1ULL << GD_PAGE_BITS) // 16 KB
#define GD_PAGE_MASK (GD_PAGE_SIZE - 1)
#define GD_PAGE_ROUND_DOWN(foo) (foo & (~GD_PAGE_MASK))
#define GD_PAGEBITS 14ULL
#define GD_PAGESIZE (1ULL << GD_PAGEBITS) /* 16K */
static uintptr_t guard_p;
static const uintptr_t *stack_pp;
static const uintptr_t *alloc_pp;
static jmp_buf env_buffer;
static struct sigaction prev_sa;
static uint64_t *guard_p = 0;
static jmp_buf env_buffer;
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;
static int32_t
_prot_page(void *address, int prot)
{
if (mprotect(address, GD_PAGE_SIZE, prot)) {
fprintf(stderr, "guard: prot: mprotect error %d\r\n", errno);
fprintf(stderr, "%s\r\n", strerror(errno));
return guard_mprotect | errno;
}
return 0;
}
static int32_t
_mark_page(void *address)
{
return _prot_page(address, PROT_NONE);
}
static int32_t
_unmark_page(void *address)
{
return _prot_page(address, PROT_READ | PROT_WRITE);
}
// Center the guard page.
static guard_err
// XX: could be a false positive if the new frame results in exact same guard page
// solution: we only re-center from the signal handler
static int32_t
_focus_guard()
{
const uint64_t *low_p = low(bounds, context);
const uint64_t *high_p = high(bounds, context);
uintptr_t stack_p = *stack_pp;
uintptr_t alloc_p = *alloc_pp;
uintptr_t old_guard_p = guard_p;
uintptr_t new_guard_p;
int32_t err = 0;
if (low_p >= high_p ) {
return guard_spent;
fprintf(stderr, "guard: focus: stack pointer at %p\r\n", (void *)stack_p);
fprintf(stderr, "guard: focus: alloc pointer at %p\r\n", (void *)alloc_p);
if (stack_p == 0 || alloc_p == 0) {
fprintf(stderr, "guard: focus: stack or alloc pointer is null\r\n");
return guard_null;
} else if (stack_p == alloc_p) {
fprintf(stderr, "guard: focus: stack and alloc pointers equal\r\n");
return guard_oom;
}
if (low_p == 0 || high_p == 0) {
fprintf(stderr, "guard: low or high bound pointer is null\r\n");
return guard_weird;
fprintf(stderr, "guard: focus: old guard = %p\r\n", (void *)old_guard_p);
// Compute new guard page
// XX: Should we also check for new_guard_p < min(stack_p, alloc_p)?
new_guard_p = GD_PAGE_ROUND_DOWN((stack_p + alloc_p) / 2);
fprintf(stderr, "guard: focus: new guard = %p\r\n", (void *)new_guard_p);
if (new_guard_p == old_guard_p) {
fprintf(stderr, "guard: focus: OOM\r\n");
return guard_oom;
}
void *old_guard_p = guard_p;
guard_p = (uint64_t *)low_p + ((high_p - low_p) / 2);
guard_p = (uint64_t *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1));
const bool same = old_guard_p == guard_p;
const bool left = (uint64_t)(high_p - low_p) > GD_PAGESIZE;
if (same && !left) {
fprintf(stderr, "guard: spent: %p; left: %u\r\n", (void *)guard_p, left);
return guard_spent;
// Mark new guard page
if ((err = _mark_page((void *)new_guard_p))) {
fprintf(stderr, "guard: focus: mark error\r\n");
return err;
}
else {
if (mprotect(guard_p, GD_PAGESIZE, PROT_NONE) == -1) {
return guard_armor;
// Update guard page tracker
fprintf(stderr, "guard: focus: installed guard page at %p\r\n", (void *)new_guard_p);
guard_p = new_guard_p;
// Unmark the old guard page (if there is one)
if (old_guard_p) {
if ((err = _unmark_page((void *)old_guard_p))) {
fprintf(stderr, "guard: focus: unmark error\r\n");
return err;
}
}
if (old_guard_p != NULL &&
mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1)
{
return guard_armor;
}
return guard_sound;
return 0;
}
static void
_signal_handler(int sig, siginfo_t *si, void *unused)
{
uintptr_t sig_addr;
int32_t err = 0;
assert(guard_p);
fprintf(stderr, "guard: sig_handle: %d received\r\n", sig);
if (sig != SIGSEGV) {
fprintf(stderr, "guard: weird signal: %d\r\n", sig);
return;
fprintf(stderr, "guard: sig_handle: invalid signal\r\n");
// XX: do we even want to jump? if this is fatal error, maybe just die now
siglongjmp(env_buffer, guard_signal);
}
if (guard_p == NULL) {
fprintf(stderr, "guard: no guard page\r\n");
return;
}
sig_addr = (uintptr_t)si->si_addr;
fprintf(stderr, "guard: SIGSEGV address = %p\r\n", (void *)sig_addr);
if (si == NULL) {
fprintf(stderr, "guard: no signal info\r\n");
return;
}
if (si->si_addr >= (void *)guard_p &&
si->si_addr < (void *)guard_p + GD_PAGESIZE)
if (sig_addr >= guard_p &&
sig_addr < guard_p + GD_PAGE_SIZE)
{
fprintf(stderr, "guard: hit: %p\r\n", si->si_addr);
guard_err err = _focus_guard();
if (err != guard_sound) {
fprintf(stderr, "guard: jump\r\n");
err = _focus_guard();
if (err) {
fprintf(stderr, "guard: sig_handle: focus error\r\n");
siglongjmp(env_buffer, err);
}
}
else {
fprintf(stderr, "guard: weird hit: %p\r\n", si->si_addr);
return;
fprintf(stderr, "guard: page at %p miss\r\n", (void *)guard_p);
if (prev_sa.sa_sigaction != NULL) {
prev_sa.sa_sigaction(sig, si, unused);
} else if (prev_sa.sa_handler != NULL) {
prev_sa.sa_handler(sig);
} else {
// There should always be a default SIGSEGV handler
assert(0);
}
}
}
static guard_err
static int32_t
_register_handler()
{
struct sigaction sa;
struct sigaction prev_sa;
// Flag to use sa_sigaction
sa.sa_flags = SA_SIGINFO;
// Must use sa_sigaction; sa-handler takes signal handler as its only argument
sa.sa_sigaction = _signal_handler;
sigemptyset(&sa.sa_mask);
sigaddset(&(sa.sa_mask), SIGSEGV);
// Set mask of signals to ignore while running signal handler
// TODO: By default the signal that triggered the signal handler is automatically added to the
// mask while it's being handled, so unless we plan to add more signals to this then I
// don't think it's necessary.
// sigemptyset(&sa.sa_mask);
// sigaddset(&(sa.sa_mask), SIGSEGV);
// XX: should assert that prev_sa doesn't have a handler in it, but it's not a pointer so non-trivial
if (sigaction(SIGSEGV, &sa, &prev_sa)) {
fprintf(stderr, "guard: failed to register handler\r\n");
return guard_weird;
fprintf(stderr, "guard: register: sigaction error\r\n");
fprintf(stderr, "%s\r\n", strerror(errno));
return guard_sigaction | errno;
}
prev_sigsegv_handler = prev_sa.sa_sigaction;
return guard_sound;
return 0;
}
guard_err
int32_t
guard(
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
)
{
guard_err err;
callback f,
void *closure,
const uintptr_t *const s_pp,
const uintptr_t *const a_pp,
void ** ret
) {
int32_t err = 0;
int32_t td_err;
low = low_f;
high = high_f;
bounds= bounds_data;
context = context_p;
assert(guard_p == 0);
fprintf(stderr, "guard: setup: stack pointer at %p\r\n", (void *)(*stack_pp));
fprintf(stderr, "guard: setup: alloc pointer at %p\r\n", (void *)(*alloc_pp));
err = _focus_guard();
if (guard_p == NULL && err != guard_sound && err != guard_spent) {
fprintf(stderr, "guard: failed to install\r\n");
goto fail;
guard_p = 0;
stack_pp = s_pp;
alloc_pp = a_pp;
// Initialize the guard page
if ((err = _focus_guard())) {
fprintf(stderr, "guard: setup: _focus_guard error\r\n");
goto done;
}
if ((err = _register_handler()) != guard_sound) {
fprintf(stderr, "guard: failed to register handler\r\n");
goto fail;
// Register guard page signal handler
if ((err = _register_handler())) {
fprintf(stderr, "guard: setup: _register_handler error\r\n");
goto done;
}
err = sigsetjmp(env_buffer, 1);
if (err == guard_start) {
*(void **)ret = work_f(work_data, context_p);
return guard_sound;
}
else {
goto fail;
// Run given closure
fprintf(stderr, "guard: run\r\n");
if (!(err = sigsetjmp(env_buffer, 1))) {
*ret = f(closure);
}
fail:
// Restore the previous signal handler.
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = prev_sigsegv_handler;
sigemptyset(&sa.sa_mask);
sigaddset(&(sa.sa_mask), SIGSEGV);
sigaction(SIGSEGV, &sa, NULL);
done:
// Clean up
if (guard_p != 0) {
td_err = _unmark_page((void *)guard_p);
}
fprintf(stderr, "guard: fail: %d\r\n", err);
if (sigaction(SIGSEGV, &prev_sa, NULL)) {
fprintf(stderr, "guard: teardown: sigaction error\r\n");
fprintf(stderr, "%s\r\n", strerror(errno));
td_err = guard_sigaction | errno;
}
if (!err) {
err = td_err;
}
fprintf(stderr, "guard: return\r\n");
return err;
}

View File

@ -3,15 +3,22 @@
#include <stdint.h>
/**
* Error codes and flags.
*
* The flags are bitwise added to the errno of their respective errors.
*/
typedef enum {
guard_start = 0, // setjmp
guard_sound = 1, // good/done
guard_armor = 2, // mprotect
guard_weird = 3, // strange state
guard_spent = 4, // out of memory (bail:meme)
guard_null = 1, // null stack or alloc pointer
guard_signal, // invalid signal
guard_oom, // OOM
guard_malloc = 0x10000000, // malloc error flag
guard_mprotect = 0x20000000, // mprotect error flag
guard_sigaction = 0x40000000, // sigaction error flag
} guard_err;
typedef void *(*callback)(void *);
/**
* Execute the given closure `f` within the memory arena between the
* `stack` and `alloc` pointers, with guard page protection. Write either
@ -40,15 +47,13 @@ typedef enum {
* error will be written to the `ret` pointer. The caller is then responsible
* for handling this error and aborting with a `bail:meme`.
*/
guard_err
int32_t
guard(
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
callback f,
void *closure,
const uintptr_t *const s_pp,
const uintptr_t *const a_pp,
void **ret
);
#endif
#endif // __GUARD_H__

View File

@ -4,7 +4,9 @@
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_NULL: u32 = guard_err_guard_null;
pub const GUARD_SIGNAL: u32 = guard_err_guard_signal;
pub const GUARD_OOM: u32 = guard_err_guard_oom;
pub const GUARD_MALLOC: u32 = guard_err_guard_malloc;
pub const GUARD_MPROTECT: u32 = guard_err_guard_mprotect;
pub const GUARD_SIGACTION: u32 = guard_err_guard_sigaction;

View File

@ -76,7 +76,6 @@ STATIC_ASSERT(0, "debugger break instruction unimplemented");
/* the opposite of P2BYTES */
#define B2PAGES(x) ((size_t)(x) >> BT_PAGEBITS)
#define __packed __attribute__((__packed__))
#define UNUSED(x) ((void)(x))
@ -95,7 +94,6 @@ STATIC_ASSERT(0, "debugger break instruction unimplemented");
/* given a pointer p returns the low page-aligned addr */
#define LO_ALIGN_PAGE(p) ((BT_page *)(((uintptr_t)p) & ~(BT_PAGESIZE - 1)))
#define BT_MAPADDR ((BYTE *) S(0x1000,0000,0000))
static inline vaof_t
@ -341,7 +339,6 @@ struct BT_state {
/*
//// ===========================================================================
//// btree internal routines

View File

@ -1,2 +0,0 @@
/target
Cargo.lock

View File

@ -149,6 +149,14 @@ pub fn reset_violation_count() {
ALLOC_VIOLATION_COUNT.with(|c| c.set(0));
}
pub fn reset_counters() {
ALLOC_FORBID_COUNT.with(|c| c.set(0));
ALLOC_PERMIT_COUNT.with(|c| c.set(0));
#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))]
ALLOC_VIOLATION_COUNT.with(|c| c.set(0));
}