diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 733b43f..3156e0d 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -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] diff --git a/rust/ares/src/guard.rs b/rust/ares/src/guard.rs new file mode 100644 index 0000000..843a4d8 --- /dev/null +++ b/rust/ares/src/guard.rs @@ -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 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(closure: &'closure mut F) -> Self + where + F: FnMut() -> Result, + { + let function: unsafe extern "C" fn(*mut c_void) -> *mut c_void = Self::call_closure::; + + // debug_assert_eq!( + // std::mem::size_of::<&'closure mut F>(), + // std::mem::size_of::<*const c_void>() + // ); + // debug_assert_eq!( + // std::mem::size_of_val(&function), + // std::mem::size_of::<*const c_void>() + // ); + + Self { + function, + input: closure as *mut F as *mut c_void, + _lifetime: PhantomData, + } + } + + unsafe extern "C" fn call_closure(input: *mut c_void) -> *mut c_void + where + F: FnMut() -> Result, + { + let cb: &mut F = input.cast::().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 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); + } + } + } + } +} diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index cb85b6a..d7859ca 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -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 for GuardError { - type Error = (); - fn try_from(value: u32) -> std::result::Result { - match value { - GUARD_SOUND => Ok(GuardError::GuardSound), - GUARD_ARMOR => Ok(GuardError::GuardArmor), - GUARD_WEIRD => Ok(GuardError::GuardWeird), - GUARD_SPENT => Ok(GuardError::GuardSpent), - _ => Err(()), - } - } -} - impl Preserve for Error { unsafe fn preserve(&mut self, stack: &mut NockStack) { match self { @@ -382,6 +360,7 @@ impl From for Error { pub type Result = result::Result; 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(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::; - - 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( - 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::().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(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::; - - debug_assert_eq!( - std::mem::size_of::<&'closure mut F>(), - std::mem::size_of::<*const c_void>() - ); - debug_assert_eq!( - std::mem::size_of_val(&function), - std::mem::size_of::<*const c_void>() - ); - - Self { - function, - user_data: closure as *mut F as *mut c_void, - _lifetime: PhantomData, - } - } - - unsafe extern "C" fn call_closure( - user_data: *mut c_void, - context_p: *mut c_void, - ) -> *mut c_void - where - F: FnMut(*mut c_void) -> Result, - { - let cb: &mut F = user_data.cast::().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::(); } @@ -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::(); + context.stack.pop::(); } 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::(); + context.stack.pop::(); } 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::(); 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::(); @@ -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::(); + context.stack.pop::(); } }, 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::(); + res = inc(&mut context.stack, atom).as_noun(); + context.stack.pop::(); } 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::(); 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::(); 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::(); + context.stack.pop::(); } }, 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::(); @@ -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::(); + context.stack.pop::(); } }, 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::(); + context.stack.pop::(); 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::(); // 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::(); @@ -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::(); + context.stack.pop::(); } } } 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::(); + context.stack.pop::(); } 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::(); + context.stack.pop::(); } 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::(); + context.stack.pop::(); } 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::(); + context.stack.pop::(); } }, 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::(); + context.stack.pop::(); } 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::(); + context.stack.pop::(); } 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::(); + context.stack.pop::(); } }, 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::(); + context.scry_stack = scry_stack; + context.stack.pop::(); } } } @@ -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 { diff --git a/rust/ares/src/lib.rs b/rust/ares/src/lib.rs index 17b7223..d6f6391 100644 --- a/rust/ares/src/lib.rs +++ b/rust/ares/src/lib.rs @@ -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; diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 7e99d15..210e897 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -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::().read_volatile(); - } - } else { - self.stack_pointer = self.alloc_pointer.add(RESERVED); - if self.stack_pointer > old_stack_pointer { - // OOM - std::ptr::null::().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::().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::().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; diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 3aa8031..70632f1 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -143,7 +143,7 @@ impl Context { snapshot: Option, 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::::new(&mut stack); @@ -401,6 +401,7 @@ fn slam(context: &mut Context, axis: u64, ovo: Noun) -> Result { 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); diff --git a/rust/ares_crypto/Cargo.lock b/rust/ares_crypto/Cargo.lock index 4610dce..cf47b03 100644 --- a/rust/ares_crypto/Cargo.lock +++ b/rust/ares_crypto/Cargo.lock @@ -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" diff --git a/rust/ares_crypto/Cargo.toml b/rust/ares_crypto/Cargo.toml index 2b893d2..3ee2ab1 100644 --- a/rust/ares_crypto/Cargo.toml +++ b/rust/ares_crypto/Cargo.toml @@ -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"] diff --git a/rust/ares_guard/Cargo.toml b/rust/ares_guard/Cargo.toml index 1031ab0..5a3f718 100644 --- a/rust/ares_guard/Cargo.toml +++ b/rust/ares_guard/Cargo.toml @@ -8,8 +8,5 @@ edition = "2021" [dependencies] [build-dependencies] -bindgen = "0.69.1" +bindgen = "0.69" cc = "1.0" - -[provile.dev] -opt-level = 0 \ No newline at end of file diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 6542d9b..4557041 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -1,169 +1,227 @@ +#include +#include #include #include -#include #include #include +#include #include #include #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; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 5d65389..ae23815 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -3,15 +3,22 @@ #include - +/** + * 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__ diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index aba1b5e..c10fb5a 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -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; diff --git a/rust/ares_pma/c-src/btree.c b/rust/ares_pma/c-src/btree.c index a225f72..9ecf982 100644 --- a/rust/ares_pma/c-src/btree.c +++ b/rust/ares_pma/c-src/btree.c @@ -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 diff --git a/rust/rust-assert-no-alloc/.gitignore b/rust/rust-assert-no-alloc/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/rust/rust-assert-no-alloc/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/rust/rust-assert-no-alloc/src/lib.rs b/rust/rust-assert-no-alloc/src/lib.rs index aa42238..0e2b0aa 100644 --- a/rust/rust-assert-no-alloc/src/lib.rs +++ b/rust/rust-assert-no-alloc/src/lib.rs @@ -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)); +} +