diff --git a/rust/ares/Cargo.lock b/rust/ares/Cargo.lock index 02fe6dd..274c0b5 100644 --- a/rust/ares/Cargo.lock +++ b/rust/ares/Cargo.lock @@ -62,7 +62,7 @@ dependencies = [ "ares_guard", "ares_macros", "ares_pma", - "assert_no_alloc 1.1.2", + "assert_no_alloc", "autotools", "bitvec", "cc", @@ -87,7 +87,7 @@ version = "0.1.0" dependencies = [ "aes", "aes-siv", - "assert_no_alloc 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_no_alloc", "curve25519-dalek", "ed25519-dalek", "ibig 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -124,12 +124,6 @@ dependencies = [ name = "assert_no_alloc" version = "1.1.2" -[[package]] -name = "assert_no_alloc" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ca83137a482d61d916ceb1eba52a684f98004f18e0cafea230fe5579c178a3" - [[package]] name = "atty" version = "0.2.14" diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 8928d11..3156e0d 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -18,8 +18,8 @@ ares_macros = { path = "../ares_macros" } # ares_pma = { path = "../ares_pma", features=["debug_prints"] } ares_pma = { path = "../ares_pma" } # use this when debugging requires allocation (e.g. eprintln) -# assert_no_alloc = { path = "../assert-no-alloc", features=["warn_debug"] } -assert_no_alloc = { path = "../assert-no-alloc" } +# assert_no_alloc = { path = "../rust-assert-no-alloc", features=["warn_debug"] } +assert_no_alloc = { path = "../rust-assert-no-alloc" } bitvec = "1.0.0" criterion = "0.4" either = "1.9.0" diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 186ba3d..12bb229 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; @@ -16,9 +17,9 @@ use crate::serf::TERMINATOR; use crate::trace::{write_nock_trace, TraceInfo, TraceStack}; use crate::unifying_equality::unifying_equality; use ares_macros::tas; -use assert_no_alloc::assert_no_alloc; +use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters}; use bitvec::prelude::{BitSlice, Lsb0}; -use either::Either::*; +use either::*; use std::result; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -400,533 +401,560 @@ 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(|| unsafe { - push_formula(&mut context.stack, formula, true)?; + let nock = assert_no_alloc(|| { + ensure_alloc_counters(|| { + 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 = *context.stack.top(); - match work { - NockWork::Done => { - write_trace(context); + loop { + let work: NockWork = *context.stack.top(); + match work { + NockWork::Done => { + write_trace(context); - let stack = &mut context.stack; - debug_assertions(stack, orig_subject); - debug_assertions(stack, subject); - debug_assertions(stack, res); - - stack.preserve(&mut context.cache); - stack.preserve(&mut context.cold); - stack.preserve(&mut context.warm); - stack.preserve(&mut res); - stack.frame_pop(); - - debug_assertions(stack, orig_subject); - debug_assertions(stack, res); - - break Ok(res); - } - NockWork::Ret => { - write_trace(context); - - let stack = &mut context.stack; - debug_assertions(stack, orig_subject); - debug_assertions(stack, subject); - debug_assertions(stack, res); - - stack.preserve(&mut context.cache); - stack.preserve(&mut context.cold); - stack.preserve(&mut context.warm); - stack.preserve(&mut res); - stack.frame_pop(); - - debug_assertions(stack, orig_subject); - debug_assertions(stack, res); - } - NockWork::WorkCons(mut cons) => match cons.todo { - TodoCons::ComputeHead => { - cons.todo = TodoCons::ComputeTail; - *context.stack.top() = NockWork::WorkCons(cons); - push_formula(&mut context.stack, cons.head, false)?; - } - TodoCons::ComputeTail => { - cons.todo = TodoCons::Cons; - cons.head = res; - *context.stack.top() = NockWork::WorkCons(cons); - push_formula(&mut context.stack, cons.tail, false)?; - } - TodoCons::Cons => { - let stack = &mut context.stack; - res = T(stack, &[cons.head, res]); - stack.pop::(); - } - }, - NockWork::Work0(zero) => { - if let Ok(noun) = subject.slot_atom(zero.axis) { - res = noun; - context.stack.pop::(); - } else { - // Axis invalid for input Noun - break BAIL_EXIT; - } - } - NockWork::Work1(once) => { - res = once.noun; - context.stack.pop::(); - } - NockWork::Work2(mut vale) => { - if (*terminator).load(Ordering::Relaxed) { - break BAIL_INTR; - } - - match vale.todo { - Todo2::ComputeSubject => { - vale.todo = Todo2::ComputeFormula; - *context.stack.top() = NockWork::Work2(vale); - push_formula(&mut context.stack, vale.subject, false)?; - } - Todo2::ComputeFormula => { - vale.todo = Todo2::ComputeResult; - vale.subject = res; - *context.stack.top() = NockWork::Work2(vale); - push_formula(&mut context.stack, vale.formula, false)?; - } - Todo2::ComputeResult => { let stack = &mut context.stack; - if vale.tail { - stack.pop::(); - subject = vale.subject; - push_formula(stack, res, true)?; - } else { - vale.todo = Todo2::RestoreSubject; - std::mem::swap(&mut vale.subject, &mut subject); - *stack.top() = NockWork::Work2(vale); - - debug_assertions(stack, orig_subject); - debug_assertions(stack, subject); - debug_assertions(stack, res); - - mean_frame_push(stack, 0); - *stack.push() = NockWork::Ret; - push_formula(stack, res, true)?; - } - } - Todo2::RestoreSubject => { - let stack = &mut context.stack; - - subject = vale.subject; - stack.pop::(); - debug_assertions(stack, orig_subject); debug_assertions(stack, subject); debug_assertions(stack, res); + + stack.preserve(&mut context.cache); + stack.preserve(&mut context.cold); + stack.preserve(&mut context.warm); + stack.preserve(&mut res); + stack.frame_pop(); + + debug_assertions(stack, orig_subject); + debug_assertions(stack, res); + + break Ok(res); } - } - } - NockWork::Work3(mut thee) => match thee.todo { - Todo3::ComputeChild => { - thee.todo = Todo3::ComputeType; - *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) }; - context.stack.pop::(); - } - }, - NockWork::Work4(mut four) => match four.todo { - Todo4::ComputeChild => { - four.todo = Todo4::Increment; - *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 context.stack, atom).as_noun(); - context.stack.pop::(); - } else { - // Cannot increment (Nock 4) a cell - break BAIL_EXIT; + NockWork::Ret => { + write_trace(context); + + let stack = &mut context.stack; + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); + + stack.preserve(&mut context.cache); + stack.preserve(&mut context.cold); + stack.preserve(&mut context.warm); + stack.preserve(&mut res); + stack.frame_pop(); + + debug_assertions(stack, orig_subject); + debug_assertions(stack, res); } - } - }, - NockWork::Work5(mut five) => match five.todo { - Todo5::ComputeLeftChild => { - five.todo = Todo5::ComputeRightChild; - *context.stack.top() = NockWork::Work5(five); - push_formula(&mut context.stack, five.left, false)?; - } - Todo5::ComputeRightChild => { - five.todo = Todo5::TestEquals; - five.left = res; - *context.stack.top() = NockWork::Work5(five); - push_formula(&mut context.stack, five.right, false)?; - } - Todo5::TestEquals => { - let stack = &mut context.stack; - let saved_value_ptr = &mut five.left; - res = if unifying_equality(stack, &mut res, saved_value_ptr) { - D(0) - } else { - D(1) - }; - stack.pop::(); - } - }, - NockWork::Work6(mut cond) => match cond.todo { - Todo6::ComputeTest => { - cond.todo = Todo6::ComputeBranch; - *context.stack.top() = NockWork::Work6(cond); - push_formula(&mut context.stack, cond.test, false)?; - } - Todo6::ComputeBranch => { - let stack = &mut context.stack; - stack.pop::(); - if let Left(direct) = res.as_either_direct_allocated() { - if direct.data() == 0 { - push_formula(stack, cond.zero, cond.tail)?; - } else if direct.data() == 1 { - push_formula(stack, cond.once, cond.tail)?; + NockWork::WorkCons(mut cons) => match cons.todo { + TodoCons::ComputeHead => { + cons.todo = TodoCons::ComputeTail; + *context.stack.top() = NockWork::WorkCons(cons); + push_formula(&mut context.stack, cons.head, false)?; + } + TodoCons::ComputeTail => { + cons.todo = TodoCons::Cons; + cons.head = res; + *context.stack.top() = NockWork::WorkCons(cons); + push_formula(&mut context.stack, cons.tail, false)?; + } + TodoCons::Cons => { + let stack = &mut context.stack; + res = T(stack, &[cons.head, res]); + stack.pop::(); + } + }, + NockWork::Work0(zero) => { + if let Ok(noun) = subject.slot_atom(zero.axis) { + res = noun; + context.stack.pop::(); } else { - // Test branch of Nock 6 must return 0 or 1 + // Axis invalid for input Noun break BAIL_EXIT; } - } else { - // Test branch of Nock 6 must return a direct atom - break BAIL_EXIT; } - } - }, - NockWork::Work7(mut pose) => match pose.todo { - Todo7::ComputeSubject => { - pose.todo = Todo7::ComputeResult; - *context.stack.top() = NockWork::Work7(pose); - push_formula(&mut context.stack, pose.subject, false)?; - } - Todo7::ComputeResult => { - let stack = &mut context.stack; - if pose.tail { - stack.pop::(); - subject = res; - push_formula(stack, pose.formula, true)?; - } else { - pose.todo = Todo7::RestoreSubject; - pose.subject = subject; - *stack.top() = NockWork::Work7(pose); - subject = res; - push_formula(stack, pose.formula, false)?; + NockWork::Work1(once) => { + res = once.noun; + context.stack.pop::(); } - } - Todo7::RestoreSubject => { - subject = pose.subject; - context.stack.pop::(); - } - }, - NockWork::Work8(mut pins) => match pins.todo { - Todo8::ComputeSubject => { - pins.todo = Todo8::ComputeResult; - *context.stack.top() = NockWork::Work8(pins); - push_formula(&mut context.stack, pins.pin, false)?; - } - Todo8::ComputeResult => { - let stack = &mut context.stack; - if pins.tail { - subject = T(stack, &[res, subject]); - stack.pop::(); - push_formula(stack, pins.formula, true)?; - } else { - pins.todo = Todo8::RestoreSubject; - pins.pin = subject; - *stack.top() = NockWork::Work8(pins); - subject = T(stack, &[res, subject]); - push_formula(stack, pins.formula, false)?; - } - } - Todo8::RestoreSubject => { - subject = pins.pin; - context.stack.pop::(); - } - }, - NockWork::Work9(mut kale) => { - if (*terminator).load(Ordering::Relaxed) { - break BAIL_INTR; - } + NockWork::Work2(mut vale) => { + if (*terminator).load(Ordering::Relaxed) { + break BAIL_INTR; + } - match kale.todo { - Todo9::ComputeCore => { - kale.todo = Todo9::ComputeResult; - *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)) = context.warm.find_jet( - &mut context.stack, - &mut res, - &mut formula, - ) { - match jet(context, res) { - Ok(jet_res) => { - res = jet_res; - context.stack.pop::(); - continue; - } - Err(JetErr::Punt) => {} - Err(err) => { - break Err(err.into()); - } - } + match vale.todo { + Todo2::ComputeSubject => { + vale.todo = Todo2::ComputeFormula; + *context.stack.top() = NockWork::Work2(vale); + push_formula(&mut context.stack, vale.subject, false)?; + } + Todo2::ComputeFormula => { + vale.todo = Todo2::ComputeResult; + vale.subject = res; + *context.stack.top() = NockWork::Work2(vale); + push_formula(&mut context.stack, vale.formula, false)?; + } + Todo2::ComputeResult => { + let stack = &mut context.stack; + if vale.tail { + stack.pop::(); + subject = vale.subject; + push_formula(stack, res, true)?; + } else { + vale.todo = Todo2::RestoreSubject; + std::mem::swap(&mut vale.subject, &mut subject); + *stack.top() = NockWork::Work2(vale); + + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); + + mean_frame_push(stack, 0); + *stack.push() = NockWork::Ret; + push_formula(stack, res, true)?; } - }; + } + Todo2::RestoreSubject => { + let stack = &mut context.stack; - let stack = &mut context.stack; - if kale.tail { + subject = vale.subject; 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 context.trace_info.is_some() { - if let Some(path) = context.cold.matches(stack, &mut res) { - append_trace(stack, path); - }; - }; - - subject = res; - push_formula(stack, formula, true)?; - } else { - kale.todo = Todo9::RestoreSubject; - kale.core = subject; - *stack.top() = NockWork::Work9(kale); - debug_assertions(stack, orig_subject); debug_assertions(stack, subject); debug_assertions(stack, res); - - subject = res; - mean_frame_push(stack, 0); - *stack.push() = NockWork::Ret; - push_formula(stack, formula, true)?; - - // 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 context.trace_info.is_some() { - if let Some(path) = context.cold.matches(stack, &mut res) { - append_trace(stack, path); - }; - }; - } - } else { - // Axis into core must be atom - break BAIL_EXIT; - } - } - Todo9::RestoreSubject => { - let stack = &mut context.stack; - - subject = kale.core; - stack.pop::(); - - debug_assertions(stack, orig_subject); - debug_assertions(stack, subject); - debug_assertions(stack, res); - } - } - } - NockWork::Work10(mut diet) => { - match diet.todo { - Todo10::ComputeTree => { - diet.todo = Todo10::ComputePatch; // should we compute patch then tree? - *context.stack.top() = NockWork::Work10(diet); - push_formula(&mut context.stack, diet.tree, false)?; - } - Todo10::ComputePatch => { - diet.todo = Todo10::Edit; - diet.tree = res; - *context.stack.top() = NockWork::Work10(diet); - push_formula(&mut context.stack, diet.patch, false)?; - } - Todo10::Edit => { - res = edit(&mut context.stack, diet.axis.as_bitslice(), res, diet.tree); - context.stack.pop::(); - } - } - } - NockWork::Work11D(mut dint) => match dint.todo { - Todo11D::ComputeHint => { - if let Some(ret) = - hint::match_pre_hint(context, subject, dint.tag, dint.hint, dint.body) - { - match ret { - Ok(found) => { - res = found; - context.stack.pop::(); - } - Err(err) => { - break Err(err); } } - } else { - dint.todo = Todo11D::ComputeResult; - *context.stack.top() = NockWork::Work11D(dint); - push_formula(&mut context.stack, dint.hint, false)?; } - } - Todo11D::ComputeResult => { - if let Some(ret) = hint::match_pre_nock( - context, - subject, - dint.tag, - Some((dint.hint, res)), - dint.body, - ) { - match ret { - Ok(found) => { - res = found; - context.stack.pop::(); - } - Err(err) => { - break Err(err); - } + NockWork::Work3(mut thee) => match thee.todo { + Todo3::ComputeChild => { + thee.todo = Todo3::ComputeType; + *context.stack.top() = NockWork::Work3(thee); + push_formula(&mut context.stack, thee.child, false)?; } - } else { - if dint.tail { + Todo3::ComputeType => { + res = if res.is_cell() { D(0) } else { D(1) }; context.stack.pop::(); - } else { - dint.todo = Todo11D::Done; - dint.hint = res; - *context.stack.top() = NockWork::Work11D(dint); } - push_formula(&mut context.stack, dint.body, dint.tail)?; - } - } - Todo11D::Done => { - if let Some(found) = hint::match_post_nock( - context, - subject, - dint.tag, - Some(dint.hint), - dint.body, - res, - ) { - res = found; - } - context.stack.pop::(); - } - }, - NockWork::Work11S(mut sint) => match sint.todo { - Todo11S::ComputeResult => { - if let Some(ret) = - hint::match_pre_nock(context, subject, sint.tag, None, sint.body) - { - match ret { - Ok(found) => { - res = found; + }, + NockWork::Work4(mut four) => match four.todo { + Todo4::ComputeChild => { + four.todo = Todo4::Increment; + *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 context.stack, atom).as_noun(); context.stack.pop::(); - } - Err(err) => { - break Err(err); + } else { + // Cannot increment (Nock 4) a cell + break BAIL_EXIT; } } - } else { - if sint.tail { - context.stack.pop::(); - } else { - sint.todo = Todo11S::Done; - *context.stack.top() = NockWork::Work11S(sint); + }, + NockWork::Work5(mut five) => match five.todo { + Todo5::ComputeLeftChild => { + five.todo = Todo5::ComputeRightChild; + *context.stack.top() = NockWork::Work5(five); + push_formula(&mut context.stack, five.left, false)?; } - push_formula(&mut context.stack, sint.body, sint.tail)?; - } - } - Todo11S::Done => { - if let Some(found) = - hint::match_post_nock(context, subject, sint.tag, None, sint.body, res) - { - res = found; - } - context.stack.pop::(); - } - }, - NockWork::Work12(mut scry) => match scry.todo { - Todo12::ComputeReff => { - 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 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) = context.scry_stack.cell() { - scry.path = res; - let scry_stack = context.scry_stack; - let scry_handler = cell.head(); - let scry_gate = scry_handler.as_cell()?; - let payload = T(&mut context.stack, &[scry.reff, res]); - let scry_core = T( - &mut context.stack, - &[ - scry_gate.head(), - payload, - scry_gate.tail().as_cell()?.tail(), - ], - ); - let scry_form = T(&mut context.stack, &[D(9), D(2), D(1), scry_core]); - - 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(context, D(0), scry_form) { - Ok(noun) => match noun.as_either_atom_cell() { - Left(atom) => { - if atom.as_noun().raw_equals(D(0)) { - break Err(Error::ScryBlocked(scry.path)); - } else { - break Err(Error::ScryCrashed(D(0))); - } + Todo5::ComputeRightChild => { + five.todo = Todo5::TestEquals; + five.left = res; + *context.stack.top() = NockWork::Work5(five); + push_formula(&mut context.stack, five.right, false)?; + } + Todo5::TestEquals => { + let stack = &mut context.stack; + let saved_value_ptr = &mut five.left; + res = if unifying_equality(stack, &mut res, saved_value_ptr) { + D(0) + } else { + D(1) + }; + stack.pop::(); + } + }, + NockWork::Work6(mut cond) => match cond.todo { + Todo6::ComputeTest => { + cond.todo = Todo6::ComputeBranch; + *context.stack.top() = NockWork::Work6(cond); + push_formula(&mut context.stack, cond.test, false)?; + } + Todo6::ComputeBranch => { + let stack = &mut context.stack; + stack.pop::(); + if let Left(direct) = res.as_either_direct_allocated() { + if direct.data() == 0 { + push_formula(stack, cond.zero, cond.tail)?; + } else if direct.data() == 1 { + push_formula(stack, cond.once, cond.tail)?; + } else { + // Test branch of Nock 6 must return 0 or 1 + break BAIL_EXIT; } - Right(cell) => match cell.tail().as_either_atom_cell() { - Left(_) => { - let stack = &mut context.stack; - let hunk = - T(stack, &[D(tas!(b"hunk")), scry.reff, scry.path]); - mean_push(stack, hunk); - break Err(Error::ScryCrashed(D(0))); + } else { + // Test branch of Nock 6 must return a direct atom + break BAIL_EXIT; + } + } + }, + NockWork::Work7(mut pose) => match pose.todo { + Todo7::ComputeSubject => { + pose.todo = Todo7::ComputeResult; + *context.stack.top() = NockWork::Work7(pose); + push_formula(&mut context.stack, pose.subject, false)?; + } + Todo7::ComputeResult => { + let stack = &mut context.stack; + if pose.tail { + stack.pop::(); + subject = res; + push_formula(stack, pose.formula, true)?; + } else { + pose.todo = Todo7::RestoreSubject; + pose.subject = subject; + *stack.top() = NockWork::Work7(pose); + subject = res; + push_formula(stack, pose.formula, false)?; + } + } + Todo7::RestoreSubject => { + subject = pose.subject; + context.stack.pop::(); + } + }, + NockWork::Work8(mut pins) => match pins.todo { + Todo8::ComputeSubject => { + pins.todo = Todo8::ComputeResult; + *context.stack.top() = NockWork::Work8(pins); + push_formula(&mut context.stack, pins.pin, false)?; + } + Todo8::ComputeResult => { + let stack = &mut context.stack; + if pins.tail { + subject = T(stack, &[res, subject]); + stack.pop::(); + push_formula(stack, pins.formula, true)?; + } else { + pins.todo = Todo8::RestoreSubject; + pins.pin = subject; + *stack.top() = NockWork::Work8(pins); + subject = T(stack, &[res, subject]); + push_formula(stack, pins.formula, false)?; + } + } + Todo8::RestoreSubject => { + subject = pins.pin; + context.stack.pop::(); + } + }, + NockWork::Work9(mut kale) => { + if (*terminator).load(Ordering::Relaxed) { + break BAIL_INTR; + } + + match kale.todo { + Todo9::ComputeCore => { + kale.todo = Todo9::ComputeResult; + *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)) = context.warm.find_jet( + &mut context.stack, + &mut res, + &mut formula, + ) { + match jet(context, res) { + Ok(jet_res) => { + res = jet_res; + context.stack.pop::(); + continue; + } + Err(JetErr::Punt) => {} + Err(err) => { + break Err(err.into()); + } + } + } + }; + + 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 context.trace_info.is_some() { + if let Some(path) = + context.cold.matches(stack, &mut res) + { + append_trace(stack, path); + }; + }; + + subject = res; + push_formula(stack, formula, true)?; + } else { + kale.todo = Todo9::RestoreSubject; + kale.core = subject; + *stack.top() = NockWork::Work9(kale); + + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); + + subject = res; + mean_frame_push(stack, 0); + *stack.push() = NockWork::Ret; + push_formula(stack, formula, true)?; + + // 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 context.trace_info.is_some() { + if let Some(path) = + context.cold.matches(stack, &mut res) + { + append_trace(stack, path); + }; + }; } - Right(cell) => { - res = cell.tail(); - context.scry_stack = scry_stack; + } else { + // Axis into core must be atom + break BAIL_EXIT; + } + } + Todo9::RestoreSubject => { + let stack = &mut context.stack; + + subject = kale.core; + stack.pop::(); + + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); + } + } + } + NockWork::Work10(mut diet) => { + match diet.todo { + Todo10::ComputeTree => { + diet.todo = Todo10::ComputePatch; // should we compute patch then tree? + *context.stack.top() = NockWork::Work10(diet); + push_formula(&mut context.stack, diet.tree, false)?; + } + Todo10::ComputePatch => { + diet.todo = Todo10::Edit; + diet.tree = res; + *context.stack.top() = NockWork::Work10(diet); + push_formula(&mut context.stack, diet.patch, false)?; + } + Todo10::Edit => { + res = edit( + &mut context.stack, + diet.axis.as_bitslice(), + res, + diet.tree, + ); + context.stack.pop::(); + } + } + } + NockWork::Work11D(mut dint) => match dint.todo { + Todo11D::ComputeHint => { + if let Some(ret) = hint::match_pre_hint( + context, subject, dint.tag, dint.hint, dint.body, + ) { + match ret { + Ok(found) => { + res = found; context.stack.pop::(); } - }, - }, - Err(error) => match error { - Error::Deterministic(_, trace) | Error::ScryCrashed(trace) => { - break Err(Error::ScryCrashed(trace)); + Err(err) => { + break Err(err); + } } - Error::NonDeterministic(_, _) => { - break Err(error); - } - Error::ScryBlocked(_) => { - break BAIL_FAIL; - } - }, + } else { + dint.todo = Todo11D::ComputeResult; + *context.stack.top() = NockWork::Work11D(dint); + push_formula(&mut context.stack, dint.hint, false)?; + } } - } else { - // No scry handler - break BAIL_EXIT; - } - } - }, + Todo11D::ComputeResult => { + if let Some(ret) = hint::match_pre_nock( + context, + subject, + dint.tag, + Some((dint.hint, res)), + dint.body, + ) { + match ret { + Ok(found) => { + res = found; + context.stack.pop::(); + } + Err(err) => { + break Err(err); + } + } + } else { + if dint.tail { + context.stack.pop::(); + } else { + dint.todo = Todo11D::Done; + dint.hint = res; + *context.stack.top() = NockWork::Work11D(dint); + } + push_formula(&mut context.stack, dint.body, dint.tail)?; + } + } + Todo11D::Done => { + if let Some(found) = hint::match_post_nock( + context, + subject, + dint.tag, + Some(dint.hint), + dint.body, + res, + ) { + res = found; + } + context.stack.pop::(); + } + }, + NockWork::Work11S(mut sint) => match sint.todo { + Todo11S::ComputeResult => { + if let Some(ret) = hint::match_pre_nock( + context, subject, sint.tag, None, sint.body, + ) { + match ret { + Ok(found) => { + res = found; + context.stack.pop::(); + } + Err(err) => { + break Err(err); + } + } + } else { + if sint.tail { + context.stack.pop::(); + } else { + sint.todo = Todo11S::Done; + *context.stack.top() = NockWork::Work11S(sint); + } + push_formula(&mut context.stack, sint.body, sint.tail)?; + } + } + Todo11S::Done => { + if let Some(found) = hint::match_post_nock( + context, subject, sint.tag, None, sint.body, res, + ) { + res = found; + } + context.stack.pop::(); + } + }, + NockWork::Work12(mut scry) => match scry.todo { + Todo12::ComputeReff => { + 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 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) = context.scry_stack.cell() { + scry.path = res; + let scry_stack = context.scry_stack; + let scry_handler = cell.head(); + let scry_gate = scry_handler.as_cell()?; + let payload = T(&mut context.stack, &[scry.reff, res]); + let scry_core = T( + &mut context.stack, + &[ + scry_gate.head(), + payload, + scry_gate.tail().as_cell()?.tail(), + ], + ); + let scry_form = + T(&mut context.stack, &[D(9), D(2), D(1), scry_core]); + + 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(context, D(0), scry_form) { + Ok(noun) => match noun.as_either_atom_cell() { + Left(atom) => { + if atom.as_noun().raw_equals(D(0)) { + break Err(Error::ScryBlocked(scry.path)); + } else { + break Err(Error::ScryCrashed(D(0))); + } + } + Right(cell) => { + match cell.tail().as_either_atom_cell() { + Left(_) => { + let stack = &mut context.stack; + let hunk = T( + stack, + &[ + D(tas!(b"hunk")), + scry.reff, + scry.path, + ], + ); + mean_push(stack, hunk); + break Err(Error::ScryCrashed(D(0))); + } + Right(cell) => { + res = cell.tail(); + context.scry_stack = scry_stack; + context.stack.pop::(); + } + } + } + }, + Err(error) => match error { + Error::Deterministic(_, trace) + | Error::ScryCrashed(trace) => { + break Err(Error::ScryCrashed(trace)); + } + Error::NonDeterministic(_, _) => { + break Err(error); + } + Error::ScryBlocked(_) => { + break BAIL_FAIL; + } + }, + } + } else { + // No scry handler + break BAIL_EXIT; + } + } + }, + }; + } }; - } + + call_with_guard(stack_pp, alloc_pp, work_f) + }) }); match nock { @@ -1163,6 +1191,7 @@ fn exit( Error::Deterministic(_, t) | Error::NonDeterministic(_, t) | Error::ScryCrashed(t) => { // Return $tang of traces let h = *(stack.local_noun_pointer(0)); + // XX: Small chance of clobbering something important after OOM? T(stack, &[h, t]) } }; diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 8da4e92..1f4aecc 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,4 +1,3 @@ -use crate::guard::call_with_guard; use crate::hamt::Hamt; use crate::interpreter; use crate::interpreter::{inc, interpret, Error, Mote}; @@ -403,27 +402,7 @@ fn slam(context: &mut Context, axis: u64, ovo: Noun) -> Result { let fol = T(stack, &[D(8), pul, D(9), D(2), D(10), sam, D(0), D(2)]); let sub = T(stack, &[arvo, ovo]); - let frame_p = stack.get_frame_pointer(); - let stack_pp = stack.get_stack_pointer_pointer(); - let alloc_pp = stack.get_alloc_pointer_pointer(); - - let res = call_with_guard( - stack_pp as *const *const u64, - alloc_pp as *const *const u64, - &mut || interpret(&mut context.nock_context, sub, fol), - ); - - if let Err(Error::NonDeterministic(Mote::Meme, _)) = res { - unsafe { - let stack = &mut context.nock_context.stack; - assert_no_alloc::reset_counters(); - while stack.get_frame_pointer() != frame_p { - stack.frame_pop(); - } - } - } - - res + interpret(&mut context.nock_context, sub, fol) } fn peek(context: &mut Context, ovo: Noun) -> Noun { @@ -626,7 +605,7 @@ fn work_swap(context: &mut Context, job: Noun, goof: Noun) { context.work_swap(ovo, fec); } Err(goof_crud) => { - eprintln!("\r serf: bail"); + eprintln!("\rserf: bail"); let stack = &mut context.nock_context.stack; let lud = T(stack, &[goof_crud, goof, D(0)]); context.work_bail(lud); diff --git a/rust/ares_crypto/Cargo.toml b/rust/ares_crypto/Cargo.toml index a52b57b..3ee2ab1 100644 --- a/rust/ares_crypto/Cargo.toml +++ b/rust/ares_crypto/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" [dependencies] # use this when debugging requires allocation (e.g. eprintln) -# assert_no_alloc = {version="1.1.2", features=["warn_debug"]} -assert_no_alloc = "1.1.2" +# assert_no_alloc = { path = "../rust-assert-no-alloc", features=["warn_debug"] } +assert_no_alloc = { path = "../rust-assert-no-alloc" } ibig = "0.3.6" # ed25519 diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index fe419bf..e296201 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -22,54 +22,56 @@ #endif /** - * XX: documentation + * Linked list stack of jump buffers. */ -typedef struct _GD_state GD_state; -struct _GD_state { +typedef struct GD_buflistnode GD_buflistnode; +struct GD_buflistnode { + jmp_buf buffer; + GD_buflistnode *next; +}; + +/** + * Global guard page state. + */ +typedef struct GD_state GD_state; +struct GD_state { uintptr_t guard_p; const uintptr_t *stack_pp; const uintptr_t *alloc_pp; - jmp_buf env_buffer; + GD_buflistnode *buffer_list; struct sigaction prev_sa; }; -static GD_state *_guard_state = NULL; - +static GD_state _gd_state = { + .guard_p = 0, + .stack_pp = NULL, + .alloc_pp = NULL, + .buffer_list = NULL, + .prev_sa = { .sa_sigaction = NULL, .sa_flags = 0 }, +}; static uint32_t -_prot_page(void *address, int prot) +_protect_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 guard_mprotect ; } return 0; } +// Center the guard page. static uint32_t -_mark_page(void *address) +_focus_guard(GD_state *gd) { - return _prot_page(address, PROT_NONE); -} + uintptr_t stack_p = *(gd->stack_pp); + uintptr_t alloc_p = *(gd->alloc_pp); + uintptr_t old_guard_p = (gd->guard_p); + uintptr_t new_guard_p; + uint32_t err = 0; -static uint32_t -_unmark_page(void *address) -{ - return _prot_page(address, PROT_READ | PROT_WRITE); -} - -/** - * Center the guard page. - */ -static uint32_t -_focus_guard(GD_state *gs) { - uintptr_t *guard_pp = &(gs->guard_p); - uintptr_t stack_p = *(gs->stack_pp); - uintptr_t alloc_p = *(gs->alloc_pp); - - // Check anomalous arguments if (stack_p == 0 || alloc_p == 0) { fprintf(stderr, "guard: focus: stack or alloc pointer is null\r\n"); return guard_null; @@ -77,89 +79,81 @@ _focus_guard(GD_state *gs) { return guard_oom; } - uintptr_t old_guard_p = *guard_pp; - uintptr_t new_guard_p; - int32_t err = 0; - - // Compute new guard page - // XX: Should we also check for new_guard_p < min(stack_p, alloc_p)? + // Compute new guard page. new_guard_p = GD_PAGE_ROUND_DOWN((stack_p + alloc_p) / 2); if (new_guard_p == old_guard_p) { return guard_oom; } - // Mark new guard page - if ((err = _mark_page((void *)new_guard_p))) { - fprintf(stderr, "guard: focus: mark error %p\r\n", (void *)new_guard_p); + // Mark new guard page. + if ((err = _protect_page((void *)new_guard_p, PROT_NONE))) { + fprintf(stderr, "guard: focus: mark error\r\n"); return err; } - // Update guard page tracker - *guard_pp = new_guard_p; + // Update guard page tracker. + gd->guard_p = new_guard_p; - // Unmark the old guard page (if there is one) + // 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, %p\r\n", (void *)old_guard_p); + if ((err = _protect_page((void *)old_guard_p, PROT_READ | PROT_WRITE))) { + fprintf(stderr, "guard: focus: unmark error\r\n"); return err; } } - + return 0; } -void +static void _signal_handler(int sig, siginfo_t *si, void *unused) { uintptr_t sig_addr; - int32_t err = 0; + uint32_t err = 0; - assert(sig == GD_SIGNAL); + assert(_gd_state.guard_p); + if (sig != GD_SIGNAL) { + fprintf(stderr, "guard: handler: invalid signal: %d\r\n", sig); + assert(0); + } sig_addr = (uintptr_t)si->si_addr; - if ( - sig_addr >= _guard_state->guard_p && - sig_addr < (_guard_state->guard_p + GD_PAGE_SIZE)) + + if (sig_addr >= _gd_state.guard_p && + sig_addr < _gd_state.guard_p + GD_PAGE_SIZE) { - err = _focus_guard(_guard_state); + err = _focus_guard(&_gd_state); if (err) { - siglongjmp(_guard_state->env_buffer, err); + siglongjmp(_gd_state.buffer_list->buffer, err); } - } else { - struct sigaction prev_sa = _guard_state->prev_sa; + } + else { + struct sigaction prev_sa = _gd_state.prev_sa; 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 + // There should always be a default handler assert(0); } } } -uint32_t -_register_handler(GD_state *gs) +// Registers the handler function. +static uint32_t +_register_handler(GD_state *gd) { struct sigaction 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; - // Set mask of signals to ignore while running signal handler - // XX: 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); - // Set the new SIGSEGV handler, and save the old SIGSEGV handler (if any) - if (sigaction(GD_SIGNAL, &sa, &(gs->prev_sa))) { + if (sigaction(GD_SIGNAL, &sa, &(gd->prev_sa))) { fprintf(stderr, "guard: register: sigaction error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); - return guard_sigaction | errno; + return guard_sigaction; } return 0; @@ -167,66 +161,83 @@ _register_handler(GD_state *gs) uint32_t guard( - callback f, + void *(*f)(void *), void *closure, - const uintptr_t *const stack_pp, - const uintptr_t *const alloc_pp, - void ** ret + const uintptr_t *const s_pp, + const uintptr_t *const a_pp, + void **ret ) { - int32_t err = 0; - int32_t err_c = 0; + GD_buflistnode *new_buffer; + uint32_t err = 0; + uint32_t td_err = 0; - // - // Setup guard page state - // + if (_gd_state.guard_p == 0) { + assert(_gd_state.buffer_list == NULL); - // guard() presumes that it is only ever called once at a time - assert(_guard_state == NULL); + _gd_state.stack_pp = s_pp; + _gd_state.alloc_pp = a_pp; - _guard_state = (GD_state *)malloc(sizeof(GD_state)); - if (_guard_state == NULL) { + // Initialize the guard page. + if ((err = _focus_guard(&_gd_state))) { + fprintf(stderr, "guard: initial focus error\r\n"); + goto exit; + } + + // Register guard page signal handler. + if ((err = _register_handler(&_gd_state))) { + fprintf(stderr, "guard: registration error\r\n"); + goto tidy; + } + } else { + assert(_gd_state.buffer_list != NULL); + } + + // Setup new longjmp buffer. + new_buffer = (GD_buflistnode *)malloc(sizeof(GD_buflistnode)); + if (new_buffer == NULL) { fprintf(stderr, "guard: malloc error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); - return guard_malloc | errno; + err = guard_malloc; + goto skip; } - _guard_state->guard_p = 0; - _guard_state->stack_pp = stack_pp; - _guard_state->alloc_pp = alloc_pp; + new_buffer->next = _gd_state.buffer_list; + _gd_state.buffer_list = new_buffer; - // Initialize the guard page - if ((err = _focus_guard(_guard_state))) { - goto clear; - } - - // Register guard page signal handler - if ((err = _register_handler(_guard_state))) { - goto unmark; - } - - // - // Run closure - // - - if (!(err = sigsetjmp(_guard_state->env_buffer, 1))) { + // Run given closure. + if (!(err = sigsetjmp(_gd_state.buffer_list->buffer, 1))) { *ret = f(closure); } - // - // Clean up guard page state - // + // Restore previous longjmp buffer. + _gd_state.buffer_list = _gd_state.buffer_list->next; + free((void *)new_buffer); - if (sigaction(GD_SIGNAL, &(_guard_state->prev_sa), NULL)) { - fprintf(stderr, "guard: sigaction error\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - err_c = guard_sigaction | errno; +skip: + if (_gd_state.buffer_list == NULL) { + if (sigaction(GD_SIGNAL, &_gd_state.prev_sa, NULL)) { + fprintf(stderr, "guard: error replacing sigsegv handler\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + td_err = guard_sigaction; + + if (!err) { + err = td_err; + } + } + +tidy: + // Unmark guard page. + assert(_gd_state.guard_p != 0); + td_err = _protect_page((void *)_gd_state.guard_p, PROT_READ | PROT_WRITE); + if (td_err) { + fprintf(stderr, "guard: unmark error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + if (!err) { + err = td_err; + } + } + _gd_state.guard_p = 0; } -unmark: - err_c = _unmark_page((void *)_guard_state->guard_p); - -clear: - free(_guard_state); - _guard_state = NULL; - - return err ? err : err_c; +exit: + return err; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 13b3256..bcbffb2 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -1,58 +1,73 @@ #ifndef __GUARD_H__ #define __GUARD_H__ +#include #include /** - * Error codes and flags. - * - * The flags are bitwise added to the errno of their respective errors. + * Error codes. */ typedef enum { - guard_null = 1, // null stack or alloc pointer - guard_oom, // OOM - guard_malloc = 0x10000000, // malloc error flag - guard_mprotect = 0x20000000, // mprotect error flag - guard_sigaction = 0x40000000, // sigaction error flag + guard_null, // null stack or alloc pointer + guard_signal, // invalid signal + guard_oom, // out of memory + guard_malloc, // malloc error + guard_mprotect, // mprotect error + guard_sigaction, // sigaction error } 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 - * `f`'s succesful result or a `guard_err` to the given `ret` pointer. + * @brief Executes the given callback function `f` within the memory arena + * between the stack and allocation pointers pointed to by `s_pp` and `a_pp`, + * with guard page protection. If `f`'s execution succeeds, its result is + * written to the return pointer `*ret`. If `f`'s execution triggers an + * out of memory error or any other `guard_err`, the `guard_err` is + * returned and `*ret` is left empty. In either case, cleanup is performed + * before returning. + * + * Definitions: + * - A guard page is marked `PROT_NONE`. * - * Memory - * ------ - * The free memory arena between the `stack` and `alloc` pointers is part of a - * NockStack frame, which may either face east or west. If the frame faces - * east, the `stack` pointer will be greater than the `alloc` pointer. If it - * faces west, the `stack` pointer will be less than the `alloc` pointer. + * Assumptions: + * - `NockStack` pages are marked `PROT_READ|PROT_WRITE` by default. + * - All memory access patterns are outside-in. + * - Callback functions are compatible with the C ABI. + * - `NockStack` stack and allocation pointer locations are fixed. + * - The caller is responsible for return value memory allocation. + * - The caller is responsible for managing any external state the callback + * function may mutate. + * - The callback function may be interrupted in the case of memory exhaustion + * or other `guard_err` error (failure to `mprotect`, `malloc`, etc.). + * - `SIGSEGV` (`SIGBUS` on macOS) signals are expected to be raised only on + * guard page accesses. * - * All the pages in the memory arena are marked clean (`PROT_READ | PROT_WRITE`) - * by default, with the exception of a single guard page in the middle of the - * arena, which is marked with `PROT_NONE`. + * Invariants: + * - A single guard page is installed and maintained in the approximate center + * until `crate::guard::call_with_guard` returns. + * - A return value is only written to `*ret` on successful callback execution. + * - A `guard_err` is returned. * - * Guard - * ----- - * This function protects the free memory arena between the `stack` and `alloc` - * pointers with a guard page. A guard page is simply a single page of memory - * which is marked with `PROT_NONE`. Since all other pages are marked clean by - * default, a SIGSEGV will only be raised if the `f` function attempts to write - * to the guard page. When it does, the signal handler will attempt to re-center - * the guard page in the remaining free space left in the arena. If there is no - * more free space, then memory exhaustion has occurred and the `guard_spent` - * error will be written to the `ret` pointer. The caller is then responsible - * for handling this error and aborting with a `bail:meme`. + * Enhancements: + * - Use only a single, static jump buffer variable instead of a linked list. + * We currently use a linked list of jump buffers because we don't have a + * function for preserving stack traces across `crate::interpreter::interpret` + * calls. + * + * @param f The callback function to execute. + * @param closure A pointer to the closure data for the callback function. + * @param s_pp A pointer to the stack pointer location. + * @param a_pp A pointer to the allocation pointer location. + * @param ret A pointer to a location where the callback's result can be stored. + * + * @return 0 on callback success; otherwise `guard_err` error code. */ uint32_t guard( - callback f, + void *(*f)(void *), void *closure, - const uintptr_t *const stack_pp, - const uintptr_t *const alloc_pp, - void ** ret + const uintptr_t *const s_pp, + const uintptr_t *const a_pp, + void **ret ); #endif // __GUARD_H__ diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index 246b5ac..f7b1089 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -4,8 +4,9 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -pub const GUARD_NULL: u32 = guard_err_guard_null; -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; +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 4b2efd2..24dff2f 100644 --- a/rust/ares_pma/c-src/btree.c +++ b/rust/ares_pma/c-src/btree.c @@ -76,6 +76,7 @@ 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)) @@ -94,6 +95,7 @@ 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 @@ -339,6 +341,7 @@ struct BT_state { /* + //// =========================================================================== //// btree internal routines diff --git a/rust/assert-no-alloc/Cargo.toml b/rust/rust-assert-no-alloc/Cargo.toml similarity index 98% rename from rust/assert-no-alloc/Cargo.toml rename to rust/rust-assert-no-alloc/Cargo.toml index 95a935e..004f51c 100644 --- a/rust/assert-no-alloc/Cargo.toml +++ b/rust/rust-assert-no-alloc/Cargo.toml @@ -12,6 +12,7 @@ keywords = ["allocator", "real-time", "debug", "audio"] categories = ["development-tools::debugging"] [features] +default = [] warn_debug = [] warn_release = [] disable_release = [] diff --git a/rust/assert-no-alloc/LICENSE b/rust/rust-assert-no-alloc/LICENSE similarity index 100% rename from rust/assert-no-alloc/LICENSE rename to rust/rust-assert-no-alloc/LICENSE diff --git a/rust/assert-no-alloc/README.md b/rust/rust-assert-no-alloc/README.md similarity index 100% rename from rust/assert-no-alloc/README.md rename to rust/rust-assert-no-alloc/README.md diff --git a/rust/assert-no-alloc/examples/main.rs b/rust/rust-assert-no-alloc/examples/main.rs similarity index 100% rename from rust/assert-no-alloc/examples/main.rs rename to rust/rust-assert-no-alloc/examples/main.rs diff --git a/rust/assert-no-alloc/src/lib.rs b/rust/rust-assert-no-alloc/src/lib.rs similarity index 93% rename from rust/assert-no-alloc/src/lib.rs rename to rust/rust-assert-no-alloc/src/lib.rs index 00f6a92..8192d56 100644 --- a/rust/assert-no-alloc/src/lib.rs +++ b/rust/rust-assert-no-alloc/src/lib.rs @@ -91,6 +91,22 @@ pub fn assert_no_alloc T> (func: F) -> T { return ret; } +/// Calls the `func` closure, but ensures that the forbid and permit counters +/// are maintained accurately even if a longjmp originates and terminates +/// within the closure. If you longjmp over this function, we can't fix +/// anything about it. +pub fn ensure_alloc_counters T> (func: F) -> T { + let forbid_counter = ALLOC_FORBID_COUNT.with(|c| c.get()); + let permit_counter = ALLOC_PERMIT_COUNT.with(|c| c.get()); + + let ret = func(); + + ALLOC_FORBID_COUNT.with(|c| c.set(forbid_counter)); + ALLOC_PERMIT_COUNT.with(|c| c.set(permit_counter)); + + return ret; +} + #[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled /// Calls the `func` closure. Allocations are temporarily allowed, even if this /// code runs inside of assert_no_alloc. @@ -132,13 +148,6 @@ 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)); -} diff --git a/rust/assert-no-alloc/tests/test.rs b/rust/rust-assert-no-alloc/tests/test.rs similarity index 100% rename from rust/assert-no-alloc/tests/test.rs rename to rust/rust-assert-no-alloc/tests/test.rs