From 6aab4f089dacb4f916290cd5fb649158f4c4e28e Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Thu, 8 Feb 2024 12:33:53 +0900 Subject: [PATCH 1/7] stack: remove stack / alloc pointer collision checks --- rust/ares/src/mem.rs | 82 +++++++++++--------------------------------- 1 file changed, 20 insertions(+), 62 deletions(-) diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 15a69de..f2c403c 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -255,7 +255,7 @@ impl NockStack { } } - /** Pointer to where the previous stack pointer is saved in a frame */ + /** Pointer to where the previous alloc pointer is saved in a frame */ unsafe fn prev_alloc_pointer_pointer(&self) -> *mut *mut u64 { if !self.pc { self.slot_pointer(ALLOC) as *mut *mut u64 @@ -278,14 +278,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 */ @@ -293,15 +287,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 */ @@ -345,24 +333,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 @@ -427,27 +405,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); + } } } @@ -652,22 +619,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; From db635327db0ecb89b3115e54ace14248586db511 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Thu, 8 Feb 2024 12:34:51 +0900 Subject: [PATCH 2/7] build: toml bikeshedding --- rust/ares_crypto/Cargo.lock | 11 ----------- rust/ares_crypto/Cargo.toml | 5 +++-- 2 files changed, 3 insertions(+), 13 deletions(-) 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 e2b895b..a52b57b 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 = "1.1.2" # 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" 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"] From 852f033d4e5ed195223a68c4b4698a7545f4d1d3 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Thu, 8 Feb 2024 12:36:11 +0900 Subject: [PATCH 3/7] pma: remove invisible characters from code --- rust/ares_pma/c-src/btree.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/rust/ares_pma/c-src/btree.c b/rust/ares_pma/c-src/btree.c index 24dff2f..4b2efd2 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 From 7f5d8b57e178210b1a0395d31060445ff0b66002 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Thu, 8 Feb 2024 12:37:22 +0900 Subject: [PATCH 4/7] jets: replace manual bail:exit call --- rust/ares/src/jets.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/ares/src/jets.rs b/rust/ares/src/jets.rs index 7bb034d..9d1a97d 100644 --- a/rust/ares/src/jets.rs +++ b/rust/ares/src/jets.rs @@ -234,8 +234,7 @@ pub mod util { } pub fn slot(noun: Noun, axis: u64) -> Result { - noun.slot(axis) - .map_err(|_e| JetErr::Fail(Error::Deterministic(Mote::Exit, D(0)))) + noun.slot(axis).map_err(|_e| BAIL_EXIT) } /// Extract a bloq and check that it's computable by the current system From 3a64e7f6566a88274fd604a4a62036722083a634 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Thu, 8 Feb 2024 12:39:46 +0900 Subject: [PATCH 5/7] pill: add pill that causes OOM errors --- resources/pills/oom.pill | Bin 0 -> 13617 bytes resources/pills/src/oom/oom.hoon | 81 +++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 resources/pills/oom.pill create mode 100644 resources/pills/src/oom/oom.hoon diff --git a/resources/pills/oom.pill b/resources/pills/oom.pill new file mode 100644 index 0000000000000000000000000000000000000000..e43ce0d681837cfc72873fef04a3e098d4c410b3 GIT binary patch literal 13617 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 N2AZoDD|E9x{~vl2?=%1a literal 0 HcmV?d00001 diff --git a/resources/pills/src/oom/oom.hoon b/resources/pills/src/oom/oom.hoon new file mode 100644 index 0000000..de9b4f4 --- /dev/null +++ b/resources/pills/src/oom/oom.hoon @@ -0,0 +1,81 @@ +:: A working pill designed to force bail:meme OOM errors. Requires +:: playpen.hoon, originally used by the toddler pill. +:: +/+ playpen +!. +=/ core + => playpen + != + => + |% + +$ card (cask) + ++ cask |$ [a] (pair mark a) + +$ goof [mote=term =tang] + +$ mark @tas + +$ ovum [=wire =card] + +$ vere [[non=@ta rev=path] kel=wynn] + +$ wire path + +$ wasp + :: %crud: reroute $ovum with $goof + :: %wack: iterate entropy + :: %wyrd: check/record runtime kelvin stack + :: + $% [%crud =goof =ovum] + [%wack p=@uvJ] + [%wyrd p=vere] + == + +$ weft [lal=@tas num=@ud] + +$ wynn (list weft) + -- => + :: + =| counter=@u + |% + ++ load !! + ++ peek _~ + ++ wish !! + ++ poke + |= [now=@da ovo=ovum] + ^- ^ + :: + ?. ?=(?(%crud %wack %wyrd) p.card.ovo) + ?: (gte counter 20) + ~> %slog.[0 %leaf .=(~ =|(i=@ |-(?:(=(i ^~((bex 32))) ~ [i $(i +(i))])))) 0] + [~ ..poke] + ~> %slog.[0 leaf+(scow %ud counter)] + =. counter +(counter) + [~ ..poke] + :: + =/ buz + ~> %mean.'pith: bad wasp' + ;;(wasp card.ovo) + ?+ -.buz + ~> %slog.[0 leaf+"%wack / %wyrd"] + [~ ..poke] + :: + %crud + =/ tang tang.goof.buz + |- + ?~ tang + [~ ..poke] + ~> %slog.[0 -.tang] + $(tang +.tang) + == + -- + :: + |= [now=@da ovo=ovum] + ^- * + .(+> +:(poke now ovo)) +:: +|% +++ aeon + ^- * + => *[arvo=* epic=*] + != + |- ^- * + ?@ epic arvo + %= $ + epic +.epic + arvo .*([arvo -.epic] [%9 2 %10 [6 %0 3] %0 2]) + == +-- +[%pill %toddler [aeon .*(playpen core) ~] ~ ~] From 42ffde2208a89a54ff302e618a01807ddb303003 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Sat, 10 Feb 2024 14:43:20 +0900 Subject: [PATCH 6/7] gaurd: initial guard page commit --- rust/ares/Cargo.lock | 53 ++-- rust/ares/Cargo.toml | 9 +- rust/ares/src/guard.rs | 114 +++++++ rust/ares/src/lib.rs | 1 + rust/ares/src/mem.rs | 10 + rust/ares/src/serf.rs | 38 ++- rust/ares_guard/Cargo.lock | 432 ++++++++++++++++++++++++++ rust/ares_guard/Cargo.toml | 12 + rust/ares_guard/build.rs | 77 +++++ rust/ares_guard/c-src/guard.c | 287 +++++++++++++++++ rust/ares_guard/c-src/guard.h | 59 ++++ rust/ares_guard/c-src/wrapper.h | 1 + rust/ares_guard/src/lib.rs | 12 + rust/assert-no-alloc/Cargo.toml | 32 ++ rust/assert-no-alloc/LICENSE | 21 ++ rust/assert-no-alloc/README.md | 143 +++++++++ rust/assert-no-alloc/examples/main.rs | 34 ++ rust/assert-no-alloc/src/lib.rs | 249 +++++++++++++++ rust/assert-no-alloc/tests/test.rs | 148 +++++++++ 19 files changed, 1698 insertions(+), 34 deletions(-) create mode 100644 rust/ares/src/guard.rs create mode 100644 rust/ares_guard/Cargo.lock create mode 100644 rust/ares_guard/Cargo.toml create mode 100644 rust/ares_guard/build.rs create mode 100644 rust/ares_guard/c-src/guard.c create mode 100644 rust/ares_guard/c-src/guard.h create mode 100644 rust/ares_guard/c-src/wrapper.h create mode 100644 rust/ares_guard/src/lib.rs create mode 100644 rust/assert-no-alloc/Cargo.toml create mode 100644 rust/assert-no-alloc/LICENSE create mode 100644 rust/assert-no-alloc/README.md create mode 100644 rust/assert-no-alloc/examples/main.rs create mode 100644 rust/assert-no-alloc/src/lib.rs create mode 100644 rust/assert-no-alloc/tests/test.rs diff --git a/rust/ares/Cargo.lock b/rust/ares/Cargo.lock index d585a78..02fe6dd 100644 --- a/rust/ares/Cargo.lock +++ b/rust/ares/Cargo.lock @@ -59,9 +59,10 @@ name = "ares" version = "0.1.0" dependencies = [ "ares_crypto", + "ares_guard", "ares_macros", "ares_pma", - "assert_no_alloc", + "assert_no_alloc 1.1.2", "autotools", "bitvec", "cc", @@ -86,7 +87,7 @@ version = "0.1.0" dependencies = [ "aes", "aes-siv", - "assert_no_alloc", + "assert_no_alloc 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "curve25519-dalek", "ed25519-dalek", "ibig 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -95,6 +96,14 @@ dependencies = [ "x25519-dalek", ] +[[package]] +name = "ares_guard" +version = "0.1.0" +dependencies = [ + "bindgen", + "cc", +] + [[package]] name = "ares_macros" version = "0.1.0" @@ -111,6 +120,10 @@ dependencies = [ "cc", ] +[[package]] +name = "assert_no_alloc" +version = "1.1.2" + [[package]] name = "assert_no_alloc" version = "1.1.2" @@ -145,11 +158,11 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.1" +version = "0.69.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" +checksum = "a4c69fae65a523209d34240b60abe0c42d33d1045d445c0839d8a4894a736e2d" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cexpr", "clang-sys", "lazy_static", @@ -174,9 +187,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bitvec" @@ -664,9 +677,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" @@ -801,9 +814,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -860,9 +873,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", @@ -872,9 +885,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" dependencies = [ "aho-corasick", "memchr", @@ -904,11 +917,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", @@ -991,9 +1004,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 3d7ec25..8928d11 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -11,14 +11,15 @@ edition = "2018" # Please keep these alphabetized [dependencies] +ares_guard = { path = "../ares_guard" } ares_crypto = { path = "../ares_crypto" } ares_macros = { path = "../ares_macros" } # Use this when debugging requires the debug printfs in the PMA # ares_pma = { path = "../ares_pma", features=["debug_prints"] } ares_pma = { path = "../ares_pma" } -assert_no_alloc = "1.1.2" # use this when debugging requires allocation (e.g. eprintln) -# assert_no_alloc = {version="1.1.2", features=["warn_debug"]} +# assert_no_alloc = { path = "../assert-no-alloc", features=["warn_debug"] } +assert_no_alloc = { path = "../assert-no-alloc" } bitvec = "1.0.0" criterion = "0.4" either = "1.9.0" @@ -35,8 +36,8 @@ 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" 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/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 f2c403c..210e897 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -152,6 +152,16 @@ impl NockStack { self.alloc_pointer } + /** 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 + } + /** Start of the memory range for this NockStack */ pub fn get_start(&self) -> *const u64 { self.start diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 3aa8031..8da4e92 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,3 +1,4 @@ +use crate::guard::call_with_guard; use crate::hamt::Hamt; use crate::interpreter; use crate::interpreter::{inc, interpret, Error, Mote}; @@ -143,7 +144,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,7 +402,28 @@ 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) + + 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 } fn peek(context: &mut Context, ovo: Noun) -> Noun { @@ -604,18 +626,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_guard/Cargo.lock b/rust/ares_guard/Cargo.lock new file mode 100644 index 0000000..26d68b9 --- /dev/null +++ b/rust/ares_guard/Cargo.lock @@ -0,0 +1,432 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "ares_guard" +version = "0.1.0" +dependencies = [ + "bindgen", + "cc", +] + +[[package]] +name = "bindgen" +version = "0.69.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c69fae65a523209d34240b60abe0c42d33d1045d445c0839d8a4894a736e2d" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/rust/ares_guard/Cargo.toml b/rust/ares_guard/Cargo.toml new file mode 100644 index 0000000..5a3f718 --- /dev/null +++ b/rust/ares_guard/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ares_guard" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[build-dependencies] +bindgen = "0.69" +cc = "1.0" diff --git a/rust/ares_guard/build.rs b/rust/ares_guard/build.rs new file mode 100644 index 0000000..0c11bf3 --- /dev/null +++ b/rust/ares_guard/build.rs @@ -0,0 +1,77 @@ +extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +fn main() { + let opt_level = env::var("OPT_LEVEL").unwrap(); + let define_debug = if env::var("CARGO_FEATURE_DEBUG_PRINTS").is_ok() { + "-DDEBUG" + } else { + "-UDEBUG" + }; + + // This is the directory where the `c` library is located. + let libdir_path = PathBuf::from("c-src") + // Canonicalize the path as `rustc-link-search` requires an absolute + // path. + .canonicalize() + .expect("cannot canonicalize path"); + let libdir_path_str = libdir_path.to_str().expect("Path is not a valid string"); + + // This is the path to the `c` headers file. + let headers_path = libdir_path.join("wrapper.h"); + let headers_path_str = headers_path.to_str().expect("Path is not a valid string"); + + println!("cargo:rerun-if-changed={}", libdir_path_str); + + let res = cc::Build::new() + .file( + libdir_path + .join("guard.c") + .to_str() + .expect("Path is not a valid string"), + ) + .flag(format!("-O{}", opt_level).as_ref()) + .flag(define_debug) + .flag("-g3") + .flag("-Wall") + .flag("-Wextra") + .flag("-Wformat=2") + .flag("-Wmissing-include-dirs") + .flag("-Wnested-externs") + .flag("-Wpedantic") + .flag("-Wredundant-decls") + .flag("-Wshadow") + .flag("-Wwrite-strings") + .flag("-Wno-unused-parameter") + .flag("-Wno-pointer-arith") + .flag("-Wno-strict-prototypes") + .flag("-Wno-unused-function") + .try_compile("guard"); + + if let Err(err) = res { + panic!("{}", err); + } + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header(headers_path_str) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); + bindings + .write_to_file(out_path) + .expect("Couldn't write bindings!"); +} diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c new file mode 100644 index 0000000..8aa71f4 --- /dev/null +++ b/rust/ares_guard/c-src/guard.c @@ -0,0 +1,287 @@ +#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)) + +/** + * XX: documentation + */ +typedef struct _gs { + uintptr_t guard_p; + const uintptr_t *stack_pp; + const uintptr_t *alloc_pp; + jmp_buf env_buffer; + struct sigaction prev_sa; +} GuardState; + +static GuardState *_guard_state = NULL; + + +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 int32_t +_focus_guard( + uintptr_t *guard_pp, + const uintptr_t stack_p, + const uintptr_t alloc_p +) { + // Check for strange situations. + 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; + } + + uintptr_t old_guard_p = *guard_pp; + uintptr_t new_guard_p; + int32_t err = 0; + + fprintf(stderr, "guard: focus: old guard = %p\r\n", (void *)old_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; + } + } + + // 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; + } + + // Mark new guard page + if ((err = _mark_page((void *)new_guard_p))) { + fprintf(stderr, "guard: focus: mark error\r\n"); + return err; + } + + // Update guard page tracker + fprintf(stderr, "guard: focus: installed guard page at %p\r\n", (void *)new_guard_p); + *guard_pp = new_guard_p; + + return 0; +} + +void +_signal_handler(int sig, siginfo_t *si, void *unused) +{ + uintptr_t sig_addr; + int32_t err = 0; + + fprintf(stderr, "guard: sig_handle: %d received\r\n", sig); + + if (sig != SIGSEGV) { + fprintf(stderr, "guard: sig_handle: invalid signal\r\n"); + siglongjmp(_guard_state->env_buffer, guard_signal); + } + + sig_addr = (uintptr_t)si->si_addr; + fprintf(stderr, "guard: SIGSEGV address = %p\r\n", (void *)sig_addr); + + fprintf(stderr, "guard: sig_handle: %p \r\n", _guard_state); + if ( + sig_addr >= _guard_state->guard_p && + sig_addr < (_guard_state->guard_p + GD_PAGE_SIZE)) + { + fprintf(stderr, "guard: page at %p hit\r\n", (void *)_guard_state->guard_p); + err = _focus_guard( + &(_guard_state->guard_p), + *(_guard_state->stack_pp), + *(_guard_state->alloc_pp)); + if (err) { + fprintf(stderr, "guard: sig_handle: focus error\r\n"); + siglongjmp(_guard_state->env_buffer, err); + } + } else { + struct sigaction prev_sa = _guard_state->prev_sa; + + fprintf(stderr, "guard: page at %p miss\r\n", (void *)_guard_state->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); + } + } +} + +int32_t +_register_handler(struct sigaction *prev_sa) +{ + 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 + // 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); + + // Set the new SIGSEGV handler, and save the old SIGSEGV handler (if any) + if (sigaction(SIGSEGV, &sa, prev_sa)) { + fprintf(stderr, "guard: register: sigaction error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + return guard_sigaction | errno; + } + + return 0; +} + +int32_t +_setup( + GuardState **gs_p, + const uintptr_t *const stack_pp, + const uintptr_t *const alloc_pp +) { + GuardState *gs; + int32_t err = 0; + + assert(*gs_p == NULL); + 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)); + + // Setup guard page state + *gs_p = (GuardState *)malloc(sizeof(GuardState)); + gs = *gs_p; + if (gs == NULL) { + fprintf(stderr, "guard: malloc error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + return guard_malloc | errno; + } + fprintf(stderr, "guard: state allocated to %p \r\n", gs); + + gs->guard_p = 0; + gs->stack_pp = stack_pp; + gs->alloc_pp = alloc_pp; + + // Initialize the guard page + if ((err = _focus_guard(&(gs->guard_p), *stack_pp, *alloc_pp))) { + fprintf(stderr, "guard: setup: _focus_guard error\r\n"); + return err; + } + + // Register guard page signal handler + if ((err = _register_handler(&(gs->prev_sa)))) { + fprintf(stderr, "guard: setup: _register_handler error\r\n"); + return err; + } + + return 0; +} + +int32_t +_teardown(GuardState** gs_p) +{ + int32_t err = 0; + + if (*gs_p != NULL) { + GuardState *gs = *gs_p; + + if (gs->guard_p != 0) { + err = _unmark_page((void *)gs->guard_p); + } + + if (sigaction(SIGSEGV, &(gs->prev_sa), NULL)) { + fprintf(stderr, "guard: teardown: sigaction error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + err = guard_sigaction | errno; + } + + free(gs); + *gs_p = NULL; + } + + return err; +} + +int32_t +guard( + callback f, + void *closure, + const uintptr_t *const stack_pp, + const uintptr_t *const alloc_pp, + void ** ret +) { + int32_t err = 0; + + // Setup the guard page + fprintf(stderr, "guard: setup\r\n"); + if ((err = _setup(&_guard_state, stack_pp, alloc_pp))) { + goto done; + } + + // Run given closure + fprintf(stderr, "guard: run\r\n"); + if (!(err = sigsetjmp(_guard_state->env_buffer, 1))) { + *ret = f(closure); + + // Clean up + fprintf(stderr, "guard: teardown\r\n"); + err = _teardown(&_guard_state); + + fprintf(stderr, "guard: return\r\n"); + return err; + } else { +done: + // Clean up + fprintf(stderr, "guard: teardown\r\n"); + _teardown(&_guard_state); + + 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 new file mode 100644 index 0000000..fd08343 --- /dev/null +++ b/rust/ares_guard/c-src/guard.h @@ -0,0 +1,59 @@ +#ifndef __GUARD_H__ +#define __GUARD_H__ + +#include + +/** + * Error codes and flags. + * + * The flags are bitwise added to the errno of their respective errors. + */ +typedef enum { + 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 + * `f`'s succesful result or a `guard_err` to the given `ret` pointer. + * + * 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. + * + * 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`. + * + * 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`. + */ +int32_t +guard( + callback f, + void *closure, + const uintptr_t *const stack_pp, + const uintptr_t *const alloc_pp, + void ** ret +); + +#endif // __GUARD_H__ diff --git a/rust/ares_guard/c-src/wrapper.h b/rust/ares_guard/c-src/wrapper.h new file mode 100644 index 0000000..29d14dd --- /dev/null +++ b/rust/ares_guard/c-src/wrapper.h @@ -0,0 +1 @@ +#include "guard.h" diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs new file mode 100644 index 0000000..38e6c3c --- /dev/null +++ b/rust/ares_guard/src/lib.rs @@ -0,0 +1,12 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +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/assert-no-alloc/Cargo.toml b/rust/assert-no-alloc/Cargo.toml new file mode 100644 index 0000000..bbe18fb --- /dev/null +++ b/rust/assert-no-alloc/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "assert_no_alloc" +version = "1.1.2" +authors = ["Florian Jung "] +edition = "2018" +license = "BSD-1-Clause" +description = "Custom Rust allocator allowing to temporarily disable memory (de)allocations for a thread. Aborts or prints a warning if allocating although forbidden." +homepage = "https://github.com/Windfisch/rust-assert-no-alloc" +repository = "https://github.com/Windfisch/rust-assert-no-alloc" +readme = "README.md" +keywords = ["allocator", "real-time", "debug", "audio"] +categories = ["development-tools::debugging"] + +[features] +default = ["disable_release"] +warn_debug = [] +warn_release = [] +disable_release = [] + +# Print a backtrace before aborting the program when an allocation failure happens +backtrace = ["dep:backtrace"] +# Use the `log` crate instead of printing to STDERR +# WARNING: If the allocation failure happens during a logger call, then +# depending on the logger's implementation this may block indefinitely +log = ["dep:log"] + +[dependencies] +backtrace = { version = "0.3", optional = true } +log = { version = "0.4", optional = true } + +[package.metadata.docs.rs] +features = ["warn_debug"] diff --git a/rust/assert-no-alloc/LICENSE b/rust/assert-no-alloc/LICENSE new file mode 100644 index 0000000..c108e4a --- /dev/null +++ b/rust/assert-no-alloc/LICENSE @@ -0,0 +1,21 @@ +assert_no_alloc -- A custom Rust allocator allowing to temporarily disable +memory (de)allocations for a thread. + +Copyright (c) 2020 Florian Jung + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/rust/assert-no-alloc/README.md b/rust/assert-no-alloc/README.md new file mode 100644 index 0000000..8411b1c --- /dev/null +++ b/rust/assert-no-alloc/README.md @@ -0,0 +1,143 @@ +assert_no_alloc +=============== + +This crate provides a custom allocator that allows to temporarily disable +memory (de)allocations for a thread. If a (de)allocation is attempted +anyway, the program will abort or print a warning. + +It uses thread local storage for the "disabled-flag/counter", and thus +should be thread safe, if the underlying allocator (currently hard-coded +to `std::alloc::System`) is. + +[documentation @ docs.rs](https://docs.rs/assert_no_alloc/1.1.0/assert_no_alloc/), +[crates.io](https://crates.io/crates/assert_no_alloc) + +Rationale +--------- + +No-allocation-zones are relevant e.g. in real-time scenarios like audio +callbacks. Allocation and deallocation can take unpredictable amounts of +time, and thus can *sometimes* lead to audible glitches because the audio +data is not served in time. + +Debugging such problems can be hard, because it is difficult to reproduce +such problems consistently. Avoiding such problems is also hard, since +allocation/deallocation is a common thing to do and most libraries are not +explicit whether certain functions can allocate or not. Also, this might +even depend on the run-time situation (e.g. a `Vec::push` might allocate, +but it is guaranteed to not allocate *if* enough space has been `reserve()`d +before). + +To aid the developer in tackling these problems, this crate offers an easy +way of detecting all forbidden allocations. + +How to use +---------- + +First, configure the features: `warn_debug` and `warn_release` change the +behaviour from aborting your program into just printing an error message +on `stderr`. Aborting is useful for debugging purposes, as it allows you +to retrieve a stacktrace, while warning is less intrusive. + +Note that you need to disable the (default-enabled) `disable_release` feature +by specify `default-features = false` if you want to use `warn_release`. If +`disable_release` is set (which is the default), then this crate will do +nothing if built in `--release` mode. + +Second, use the allocator provided by this crate. Add this to `main.rs`: + +```rust +use assert_no_alloc::*; + +#[cfg(debug_assertions)] // required when disable_release is set (default) +#[global_allocator] +static A: AllocDisabler = AllocDisabler; +``` + +Third, wrap code sections that may not allocate like this: + +```rust +assert_no_alloc(|| { + println!("This code can not allocate."); +}); +``` + +Advanced use +------------ + +Values can be returned using: + +```rust +let answer = assert_no_alloc(|| { 42 }); +``` + +The effect of `assert_no_alloc` can be overridden using `permit_alloc`: + +```rust +assert_no_alloc(|| { + permit_alloc(|| { + // Allocate some memory here. This will work. + }); +}); +``` + +This is useful for test stubs whose code is executed in an `assert_no_alloc` +context. + +Objects that deallocate upon `Drop` can be wrapped in `PermitDrop`: + +```rust +let foo = PermitDrop::new( + permit_alloc(|| + Box::new(...) + ) +); +``` + +Dropping `foo` will not trigger an assertion (but dropping a `Box` would). + +`assert_no_alloc()` calls can be nested, with proper panic unwinding handling. + +Note that to fully bypass this crate, e.g. when in release mode, you need to +*both* have the `disable_release` feature flag enabled (which it is by default) +and to not register `AllocDisabler` as `global_allocator`. + +Optional features +----------------- + +These compile time features are not enabled by default: + +- `backtrace` causes a backtrace to be printed before the allocation failure. + This backtrace is gathered at runtime, and its accuracy depends on the + platform and the compilation options used. +- `log` uses the `log` crate to write the allocation failure message to the + configured logger. If the `backtrace` feature is also enabled, then the + backtrace will also be written to the logger This can be useful when using a + logger that writes directly to a file or any other place that isn't STDERR. + + The main caveat here is that if the allocation was caused by the logger and if + the logger wraps its entire log function in a regular non-entrant mutex, then + this may result in a deadlock. Make sure your logger doesn't do this before + enabling this feature. + +Examples +-------- + +See [examples/main.rs](https://github.com/Windfisch/rust-assert-no-alloc/blob/master/examples/main.rs) for an example. + +You can try out the different feature flags: + +- `cargo run --example main` -> memory allocation of 4 bytes failed. Aborted (core dumped) +- `cargo run --example main --release --no-default-features` -> same as above. +- `cargo run --example main --features=warn_debug` -> Tried to (de)allocate memory in a thread that forbids allocator calls! This will not be executed if the above allocation has aborted. +- `cargo run --example main --features=warn_release --release --no-default-features` -> same as above. +- `cargo run --example main --release` will not even check for forbidden allocations + +Test suite +---------- + +The tests will fail to compile with the default features. Run them using: + +``` +cargo test --features=warn_debug --tests +``` diff --git a/rust/assert-no-alloc/examples/main.rs b/rust/assert-no-alloc/examples/main.rs new file mode 100644 index 0000000..7f43d57 --- /dev/null +++ b/rust/assert-no-alloc/examples/main.rs @@ -0,0 +1,34 @@ +use assert_no_alloc::*; + +#[cfg(debug_assertions)] +#[global_allocator] +static A: AllocDisabler = AllocDisabler; + +fn main() { + println!("Alloc is allowed. Let's allocate some memory..."); + let mut vec_can_push = Vec::new(); + vec_can_push.push(42); + + println!(); + + let fib5 = assert_no_alloc(|| { + println!("Alloc is forbidden. Let's calculate something without memory allocations..."); + + fn fib(n: u32) -> u32 { + if n<=1 { 1 } + else { fib(n-1) + fib(n-2) } + } + + fib(5) + }); + println!("\tSuccess, the 5th fibonacci number is {}", fib5); + println!(); + + assert_no_alloc(|| { + println!("Alloc is forbidden. Let's allocate some memory..."); + let mut vec_cannot_push = Vec::new(); + vec_cannot_push.push(42); // panics + }); + + println!("This will not be executed if the above allocation has aborted."); +} diff --git a/rust/assert-no-alloc/src/lib.rs b/rust/assert-no-alloc/src/lib.rs new file mode 100644 index 0000000..00f6a92 --- /dev/null +++ b/rust/assert-no-alloc/src/lib.rs @@ -0,0 +1,249 @@ +/* assert_no_alloc -- A custom Rust allocator allowing to temporarily disable + * memory (de)allocations for a thread. + * + * Copyright (c) 2020 Florian Jung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#![doc = include_str!("../README.md")] + +use std::alloc::{System,GlobalAlloc,Layout}; +use std::cell::Cell; + +// check for mutually exclusive features. +#[cfg(all(feature = "disable_release", feature = "warn_release"))] +compile_error!("disable_release cannot be active at the same time with warn_release"); + + +#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled +thread_local! { + static ALLOC_FORBID_COUNT: Cell = Cell::new(0); + static ALLOC_PERMIT_COUNT: Cell = Cell::new(0); + + #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] + static ALLOC_VIOLATION_COUNT: Cell = Cell::new(0); +} + +#[cfg(all(feature = "disable_release", not(debug_assertions)))] // if disabled +pub fn assert_no_alloc T> (func: F) -> T { // no-op + func() +} + +#[cfg(all(feature = "disable_release", not(debug_assertions)))] // if disabled +pub fn permit_alloc T> (func: F) -> T { // no-op + func() +} + +#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled +/// Calls the `func` closure, but forbids any (de)allocations. +/// +/// If a call to the allocator is made, the program will abort with an error, +/// print a warning (depending on the `warn_debug` feature flag. Or ignore +/// the situation, when compiled in `--release` mode with the `disable_release` +///feature flag set (which is the default)). +pub fn assert_no_alloc T> (func: F) -> T { + // RAII guard for managing the forbid counter. This is to ensure correct behaviour + // when catch_unwind is used + struct Guard; + impl Guard { + fn new() -> Guard { + ALLOC_FORBID_COUNT.with(|c| c.set(c.get()+1)); + Guard + } + } + impl Drop for Guard { + fn drop(&mut self) { + ALLOC_FORBID_COUNT.with(|c| c.set(c.get()-1)); + } + } + + + #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] // if warn mode is selected + let old_violation_count = violation_count(); + + + let guard = Guard::new(); // increment the forbid counter + let ret = func(); + std::mem::drop(guard); // decrement the forbid counter + + + #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] // if warn mode is selected + if violation_count() > old_violation_count { + eprintln!("Tried to (de)allocate memory in a thread that forbids allocator calls!"); + } + + 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. +pub fn permit_alloc T> (func: F) -> T { + // RAII guard for managing the permit counter + struct Guard; + impl Guard { + fn new() -> Guard { + ALLOC_PERMIT_COUNT.with(|c| c.set(c.get()+1)); + Guard + } + } + impl Drop for Guard { + fn drop(&mut self) { + ALLOC_PERMIT_COUNT.with(|c| c.set(c.get()-1)); + } + } + + let guard = Guard::new(); // increment the forbid counter + let ret = func(); + std::mem::drop(guard); // decrement the forbid counter + + return ret; +} + +#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] // if warn mode is selected +/// Returns the count of allocation warnings emitted so far. +/// +/// Only available when the `warn_debug` or `warn release` features are enabled. +pub fn violation_count() -> u32 { + ALLOC_VIOLATION_COUNT.with(|c| c.get()) +} + +#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] // if warn mode is selected +/// Resets the count of allocation warnings to zero. +/// +/// Only available when the `warn_debug` or `warn release` features are enabled. +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)); +} + + + +#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled +/// The custom allocator that handles the checking. +/// +/// To use this crate, you must add the following in your `main.rs`: +/// ```rust +/// use assert_no_alloc::*; +/// // ... +/// #[cfg(debug_assertions)] +/// #[global_allocator] +/// static A: AllocDisabler = AllocDisabler; +/// ``` +pub struct AllocDisabler; + +#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled +impl AllocDisabler { + fn check(&self, layout: Layout) { + let forbid_count = ALLOC_FORBID_COUNT.with(|f| f.get()); + let permit_count = ALLOC_PERMIT_COUNT.with(|p| p.get()); + if forbid_count > 0 && permit_count == 0 { + #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] // if warn mode is selected + ALLOC_VIOLATION_COUNT.with(|c| c.set(c.get()+1)); + + #[cfg(any( all(not(feature="warn_debug"), debug_assertions), all(not(feature="warn_release"), not(debug_assertions)) ))] // if abort mode is selected + { + #[cfg(all(feature = "log", feature = "backtrace"))] + permit_alloc(|| log::error!("Memory allocation of {} bytes failed from:\n{:?}", layout.size(), backtrace::Backtrace::new())); + #[cfg(all(feature = "log", not(feature = "backtrace")))] + permit_alloc(|| log::error!("Memory allocation of {} bytes failed", layout.size())); + + #[cfg(all(not(feature = "log"), feature = "backtrace"))] + permit_alloc(|| eprintln!("Allocation failure from:\n{:?}", backtrace::Backtrace::new())); + + // This handler can be overridden (although as of writing, the API to do so is still + // unstable) so we must always call this even when the log feature is enabled + std::alloc::handle_alloc_error(layout); + } + } + } +} + +#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled +unsafe impl GlobalAlloc for AllocDisabler { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + self.check(layout); + System.alloc(layout) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + self.check(layout); + System.dealloc(ptr, layout) + } +} + +/// Wrapper for objects whose Drop implementation shall be permitted +/// to (de)allocate. +/// +/// Typical usage: +/// +/// ```rust +/// let foo = PermitDrop::new( +/// permit_alloc(|| +/// Box::new(...) +/// ) +/// ); +/// ``` +/// +/// Here, creation of the Box is guarded by the explicit `permit_alloc` call, +/// and destruction of the Box is guarded by PermitDrop. Neither creation nor +/// destruction will cause an assertion failure from within `assert_no_alloc`. +pub struct PermitDrop(Option); + +impl PermitDrop { + pub fn new(t: T) -> PermitDrop { + permit_alloc(|| { + PermitDrop(Some(t)) + }) + } +} + +impl std::ops::Deref for PermitDrop { + type Target = T; + fn deref(&self) -> &T { self.0.as_ref().unwrap() } +} + +impl std::ops::DerefMut for PermitDrop { + fn deref_mut(&mut self) -> &mut T { self.0.as_mut().unwrap() } +} + +impl Iterator for PermitDrop { + type Item = I::Item; + fn next(&mut self) -> Option { + (**self).next() + } +} + + +impl Drop for PermitDrop { + fn drop(&mut self) { + let mut tmp = None; + std::mem::swap(&mut tmp, &mut self.0); + permit_alloc(|| { + std::mem::drop(tmp); + }); + } +} diff --git a/rust/assert-no-alloc/tests/test.rs b/rust/assert-no-alloc/tests/test.rs new file mode 100644 index 0000000..d5f6c8d --- /dev/null +++ b/rust/assert-no-alloc/tests/test.rs @@ -0,0 +1,148 @@ +use assert_no_alloc::*; +use std::panic::catch_unwind; + +#[global_allocator] +static A: AllocDisabler = AllocDisabler; + +#[cfg(not(feature = "warn_debug"))] +compile_error!("The test suite requires the warn_debug feature to be enabled. Use `cargo test --features warn_debug`"); + +// This is only a kludge; what we actually want to check is "will do_alloc() be optimized out?", e.g. due to +// compiler optimizations turned on in --release mode. We can't do that, the closest we can get is to check +// whether debug_assertions are disabled, which coincidentially also happens in release mode. +#[cfg(not(debug_assertions))] +compile_error!("The test suite only works in debug mode. Use `cargo test --features warn_debug`"); + +#[cfg(feature = "warn_debug")] +fn check_and_reset() -> bool { + let result = violation_count() > 0; + reset_violation_count(); + result +} + +// Provide a stub check_and_reset() function if warn_debug is disabled. This will never be compiled due to the +// compile_error!() above, but this stub ensures that the output will not be cluttered with spurious error +// messages. +#[cfg(not(feature = "warn_debug"))] +fn check_and_reset() -> bool { unreachable!() } + +fn do_alloc() { + let _tmp: Box = Box::new(42); +} + +#[test] +fn ok_noop() { + assert_eq!(check_and_reset(), false); + do_alloc(); + assert_eq!(check_and_reset(), false); +} + +#[test] +fn ok_simple() { + assert_eq!(check_and_reset(), false); + assert_no_alloc(|| { + }); + + do_alloc(); + assert_eq!(check_and_reset(), false); +} + +#[test] +fn ok_nested() { + assert_eq!(check_and_reset(), false); + assert_no_alloc(|| { + assert_no_alloc(|| { + }); + }); + + do_alloc(); + assert_eq!(check_and_reset(), false); +} + +#[test] +fn forbidden_simple() { + assert_eq!(check_and_reset(), false); + assert_no_alloc(|| { + do_alloc(); + }); + assert_eq!(check_and_reset(), true); +} + +#[test] +fn forbidden_in_nested() { + assert_eq!(check_and_reset(), false); + assert_no_alloc(|| { + assert_no_alloc(|| { + do_alloc(); + }); + }); + assert_eq!(check_and_reset(), true); +} + +#[test] +fn forbidden_after_nested() { + assert_eq!(check_and_reset(), false); + assert_no_alloc(|| { + assert_no_alloc(|| { + }); + do_alloc(); + }); + assert_eq!(check_and_reset(), true); +} + +#[test] +fn unwind_ok() { + assert_eq!(check_and_reset(), false); + assert_no_alloc(|| { + let r = catch_unwind(|| { + assert_no_alloc(|| { + panic!(); + }); + }); + assert!(r.is_err()); + }); + check_and_reset(); // unwinding might have allocated memory; we don't care about that. + do_alloc(); + assert_eq!(check_and_reset(), false); +} + +#[test] +fn unwind_nested() { + assert_eq!(check_and_reset(), false); + assert_no_alloc(|| { + let r = catch_unwind(|| { + assert_no_alloc(|| { + panic!(); + }); + }); + assert!(r.is_err()); + + check_and_reset(); // unwinding might have allocated memory; we don't care about that. + do_alloc(); + assert_eq!(check_and_reset(), true); + }); +} + +#[test] +fn unwind_nested2() { + assert_eq!(check_and_reset(), false); + assert_no_alloc(|| { + assert_no_alloc(|| { + let r = catch_unwind(|| { + assert_no_alloc(|| { + assert_no_alloc(|| { + panic!(); + }); + }); + }); + assert!(r.is_err()); + + check_and_reset(); // unwinding might have allocated memory; we don't care about that. + do_alloc(); + assert_eq!(check_and_reset(), true); + }); + }); + check_and_reset(); // unwinding might have allocated memory; we don't care about that. + do_alloc(); + assert_eq!(check_and_reset(), false); +} From 1df75a2caba10d456f9e8a43c1e86b6e2b37d97f Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 14 Feb 2024 12:50:37 +0900 Subject: [PATCH 7/7] Merge branch 'as/serf-guard' into msl/guard - manual - compiles - tests fail - doesn't run --- rust/ares/Cargo.lock | 10 +- rust/ares/Cargo.toml | 4 +- rust/ares/src/interpreter.rs | 1010 +++++++++-------- rust/ares/src/serf.rs | 23 +- rust/ares_crypto/Cargo.toml | 4 +- rust/ares_guard/c-src/guard.c | 228 ++-- rust/ares_guard/c-src/guard.h | 8 +- rust/ares_guard/src/lib.rs | 4 +- rust/ares_pma/c-src/btree.c | 1 + .../Cargo.toml | 0 .../LICENSE | 0 .../README.md | 0 .../examples/main.rs | 0 .../src/lib.rs | 18 + .../tests/test.rs | 0 15 files changed, 635 insertions(+), 675 deletions(-) rename rust/{assert-no-alloc => rust-assert-no-alloc}/Cargo.toml (100%) rename rust/{assert-no-alloc => rust-assert-no-alloc}/LICENSE (100%) rename rust/{assert-no-alloc => rust-assert-no-alloc}/README.md (100%) rename rust/{assert-no-alloc => rust-assert-no-alloc}/examples/main.rs (100%) rename rust/{assert-no-alloc => rust-assert-no-alloc}/src/lib.rs (92%) rename rust/{assert-no-alloc => rust-assert-no-alloc}/tests/test.rs (100%) 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..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; @@ -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 { diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 8da4e92..70632f1 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 { 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 8aa71f4..4557041 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -15,19 +15,11 @@ #define GD_PAGE_MASK (GD_PAGE_SIZE - 1) #define GD_PAGE_ROUND_DOWN(foo) (foo & (~GD_PAGE_MASK)) -/** - * XX: documentation - */ -typedef struct _gs { - uintptr_t guard_p; - const uintptr_t *stack_pp; - const uintptr_t *alloc_pp; - jmp_buf env_buffer; - struct sigaction prev_sa; -} GuardState; - -static GuardState *_guard_state = NULL; - +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 int32_t _prot_page(void *address, int prot) @@ -53,18 +45,21 @@ _unmark_page(void *address) return _prot_page(address, PROT_READ | PROT_WRITE); } -/** - * Center the guard page. - */ +// Center the guard page. +// 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( - uintptr_t *guard_pp, - const uintptr_t stack_p, - const uintptr_t alloc_p -) { - // Check for strange situations. +_focus_guard() +{ + 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; + 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; @@ -73,20 +68,8 @@ _focus_guard( return guard_oom; } - uintptr_t old_guard_p = *guard_pp; - uintptr_t new_guard_p; - int32_t err = 0; - fprintf(stderr, "guard: focus: old guard = %p\r\n", (void *)old_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; - } - } - // 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); @@ -104,45 +87,50 @@ _focus_guard( // Update guard page tracker fprintf(stderr, "guard: focus: installed guard page at %p\r\n", (void *)new_guard_p); - *guard_pp = 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; + } + } + return 0; } -void +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: sig_handle: invalid signal\r\n"); - siglongjmp(_guard_state->env_buffer, guard_signal); + // XX: do we even want to jump? if this is fatal error, maybe just die now + siglongjmp(env_buffer, guard_signal); } sig_addr = (uintptr_t)si->si_addr; fprintf(stderr, "guard: SIGSEGV address = %p\r\n", (void *)sig_addr); - fprintf(stderr, "guard: sig_handle: %p \r\n", _guard_state); - if ( - sig_addr >= _guard_state->guard_p && - sig_addr < (_guard_state->guard_p + GD_PAGE_SIZE)) + if (sig_addr >= guard_p && + sig_addr < guard_p + GD_PAGE_SIZE) { - fprintf(stderr, "guard: page at %p hit\r\n", (void *)_guard_state->guard_p); - err = _focus_guard( - &(_guard_state->guard_p), - *(_guard_state->stack_pp), - *(_guard_state->alloc_pp)); + fprintf(stderr, "guard: hit: %p\r\n", si->si_addr); + err = _focus_guard(); if (err) { fprintf(stderr, "guard: sig_handle: focus error\r\n"); - siglongjmp(_guard_state->env_buffer, err); + siglongjmp(env_buffer, err); } - } else { - struct sigaction prev_sa = _guard_state->prev_sa; - - fprintf(stderr, "guard: page at %p miss\r\n", (void *)_guard_state->guard_p); + } + else { + 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); @@ -155,8 +143,8 @@ _signal_handler(int sig, siginfo_t *si, void *unused) } } -int32_t -_register_handler(struct sigaction *prev_sa) +static int32_t +_register_handler() { struct sigaction sa; @@ -171,8 +159,8 @@ _register_handler(struct sigaction *prev_sa) // sigemptyset(&sa.sa_mask); // sigaddset(&(sa.sa_mask), SIGSEGV); - // Set the new SIGSEGV handler, and save the old SIGSEGV handler (if any) - if (sigaction(SIGSEGV, &sa, prev_sa)) { + // 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: register: sigaction error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); return guard_sigaction | errno; @@ -181,107 +169,59 @@ _register_handler(struct sigaction *prev_sa) return 0; } -int32_t -_setup( - GuardState **gs_p, - const uintptr_t *const stack_pp, - const uintptr_t *const alloc_pp -) { - GuardState *gs; - int32_t err = 0; - - assert(*gs_p == NULL); - 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)); - - // Setup guard page state - *gs_p = (GuardState *)malloc(sizeof(GuardState)); - gs = *gs_p; - if (gs == NULL) { - fprintf(stderr, "guard: malloc error\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - return guard_malloc | errno; - } - fprintf(stderr, "guard: state allocated to %p \r\n", gs); - - gs->guard_p = 0; - gs->stack_pp = stack_pp; - gs->alloc_pp = alloc_pp; - - // Initialize the guard page - if ((err = _focus_guard(&(gs->guard_p), *stack_pp, *alloc_pp))) { - fprintf(stderr, "guard: setup: _focus_guard error\r\n"); - return err; - } - - // Register guard page signal handler - if ((err = _register_handler(&(gs->prev_sa)))) { - fprintf(stderr, "guard: setup: _register_handler error\r\n"); - return err; - } - - return 0; -} - -int32_t -_teardown(GuardState** gs_p) -{ - int32_t err = 0; - - if (*gs_p != NULL) { - GuardState *gs = *gs_p; - - if (gs->guard_p != 0) { - err = _unmark_page((void *)gs->guard_p); - } - - if (sigaction(SIGSEGV, &(gs->prev_sa), NULL)) { - fprintf(stderr, "guard: teardown: sigaction error\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - err = guard_sigaction | errno; - } - - free(gs); - *gs_p = NULL; - } - - return err; -} - int32_t guard( callback f, void *closure, - const uintptr_t *const stack_pp, - const uintptr_t *const alloc_pp, + const uintptr_t *const s_pp, + const uintptr_t *const a_pp, void ** ret ) { int32_t err = 0; + int32_t td_err; - // Setup the guard page - fprintf(stderr, "guard: setup\r\n"); - if ((err = _setup(&_guard_state, stack_pp, alloc_pp))) { + 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)); + + 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; + } + + // Register guard page signal handler + if ((err = _register_handler())) { + fprintf(stderr, "guard: setup: _register_handler error\r\n"); goto done; } // Run given closure fprintf(stderr, "guard: run\r\n"); - if (!(err = sigsetjmp(_guard_state->env_buffer, 1))) { + if (!(err = sigsetjmp(env_buffer, 1))) { *ret = f(closure); - - // Clean up - fprintf(stderr, "guard: teardown\r\n"); - err = _teardown(&_guard_state); - - fprintf(stderr, "guard: return\r\n"); - return err; - } else { -done: - // Clean up - fprintf(stderr, "guard: teardown\r\n"); - _teardown(&_guard_state); - - fprintf(stderr, "guard: return\r\n"); - return err; } + +done: + // Clean up + if (guard_p != 0) { + td_err = _unmark_page((void *)guard_p); + } + + 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 fd08343..ae23815 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -5,7 +5,7 @@ /** * Error codes and flags. - * + * * The flags are bitwise added to the errno of their respective errors. */ typedef enum { @@ -51,9 +51,9 @@ int32_t guard( callback f, 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 38e6c3c..c10fb5a 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -4,8 +4,8 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -pub const GUARD_NULL: u32 = guard_err_guard_null; -pub const GUARD_SIGNAL: u32 = guard_err_guard_signal; +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; diff --git a/rust/ares_pma/c-src/btree.c b/rust/ares_pma/c-src/btree.c index 4b2efd2..9ecf982 100644 --- a/rust/ares_pma/c-src/btree.c +++ b/rust/ares_pma/c-src/btree.c @@ -1567,6 +1567,7 @@ _flist_grow(BT_state *state, size_t pages) /* grows the backing file by PMA_GROW_SIZE_p and appends this freespace to the flist */ { + exit(1); /* grow the backing file by at least PMA_GROW_SIZE_p */ pages = MAX(pages, PMA_GROW_SIZE_p); off_t bytes = P2BYTES(pages); diff --git a/rust/assert-no-alloc/Cargo.toml b/rust/rust-assert-no-alloc/Cargo.toml similarity index 100% rename from rust/assert-no-alloc/Cargo.toml rename to rust/rust-assert-no-alloc/Cargo.toml 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 92% rename from rust/assert-no-alloc/src/lib.rs rename to rust/rust-assert-no-alloc/src/lib.rs index 00f6a92..0e2b0aa 100644 --- a/rust/assert-no-alloc/src/lib.rs +++ b/rust/rust-assert-no-alloc/src/lib.rs @@ -91,6 +91,23 @@ pub fn assert_no_alloc T> (func: F) -> T { return ret; } +#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled +/// 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. @@ -142,6 +159,7 @@ pub fn reset_counters() { + #[cfg(not(all(feature = "disable_release", not(debug_assertions))))] // if not disabled /// The custom allocator that handles the checking. /// 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