From a91442491d3cbe4a673b37fa4d2e7158335ff6d1 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 17 Jan 2024 15:14:17 -0500 Subject: [PATCH 01/58] vendor `assert_no_alloc` with `ensure_no_alloc` function added --- rust/ares/Cargo.lock | 2 -- rust/ares/Cargo.toml | 4 ++-- rust/rust-assert-no-alloc | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) create mode 160000 rust/rust-assert-no-alloc diff --git a/rust/ares/Cargo.lock b/rust/ares/Cargo.lock index 679043fd..49c40449 100644 --- a/rust/ares/Cargo.lock +++ b/rust/ares/Cargo.lock @@ -62,8 +62,6 @@ dependencies = [ [[package]] name = "assert_no_alloc" version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ca83137a482d61d916ceb1eba52a684f98004f18e0cafea230fe5579c178a3" [[package]] name = "atty" diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 2b3cfe65..0babd645 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -15,9 +15,9 @@ 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" +assert_no_alloc = { path = "../rust-assert-no-alloc" } # use this when debugging requires allocation (e.g. eprintln) -# assert_no_alloc = {version="1.1.2", features=["warn_debug"]} +# assert_no_alloc = { path = "../rust-assert-no-alloc", features=["warn_debug"] } bitvec = "1.0.0" criterion = "0.4" either = "1.9.0" diff --git a/rust/rust-assert-no-alloc b/rust/rust-assert-no-alloc new file mode 160000 index 00000000..11f0f411 --- /dev/null +++ b/rust/rust-assert-no-alloc @@ -0,0 +1 @@ +Subproject commit 11f0f41123f7fcaf0ad1c23a6a17d97f4650e824 From 3411adc93097c6030af3f19b46d615b24897e650 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 17 Jan 2024 16:47:33 -0500 Subject: [PATCH 02/58] wip: st. antony, abbot --- rust/ares/Cargo.lock | 9 + rust/ares/Cargo.toml | 1 + rust/ares/src/mem.rs | 1 + rust/ares_guard/Cargo.lock | 432 ++++++++++++++++++++++++++++++++ rust/ares_guard/Cargo.toml | 15 ++ rust/ares_guard/build.rs | 76 ++++++ rust/ares_guard/c-src/guard.c | 9 + rust/ares_guard/c-src/guard.h | 18 ++ rust/ares_guard/c-src/wrapper.h | 1 + rust/ares_guard/src/lib.rs | 5 + 10 files changed, 567 insertions(+) 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 diff --git a/rust/ares/Cargo.lock b/rust/ares/Cargo.lock index 49c40449..92331649 100644 --- a/rust/ares/Cargo.lock +++ b/rust/ares/Cargo.lock @@ -21,6 +21,7 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" name = "ares" version = "0.1.0" dependencies = [ + "ares_guard", "ares_macros", "ares_pma", "assert_no_alloc", @@ -43,6 +44,14 @@ dependencies = [ "urcrypt-sys", ] +[[package]] +name = "ares_guard" +version = "0.1.0" +dependencies = [ + "bindgen 0.69.1", + "cc", +] + [[package]] name = "ares_macros" version = "0.1.0" diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 0babd645..9178b6ff 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -11,6 +11,7 @@ edition = "2018" # Please keep these alphabetized [dependencies] +ares_guard = { path = "../ares_guard" } ares_macros = { path = "../ares_macros" } # Use this when debugging requires the debug printfs in the PMA # ares_pma = { path = "../ares_pma", features=["debug_prints"] } diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 31f81c5c..979b6e0c 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -2,6 +2,7 @@ use crate::assert_acyclic; use crate::assert_no_forwarding_pointers; use crate::assert_no_junior_pointers; use crate::noun::{Atom, Cell, CellMemory, IndirectAtom, Noun, NounAllocator}; +use ares_guard::*; use assert_no_alloc::permit_alloc; use either::Either::{self, Left, Right}; use ibig::Stack; diff --git a/rust/ares_guard/Cargo.lock b/rust/ares_guard/Cargo.lock new file mode 100644 index 00000000..26d68b9a --- /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 00000000..9db7918f --- /dev/null +++ b/rust/ares_guard/Cargo.toml @@ -0,0 +1,15 @@ +[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.1" +cc = "1.0" + +[features] +debug_prints = [] diff --git a/rust/ares_guard/build.rs b/rust/ares_guard/build.rs new file mode 100644 index 00000000..1bd7e6e1 --- /dev/null +++ b/rust/ares_guard/build.rs @@ -0,0 +1,76 @@ +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("-Wpedantic") + .flag("-Wformat=2") + .flag("-Wno-unused-parameter") + .flag("-Wshadow") + .flag("-Wwrite-strings") + .flag("-Wstrict-prototypes") + .flag("-Wold-style-definition") + .flag("-Wredundant-decls") + .flag("-Wnested-externs") + .flag("-Wmissing-include-dirs") + .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 00000000..f09564dd --- /dev/null +++ b/rust/ares_guard/c-src/guard.c @@ -0,0 +1,9 @@ +#include + +#include "guard.h" + + +void *guard(void *(*f)(void *), void *arg, void *stack, void *alloc) { + void* res = f(arg); + return res; +} diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h new file mode 100644 index 00000000..47ba7abd --- /dev/null +++ b/rust/ares_guard/c-src/guard.h @@ -0,0 +1,18 @@ +#ifndef __GUARD_H__ +#define __GUARD_H__ +#include +#include + +/** + * Execute the given closure `f` in a memory arena protected by a guard + * page. The guard page is a single page of memory marked with `PROT_NONE` + * via `mprotect`. The guard page is initially placed in the approximate + * middle of the arena. When the closure attempts to write to the guard + * page, a `SIGSEGV` signal is raised. The signal handler will then attempt + * to move the guard page to the middle of the remaining free space in the + * arena. If there is no more free space, then memory exhaustion has + * occurred and the program will abort via a `bail:meme` error. + */ +void *guard(void *(*f)(void *), void *arg, void *stack, void *alloc); + +#endif diff --git a/rust/ares_guard/c-src/wrapper.h b/rust/ares_guard/c-src/wrapper.h new file mode 100644 index 00000000..29d14dd2 --- /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 00000000..a38a13a8 --- /dev/null +++ b/rust/ares_guard/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); From f7f01f50734838a4c47cd357ee0cf4cb654b71f0 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Thu, 18 Jan 2024 16:32:50 -0500 Subject: [PATCH 03/58] guard: initial commit --- rust/ares/src/interpreter.rs | 945 +++++++++++++++++----------------- rust/ares/src/mem.rs | 1 - rust/ares_guard/Cargo.toml | 3 - rust/ares_guard/c-src/guard.c | 91 +++- rust/ares_guard/c-src/guard.h | 32 +- 5 files changed, 589 insertions(+), 483 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 358f3962..5d2e2936 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -15,8 +15,9 @@ use crate::noun::{Atom, Cell, IndirectAtom, Noun, Slots, D, T}; use crate::serf::TERMINATOR; use crate::trace::{write_nock_trace, TraceInfo, TraceStack}; use crate::unifying_equality::unifying_equality; +use ares_guard::guard; 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 std::result; @@ -388,533 +389,547 @@ 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)?; - - 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 Err(Error::Deterministic(D(0))); - } - } - NockWork::Work1(once) => { - res = once.noun; - context.stack.pop::(); - } - NockWork::Work2(mut vale) => { - if (*terminator).load(Ordering::Relaxed) { - break Err(Error::NonDeterministic(D(0))); - } + let nock = assert_no_alloc(|| { + ensure_alloc_counters(|| unsafe { + push_formula(&mut context.stack, formula, true)?; - 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); + loop { + let work: NockWork = *context.stack.top(); + match work { + NockWork::Done => { + write_trace(context); - debug_assertions(stack, orig_subject); - debug_assertions(stack, subject); - debug_assertions(stack, res); + let stack = &mut context.stack; + 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; + stack.preserve(&mut context.cache); + stack.preserve(&mut context.cold); + stack.preserve(&mut context.warm); + stack.preserve(&mut res); + stack.frame_pop(); - subject = vale.subject; - stack.pop::(); + debug_assertions(stack, orig_subject); + debug_assertions(stack, res); - debug_assertions(stack, orig_subject); - debug_assertions(stack, subject); - debug_assertions(stack, 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 Err(Error::Deterministic(D(0))); - } - } - }, - 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)?; + break Ok(res); } - Todo5::TestEquals => { + NockWork::Ret => { + write_trace(context); + 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::(); + 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::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 Err(Error::Deterministic(D(0))); - } - } else { - // Test branch of Nock 6 must return a direct atom - break Err(Error::Deterministic(D(0))); + 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)?; } - } - }, - 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)?; + TodoCons::ComputeTail => { + cons.todo = TodoCons::Cons; + cons.head = res; + *context.stack.top() = NockWork::WorkCons(cons); + push_formula(&mut context.stack, cons.tail, 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]); + TodoCons::Cons => { + let stack = &mut context.stack; + res = T(stack, &[cons.head, res]); stack.pop::(); - push_formula(stack, pins.formula, true)?; + } + }, + NockWork::Work0(zero) => { + if let Ok(noun) = subject.slot_atom(zero.axis) { + res = noun; + context.stack.pop::(); } else { - pins.todo = Todo8::RestoreSubject; - pins.pin = subject; - *stack.top() = NockWork::Work8(pins); - subject = T(stack, &[res, subject]); - push_formula(stack, pins.formula, false)?; + // Axis invalid for input Noun + break Err(Error::Deterministic(D(0))); } } - Todo8::RestoreSubject => { - subject = pins.pin; + NockWork::Work1(once) => { + res = once.noun; context.stack.pop::(); } - }, - NockWork::Work9(mut kale) => { - if (*terminator).load(Ordering::Relaxed) { - break Err(Error::NonDeterministic(D(0))); - } - - match kale.todo { - Todo9::ComputeCore => { - kale.todo = Todo9::ComputeResult; - *context.stack.top() = NockWork::Work9(kale); - push_formula(&mut context.stack, kale.core, false)?; + NockWork::Work2(mut vale) => { + if (*terminator).load(Ordering::Relaxed) { + break Err(Error::NonDeterministic(D(0))); } - 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 kale.tail { + if vale.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)?; + subject = vale.subject; + push_formula(stack, res, true)?; } else { - kale.todo = Todo9::RestoreSubject; - kale.core = subject; - *stack.top() = NockWork::Work9(kale); + 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); - 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); - }; - }; + 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); + } + } + } + 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 { - // Axis into core must be atom + // Cannot increment (Nock 4) a cell break Err(Error::Deterministic(D(0))); } } - Todo9::RestoreSubject => { + }, + 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; - - subject = kale.core; + let saved_value_ptr = &mut five.left; + res = if unifying_equality(stack, &mut res, saved_value_ptr) { + D(0) + } else { + D(1) + }; 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)?; + }, + 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 Err(Error::Deterministic(D(0))); + } + } else { + // Test branch of Nock 6 must return a direct atom + break Err(Error::Deterministic(D(0))); + } } - Todo10::ComputePatch => { - diet.todo = Todo10::Edit; - diet.tree = res; - *context.stack.top() = NockWork::Work10(diet); - push_formula(&mut context.stack, diet.patch, false)?; + }, + 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)?; } - Todo10::Edit => { - res = edit(&mut context.stack, diet.axis.as_bitslice(), res, diet.tree); + 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::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); + }, + 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 Err(Error::NonDeterministic(D(0))); + } + + 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); + }; + }; + } + } else { + // Axis into core must be atom + break Err(Error::Deterministic(D(0))); } } - } else { - dint.todo = Todo11D::ComputeResult; - *context.stack.top() = NockWork::Work11D(dint); - push_formula(&mut context.stack, dint.hint, false)?; + 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); + } } } - 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::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)?; } - } else { - if dint.tail { + 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::(); - } 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; + 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)?; + } } - 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::(); + 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); + } } - 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)?; } - } else { - if sint.tail { - context.stack.pop::(); + } + 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 { - sint.todo = Todo11S::Done; - *context.stack.top() = NockWork::Work11S(sint); + 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)?; } - 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; + Todo11S::Done => { + if let Some(found) = hint::match_post_nock( + context, subject, sint.tag, None, sint.body, res, + ) { + res = found; + } + context.stack.pop::(); } - 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))); + }, + 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) => 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); } - Right(cell) => { - res = cell.tail(); - context.scry_stack = scry_stack; - context.stack.pop::(); + Error::ScryBlocked(_) => { + break Err(Error::NonDeterministic(D(0))); } }, - }, - Err(error) => match error { - Error::Deterministic(trace) | Error::ScryCrashed(trace) => { - break Err(Error::ScryCrashed(trace)); - } - Error::NonDeterministic(_) => { - break Err(error); - } - Error::ScryBlocked(_) => { - break Err(Error::NonDeterministic(D(0))); - } - }, + } + } else { + // No scry handler + break Err(Error::Deterministic(D(0))); } - } else { - // No scry handler - break Err(Error::Deterministic(D(0))); } - } - }, - }; - } + }, + }; + } + }) }); match nock { diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 979b6e0c..31f81c5c 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -2,7 +2,6 @@ use crate::assert_acyclic; use crate::assert_no_forwarding_pointers; use crate::assert_no_junior_pointers; use crate::noun::{Atom, Cell, CellMemory, IndirectAtom, Noun, NounAllocator}; -use ares_guard::*; use assert_no_alloc::permit_alloc; use either::Either::{self, Left, Right}; use ibig::Stack; diff --git a/rust/ares_guard/Cargo.toml b/rust/ares_guard/Cargo.toml index 9db7918f..5389a204 100644 --- a/rust/ares_guard/Cargo.toml +++ b/rust/ares_guard/Cargo.toml @@ -10,6 +10,3 @@ edition = "2021" [build-dependencies] bindgen = "0.69.1" cc = "1.0" - -[features] -debug_prints = [] diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index f09564dd..afe8b763 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -1,9 +1,94 @@ #include +#include +#include +#include +#include #include "guard.h" -void *guard(void *(*f)(void *), void *arg, void *stack, void *alloc) { - void* res = f(arg); - return res; +#define GD_PAGESZ 16384 + +static void *guard_p = NULL; +static void *stack_p = NULL; +static void *alloc_p = NULL; + +typedef enum { + guard_weird = 0, // strange state + guard_armor = 1, // mprotect failure + guard_spent = 2, // out of memory (bail:meme) + guard_sound = 3 // job's done +} guard_err; + + +guard_err _focus_guard(void *si_addr) { + if (stack_p == NULL || alloc_p == NULL) { + return guard_weird; + } + + // Install the guard page. + if (guard_p == NULL) { + guard_p = stack_p + ((alloc_p - stack_p) / 2); + if (mprotect(guard_p, GD_PAGESZ, PROT_NONE) == -1) { + return guard_armor; + } + } + + // Re-center the guard page if the fault address falls within it. + if (si_addr >= guard_p && si_addr < guard_p + GD_PAGESZ) { + // Unmark the old guard page. + void *old_guard_pg = guard_p; + if (mprotect(old_guard_pg, GD_PAGESZ, PROT_READ | PROT_WRITE) == -1) { + return guard_armor; + } + + // Place the new guard page in the center. + guard_p = stack_p + ((alloc_p - stack_p) / 2); + if (guard_p != old_guard_pg) { + if (mprotect(guard_p, GD_PAGESZ, PROT_NONE) == -1) { + return guard_armor; + } + } else { + return guard_spent; + } + } + + return guard_sound; +} + +void _sigsegv_handler(int sig, siginfo_t *si, void *unused) { + _focus_guard(si->si_addr); +} + +int _register_handler() { + struct sigaction sa; + + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = _sigsegv_handler; + sigemptyset(&sa.sa_mask); + + return sigaction(SIGSEGV, &sa, NULL); +} + +void guard(void *(*f)(void *), void *arg, void **stack, void **alloc, void **ret) { + guard_err err; + + stack_p = *stack; + alloc_p = *alloc; + + if (_register_handler() == -1) { + err = guard_weird; + goto fail; + } + + if ((err = _focus_guard()) != guard_sound) { + goto fail; + } + + *ret = f(arg); + return; + +fail: + *ret = (void *) &err; + return; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 47ba7abd..faf1964e 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -1,18 +1,28 @@ #ifndef __GUARD_H__ #define __GUARD_H__ -#include -#include + /** - * Execute the given closure `f` in a memory arena protected by a guard - * page. The guard page is a single page of memory marked with `PROT_NONE` - * via `mprotect`. The guard page is initially placed in the approximate - * middle of the arena. When the closure attempts to write to the guard - * page, a `SIGSEGV` signal is raised. The signal handler will then attempt - * to move the guard page to the middle of the remaining free space in the - * arena. If there is no more free space, then memory exhaustion has - * occurred and the program will abort via a `bail:meme` error. + * Execute the given closure `f` in the free memory space denoted by + * the given `stack` and `alloc` pointers. If the current NockStack + * frame faces west, the `stack` pointer will be less than the `alloc` + * pointer. If it faces east, the `stack` pointer will be greater than + * the `alloc` pointer. + * + * This function protects the memory space denoted by the `stack` and `alloc` + * pointers with a guard page, which is a single page of memory inside of the + * are which is marked with `PROT_NONE`. The rest of the pages in our memory + * space are marked with `PROT_READ | PROT_WRITE` by default. This means that + * the closure can read and write to the memory space, but if it attempts to + * write to the guard page, a `SIGSEGV` signal will be raised. The signal + * handler will then attempt to move the guard page to the middle of the + * remaining free space in the arena. If there is no more free space, then + * memory exhaustion has occurred and the function will return a value which + * indicates that the caller should abort with a memory exhaustion error + * (`bail:meme`). + * + * TODO: update and correct documentation. */ -void *guard(void *(*f)(void *), void *arg, void *stack, void *alloc); +void guard(void *(*f)(void *), void *arg, void **stack, void **alloc, void **ret); #endif From 28a7bf51539cf58d8aa262169c3b7d66b827f42f Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Thu, 18 Jan 2024 16:46:07 -0500 Subject: [PATCH 04/58] guard: do not focus in top-level --- rust/ares_guard/c-src/guard.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index afe8b763..45e74a01 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -81,10 +81,6 @@ void guard(void *(*f)(void *), void *arg, void **stack, void **alloc, void **ret goto fail; } - if ((err = _focus_guard()) != guard_sound) { - goto fail; - } - *ret = f(arg); return; From 42f56a545a53259e69614e02a973065056051b7b Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Fri, 19 Jan 2024 10:39:45 -0500 Subject: [PATCH 05/58] guard: ready for initial rust wrapper implementation --- rust/ares_guard/c-src/guard.c | 4 ++-- rust/ares_guard/c-src/guard.h | 41 ++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 45e74a01..d79d57c8 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -22,11 +22,11 @@ typedef enum { guard_err _focus_guard(void *si_addr) { - if (stack_p == NULL || alloc_p == NULL) { + if (stack_p == NULL || alloc_p == NULL || si_addr == NULL) { return guard_weird; } - // Install the guard page. + // Install the guard page, if needed. if (guard_p == NULL) { guard_p = stack_p + ((alloc_p - stack_p) / 2); if (mprotect(guard_p, GD_PAGESZ, PROT_NONE) == -1) { diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index faf1964e..a2b368f9 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -3,25 +3,32 @@ /** - * Execute the given closure `f` in the free memory space denoted by - * the given `stack` and `alloc` pointers. If the current NockStack - * frame faces west, the `stack` pointer will be less than the `alloc` - * pointer. If it faces east, the `stack` pointer will be greater than - * the `alloc` pointer. + * 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. * - * This function protects the memory space denoted by the `stack` and `alloc` - * pointers with a guard page, which is a single page of memory inside of the - * are which is marked with `PROT_NONE`. The rest of the pages in our memory - * space are marked with `PROT_READ | PROT_WRITE` by default. This means that - * the closure can read and write to the memory space, but if it attempts to - * write to the guard page, a `SIGSEGV` signal will be raised. The signal - * handler will then attempt to move the guard page to the middle of the - * remaining free space in the arena. If there is no more free space, then - * memory exhaustion has occurred and the function will return a value which - * indicates that the caller should abort with a memory exhaustion error - * (`bail:meme`). + * 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. * - * TODO: update and correct documentation. + * 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`. */ void guard(void *(*f)(void *), void *arg, void **stack, void **alloc, void **ret); From 0d5e8b86aa52e3fddf9f91ae073d50ff85a813c6 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Fri, 19 Jan 2024 15:36:36 -0500 Subject: [PATCH 06/58] wip: today --- rust/ares_guard/build.rs | 3 +- rust/ares_guard/c-src/guard.c | 72 +++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/rust/ares_guard/build.rs b/rust/ares_guard/build.rs index 1bd7e6e1..a67919d9 100644 --- a/rust/ares_guard/build.rs +++ b/rust/ares_guard/build.rs @@ -37,12 +37,13 @@ fn main() { .flag("-g3") .flag("-Wall") .flag("-Wextra") + .flag("-Wgnu-pointer-arith") + .flag("-Wpointer-arith") .flag("-Wpedantic") .flag("-Wformat=2") .flag("-Wno-unused-parameter") .flag("-Wshadow") .flag("-Wwrite-strings") - .flag("-Wstrict-prototypes") .flag("-Wold-style-definition") .flag("-Wredundant-decls") .flag("-Wnested-externs") diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index d79d57c8..508f2078 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -14,26 +14,22 @@ static void *stack_p = NULL; static void *alloc_p = NULL; typedef enum { - guard_weird = 0, // strange state - guard_armor = 1, // mprotect failure - guard_spent = 2, // out of memory (bail:meme) - guard_sound = 3 // job's done -} guard_err; + guard_sound = 0, // job's done + guard_armor = 1, // mprotect + guard_weird = 2, // strange state + guard_spent = 3, // out of memory (bail:meme) + guard_erupt = 4, // sigint +} guard_err_t; +volatile sig_atomic_t guard_err = guard_sound; -guard_err _focus_guard(void *si_addr) { + +guard_err_t _focus_guard(void *si_addr) { + // Check for strange situations. if (stack_p == NULL || alloc_p == NULL || si_addr == NULL) { return guard_weird; } - // Install the guard page, if needed. - if (guard_p == NULL) { - guard_p = stack_p + ((alloc_p - stack_p) / 2); - if (mprotect(guard_p, GD_PAGESZ, PROT_NONE) == -1) { - return guard_armor; - } - } - // Re-center the guard page if the fault address falls within it. if (si_addr >= guard_p && si_addr < guard_p + GD_PAGESZ) { // Unmark the old guard page. @@ -44,6 +40,8 @@ guard_err _focus_guard(void *si_addr) { // Place the new guard page in the center. guard_p = stack_p + ((alloc_p - stack_p) / 2); + // TODO: Ensure the guard_p is page-aligned. + // guard_p = (void *)((uintptr_t)guard_p & ~(GD_PAGESZ - 1)); if (guard_p != old_guard_pg) { if (mprotect(guard_p, GD_PAGESZ, PROT_NONE) == -1) { return guard_armor; @@ -56,35 +54,61 @@ guard_err _focus_guard(void *si_addr) { return guard_sound; } -void _sigsegv_handler(int sig, siginfo_t *si, void *unused) { - _focus_guard(si->si_addr); +void _signal_handler(int sig, siginfo_t *si, void *unused) { + switch (sig) { + case SIGSEGV: + guard_err = _focus_guard(si->si_addr); + break; + case SIGINT: + guard_err = guard_erupt; + break; + default: + break; + } } -int _register_handler() { +guard_err_t _register_handler() { + guard_err_t err = guard_sound; struct sigaction sa; sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = _sigsegv_handler; - sigemptyset(&sa.sa_mask); + sa.sa_sigaction = _signal_handler; + sa.sa_mask = 0; + + if (sigaction(SIGSEGV, &sa, NULL)) { + err = guard_weird; + } - return sigaction(SIGSEGV, &sa, NULL); + return guard_err; } void guard(void *(*f)(void *), void *arg, void **stack, void **alloc, void **ret) { - guard_err err; - + guard_err_t err; stack_p = *stack; alloc_p = *alloc; - if (_register_handler() == -1) { + if (_register_handler() != guard_sound) { err = guard_weird; goto fail; } + if (guard_p == NULL) { + guard_p = stack_p + ((alloc_p - stack_p) / 2); + if (mprotect(guard_p, GD_PAGESZ, PROT_NONE) == -1) { + err = guard_armor; + goto fail; + } + } + *ret = f(arg); + + if (guard_err != guard_sound) { + goto fail; + } + return; fail: - *ret = (void *) &err; + *ret = (void *) &guard_err; return; } From f7fcffc429b0271115d50f594a6ae7a83e888958 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Fri, 19 Jan 2024 16:46:09 -0500 Subject: [PATCH 07/58] wip: friday --- rust/ares_guard/build.rs | 16 +++---- rust/ares_guard/c-src/guard.c | 78 ++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/rust/ares_guard/build.rs b/rust/ares_guard/build.rs index a67919d9..82057473 100644 --- a/rust/ares_guard/build.rs +++ b/rust/ares_guard/build.rs @@ -37,17 +37,17 @@ fn main() { .flag("-g3") .flag("-Wall") .flag("-Wextra") - .flag("-Wgnu-pointer-arith") - .flag("-Wpointer-arith") - .flag("-Wpedantic") .flag("-Wformat=2") - .flag("-Wno-unused-parameter") - .flag("-Wshadow") - .flag("-Wwrite-strings") + .flag("-Wmissing-include-dirs") + .flag("-Wnested-externs") .flag("-Wold-style-definition") + .flag("-Wpedantic") .flag("-Wredundant-decls") - .flag("-Wnested-externs") - .flag("-Wmissing-include-dirs") + .flag("-Wshadow") + .flag("-Wwrite-strings") + .flag("-Wno-unused-parameter") + .flag("-Wno-pointer-arith") + .flag("-Wno-strict-prototypes") .try_compile("guard"); if let Err(err) = res { diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 508f2078..6e77b14b 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -7,7 +7,8 @@ #include "guard.h" -#define GD_PAGESZ 16384 +#define GD_PAGEBITS 14ULL +#define GD_PAGESIZE (1ULL << GD_PAGEBITS) /* 16K */ static void *guard_p = NULL; static void *stack_p = NULL; @@ -19,45 +20,60 @@ typedef enum { guard_weird = 2, // strange state guard_spent = 3, // out of memory (bail:meme) guard_erupt = 4, // sigint -} guard_err_t; +} guard_error; volatile sig_atomic_t guard_err = guard_sound; -guard_err_t _focus_guard(void *si_addr) { +// Center the guard page. +guard_error _focus_guard() { // Check for strange situations. - if (stack_p == NULL || alloc_p == NULL || si_addr == NULL) { + if (stack_p == NULL || alloc_p == NULL) { return guard_weird; } - // Re-center the guard page if the fault address falls within it. - if (si_addr >= guard_p && si_addr < guard_p + GD_PAGESZ) { - // Unmark the old guard page. - void *old_guard_pg = guard_p; - if (mprotect(old_guard_pg, GD_PAGESZ, PROT_READ | PROT_WRITE) == -1) { - return guard_armor; - } + // Unmark the old guard page. + void *old_guard_p = guard_p; + if (mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + return guard_armor; + } - // Place the new guard page in the center. + // Place the new guard page in the center. + if (stack_p > alloc_p) { + guard_p = stack_p - ((stack_p - alloc_p) / 2); + } + else if (stack_p < alloc_p) { guard_p = stack_p + ((alloc_p - stack_p) / 2); - // TODO: Ensure the guard_p is page-aligned. - // guard_p = (void *)((uintptr_t)guard_p & ~(GD_PAGESZ - 1)); - if (guard_p != old_guard_pg) { - if (mprotect(guard_p, GD_PAGESZ, PROT_NONE) == -1) { - return guard_armor; - } - } else { - return guard_spent; + } + else { + return guard_weird; + } + guard_p = (void *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1)); + + // Mark the new guard page. + if (guard_p != old_guard_p) { + if (mprotect(guard_p, GD_PAGESIZE, PROT_NONE) == -1) { + return guard_armor; } + } else { + return guard_spent; } return guard_sound; } +guard_error _slash_guard(void *si_addr) { + if (si_addr >= guard_p && si_addr < guard_p + GD_PAGESIZE) { + return _focus_guard(); + } + + return guard_weird; +} + void _signal_handler(int sig, siginfo_t *si, void *unused) { switch (sig) { case SIGSEGV: - guard_err = _focus_guard(si->si_addr); + guard_err = _slash_guard(si->si_addr); break; case SIGINT: guard_err = guard_erupt; @@ -67,37 +83,31 @@ void _signal_handler(int sig, siginfo_t *si, void *unused) { } } -guard_err_t _register_handler() { - guard_err_t err = guard_sound; +guard_error _register_handler() { struct sigaction sa; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = _signal_handler; sa.sa_mask = 0; - if (sigaction(SIGSEGV, &sa, NULL)) { - err = guard_weird; + if (sigaction(SIGSEGV, &sa, NULL) || sigaction(SIGINT, &sa, NULL)) { + return guard_weird; } - return guard_err; + return guard_sound; } void guard(void *(*f)(void *), void *arg, void **stack, void **alloc, void **ret) { - guard_err_t err; stack_p = *stack; alloc_p = *alloc; if (_register_handler() != guard_sound) { - err = guard_weird; + guard_err = guard_weird; goto fail; } - if (guard_p == NULL) { - guard_p = stack_p + ((alloc_p - stack_p) / 2); - if (mprotect(guard_p, GD_PAGESZ, PROT_NONE) == -1) { - err = guard_armor; - goto fail; - } + if (guard_p == NULL && (guard_err = _focus_guard()) != guard_sound) { + goto fail; } *ret = f(arg); From cb73bcd8ca113351f0cd10717eff7cd830e32df6 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 22 Jan 2024 20:57:34 -0500 Subject: [PATCH 08/58] wip: sts. vincent and anastasius --- rust/ares/src/interpreter.rs | 1011 +++++++++++++++++---------------- rust/ares_guard/c-src/guard.c | 30 +- rust/ares_guard/c-src/guard.h | 11 +- 3 files changed, 558 insertions(+), 494 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 5d2e2936..2b10cbd8 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -15,11 +15,13 @@ use crate::noun::{Atom, Cell, IndirectAtom, Noun, Slots, D, T}; use crate::serf::TERMINATOR; use crate::trace::{write_nock_trace, TraceInfo, TraceStack}; use crate::unifying_equality::unifying_equality; -use ares_guard::guard; +use ares_guard::{guard, guard_err}; use ares_macros::tas; use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters}; use bitvec::prelude::{BitSlice, Lsb0}; +use bitvec::ptr::null_mut; use either::Either::*; +use std::ffi::c_void; use std::result; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -317,6 +319,14 @@ pub enum Error { NonDeterministic(Noun), // trace } +pub enum GuardError { + GuardSound = 0, + GuardArmor = 1, + GuardWeird = 2, + GuardSpent = 3, + GuardErupt = 4, +} + impl Preserve for Error { unsafe fn preserve(&mut self, stack: &mut NockStack) { match self { @@ -358,6 +368,51 @@ fn debug_assertions(stack: &mut NockStack, noun: Noun) { assert_no_junior_pointers!(stack, noun); } +extern "C" fn rust_callback(arg: *mut c_void) -> *mut c_void { + let closure: &mut Box *mut c_void> = + unsafe { &mut *(arg as *mut Box *mut c_void>) }; + closure() +} + +pub fn call_with_guard Result>( + f: F, + stack: *mut *mut c_void, + alloc: *mut *mut c_void, + ret: *mut *mut c_void, +) -> Result { + let boxed_f = Box::new(f); + + unsafe { + let result: *mut c_void = std::ptr::null_mut(); + let raw_f = Box::into_raw(Box::new(boxed_f)); + + guard( + Some(rust_callback), + raw_f as *mut c_void, + stack, + alloc, + ret, + ); + + let _ = Box::from_raw(raw_f); + + if !result.is_null() { + let err = *(result as *const guard_err); + match err { + 0 => { // sound + let res = result as *mut Result; + *res + }, + // TODO: handle other errors explicitly. + _ => Err(Error::Deterministic(D(0))), + } + } + else { + (Box::from_raw(raw_f))() + } + } +} + /** Interpret nock */ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Result { let terminator = Arc::clone(&TERMINATOR); @@ -390,545 +445,553 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res // // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) let nock = assert_no_alloc(|| { - ensure_alloc_counters(|| unsafe { - push_formula(&mut context.stack, formula, true)?; - - 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); + ensure_alloc_counters(|| { + let stack_p = context.stack.get_stack_pointer(); + let alloc_p = context.stack.get_alloc_pointer(); + call_with_guard(|| unsafe { + push_formula(&mut context.stack, formula, true)?; + + loop { + let work: NockWork = *context.stack.top(); + match work { + NockWork::Done => { + write_trace(context); - stack.preserve(&mut context.cache); - stack.preserve(&mut context.cold); - stack.preserve(&mut context.warm); - stack.preserve(&mut res); - stack.frame_pop(); + let stack = &mut context.stack; + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); - debug_assertions(stack, orig_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(); - break Ok(res); - } - NockWork::Ret => { - write_trace(context); + debug_assertions(stack, orig_subject); + debug_assertions(stack, res); - 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)?; + break Ok(res); } - 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 => { + NockWork::Ret => { + write_trace(context); + 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 Err(Error::Deterministic(D(0))); + 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::Work1(once) => { - res = once.noun; - context.stack.pop::(); - } - NockWork::Work2(mut vale) => { - if (*terminator).load(Ordering::Relaxed) { - break Err(Error::NonDeterministic(D(0))); - } - - match vale.todo { - Todo2::ComputeSubject => { - vale.todo = Todo2::ComputeFormula; - *context.stack.top() = NockWork::Work2(vale); - push_formula(&mut context.stack, vale.subject, false)?; + 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)?; } - Todo2::ComputeFormula => { - vale.todo = Todo2::ComputeResult; - vale.subject = res; - *context.stack.top() = NockWork::Work2(vale); - push_formula(&mut context.stack, vale.formula, false)?; + TodoCons::ComputeTail => { + cons.todo = TodoCons::Cons; + cons.head = res; + *context.stack.top() = NockWork::WorkCons(cons); + push_formula(&mut context.stack, cons.tail, false)?; } - Todo2::ComputeResult => { + TodoCons::Cons => { 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; + res = T(stack, &[cons.head, res]); stack.pop::(); - - debug_assertions(stack, orig_subject); - debug_assertions(stack, subject); - debug_assertions(stack, 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(); + }, + NockWork::Work0(zero) => { + if let Ok(noun) = subject.slot_atom(zero.axis) { + res = noun; context.stack.pop::(); } else { - // Cannot increment (Nock 4) a cell - break Err(Error::Deterministic(D(0))); - } - } - }, - 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)?; - } else { - // Test branch of Nock 6 must return 0 or 1 - break Err(Error::Deterministic(D(0))); - } - } else { - // Test branch of Nock 6 must return a direct atom + // Axis invalid for input Noun break Err(Error::Deterministic(D(0))); } } - }, - 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; + NockWork::Work1(once) => { + res = once.noun; context.stack.pop::(); } - }, - NockWork::Work9(mut kale) => { - if (*terminator).load(Ordering::Relaxed) { - break Err(Error::NonDeterministic(D(0))); - } - - match kale.todo { - Todo9::ComputeCore => { - kale.todo = Todo9::ComputeResult; - *context.stack.top() = NockWork::Work9(kale); - push_formula(&mut context.stack, kale.core, false)?; + NockWork::Work2(mut vale) => { + if (*terminator).load(Ordering::Relaxed) { + break Err(Error::NonDeterministic(D(0))); } - 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 kale.tail { + if vale.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)?; + subject = vale.subject; + push_formula(stack, res, true)?; } else { - kale.todo = Todo9::RestoreSubject; - kale.core = subject; - *stack.top() = NockWork::Work9(kale); + 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); - 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); - }; - }; + 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); + } + } + } + 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 { - // Axis into core must be atom + // Cannot increment (Nock 4) a cell break Err(Error::Deterministic(D(0))); } } - Todo9::RestoreSubject => { + }, + 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; - - subject = kale.core; + let saved_value_ptr = &mut five.left; + res = if unifying_equality(stack, &mut res, saved_value_ptr) { + D(0) + } else { + D(1) + }; 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)?; + }, + 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)?; } - Todo10::ComputePatch => { - diet.todo = Todo10::Edit; - diet.tree = res; - *context.stack.top() = NockWork::Work10(diet); - push_formula(&mut context.stack, diet.patch, 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 Err(Error::Deterministic(D(0))); + } + } else { + // Test branch of Nock 6 must return a direct atom + break Err(Error::Deterministic(D(0))); + } } - Todo10::Edit => { - res = edit( - &mut context.stack, - diet.axis.as_bitslice(), - res, - diet.tree, - ); + }, + 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::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); + }, + 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 Err(Error::NonDeterministic(D(0))); + } + + 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); + }; + }; + } + } else { + // Axis into core must be atom + break Err(Error::Deterministic(D(0))); } } - } else { - dint.todo = Todo11D::ComputeResult; - *context.stack.top() = NockWork::Work11D(dint); - push_formula(&mut context.stack, dint.hint, false)?; + 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); + } } } - 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::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)?; } - } else { - if dint.tail { + 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::(); - } 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; + 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)?; + } } - 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::(); + 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); + } } - 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)?; } - } else { - if sint.tail { - context.stack.pop::(); + } + 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 { - sint.todo = Todo11S::Done; - *context.stack.top() = NockWork::Work11S(sint); + 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)?; } - 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; + Todo11S::Done => { + if let Some(found) = hint::match_post_nock( + context, subject, sint.tag, None, sint.body, res, + ) { + res = found; + } + context.stack.pop::(); } - 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))); + }, + 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) => 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); } - Right(cell) => { - res = cell.tail(); - context.scry_stack = scry_stack; - context.stack.pop::(); + Error::ScryBlocked(_) => { + break Err(Error::NonDeterministic(D(0))); } }, - }, - Err(error) => match error { - Error::Deterministic(trace) | Error::ScryCrashed(trace) => { - break Err(Error::ScryCrashed(trace)); - } - Error::NonDeterministic(_) => { - break Err(error); - } - Error::ScryBlocked(_) => { - break Err(Error::NonDeterministic(D(0))); - } - }, + } + } else { + // No scry handler + break Err(Error::Deterministic(D(0))); } - } else { - // No scry handler - break Err(Error::Deterministic(D(0))); } - } - }, - }; - } + }, + }; + } + }, + stack_p as *mut *mut c_void, + alloc_p as *mut *mut c_void, + std::ptr::null_mut() as *mut *mut c_void, + ) }) }); diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 6e77b14b..1add3df4 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -14,19 +14,11 @@ static void *guard_p = NULL; static void *stack_p = NULL; static void *alloc_p = NULL; -typedef enum { - guard_sound = 0, // job's done - guard_armor = 1, // mprotect - guard_weird = 2, // strange state - guard_spent = 3, // out of memory (bail:meme) - guard_erupt = 4, // sigint -} guard_error; - -volatile sig_atomic_t guard_err = guard_sound; +volatile sig_atomic_t err = guard_sound; // Center the guard page. -guard_error _focus_guard() { +guard_err _focus_guard() { // Check for strange situations. if (stack_p == NULL || alloc_p == NULL) { return guard_weird; @@ -62,7 +54,7 @@ guard_error _focus_guard() { return guard_sound; } -guard_error _slash_guard(void *si_addr) { +guard_err _slash_guard(void *si_addr) { if (si_addr >= guard_p && si_addr < guard_p + GD_PAGESIZE) { return _focus_guard(); } @@ -73,17 +65,17 @@ guard_error _slash_guard(void *si_addr) { void _signal_handler(int sig, siginfo_t *si, void *unused) { switch (sig) { case SIGSEGV: - guard_err = _slash_guard(si->si_addr); + err = _slash_guard(si->si_addr); break; case SIGINT: - guard_err = guard_erupt; + err = guard_erupt; break; default: break; } } -guard_error _register_handler() { +guard_err _register_handler() { struct sigaction sa; sa.sa_flags = SA_SIGINFO; @@ -97,28 +89,28 @@ guard_error _register_handler() { return guard_sound; } -void guard(void *(*f)(void *), void *arg, void **stack, void **alloc, void **ret) { +void guard(void *(*f)(void *), void *arg, void *const *const stack, void *const *const alloc, void **ret) { stack_p = *stack; alloc_p = *alloc; if (_register_handler() != guard_sound) { - guard_err = guard_weird; + err = guard_weird; goto fail; } - if (guard_p == NULL && (guard_err = _focus_guard()) != guard_sound) { + if (guard_p == NULL && (err = _focus_guard()) != guard_sound) { goto fail; } *ret = f(arg); - if (guard_err != guard_sound) { + if (err != guard_sound) { goto fail; } return; fail: - *ret = (void *) &guard_err; + *ret = (void *) &err; return; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index a2b368f9..7e4b848d 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -30,6 +30,15 @@ * error will be written to the `ret` pointer. The caller is then responsible * for handling this error and aborting with a `bail:meme`. */ -void guard(void *(*f)(void *), void *arg, void **stack, void **alloc, void **ret); +void guard(void *(*f)(void *), void *arg, void *const *const stack, void *const *const alloc, void **ret); + +typedef enum { + guard_sound = 0, // job's done + guard_armor = 1, // mprotect + guard_weird = 2, // strange state + guard_spent = 3, // out of memory (bail:meme) + guard_erupt = 4, // sigint +} guard_err; + #endif From 5e44f2f94f0784c45f4b4d5ac77a60839aac9689 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 22 Jan 2024 21:49:24 -0500 Subject: [PATCH 09/58] wip: debugging --- rust/ares/src/interpreter.rs | 9 ++++----- rust/ares_guard/c-src/guard.c | 27 ++++++++++++++++++--------- rust/ares_guard/c-src/guard.h | 2 +- rust/ares_guard/src/lib.rs | 20 +++++++++++++++++++- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index c12792e9..2d79cec7 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -375,11 +375,10 @@ extern "C" fn rust_callback(arg: *mut c_void) -> *mut c_void { pub fn call_with_guard Result>( f: F, - stack: *mut *mut c_void, - alloc: *mut *mut c_void, + stack: *const c_void, + alloc: *const c_void, ret: *mut *mut c_void, ) -> Result { - eprintln!("call_with_guard"); let boxed_f = Box::new(f); unsafe { @@ -988,8 +987,8 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }; } }, - stack_p as *mut *mut c_void, - alloc_p as *mut *mut c_void, + stack_p as *const c_void, + alloc_p as *const c_void, std::ptr::null_mut() as *mut *mut c_void, ) }) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index ffd2254b..39e025ae 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -21,12 +21,13 @@ volatile sig_atomic_t err = guard_sound; guard_err _focus_guard() { // Check for strange situations. if (stack_p == NULL || alloc_p == NULL) { + fprintf(stderr, "guard: stack or alloc pointer is null\r\n"); return guard_weird; } // Unmark the old guard page. void *old_guard_p = guard_p; - if (mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + if (old_guard_p != NULL && mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { return guard_armor; } @@ -56,6 +57,7 @@ guard_err _focus_guard() { guard_err _slash_guard(void *si_addr) { if (si_addr >= guard_p && si_addr < guard_p + GD_PAGESIZE) { + fprintf(stderr, "guard: slash\r\n"); return _focus_guard(); } @@ -65,11 +67,11 @@ guard_err _slash_guard(void *si_addr) { void _signal_handler(int sig, siginfo_t *si, void *unused) { switch (sig) { case SIGSEGV: - fprintf(stderr, "guard: caught sigsegv\r\n"); + fprintf(stderr, "guard: segfault at %p\r\n", si->si_addr); + fprintf(stderr, "guard: guard at %p\r\n", guard_p); err = _slash_guard(si->si_addr); break; case SIGINT: - fprintf(stderr, "guard: caught sigint\r\n"); err = guard_erupt; break; default: @@ -91,19 +93,25 @@ guard_err _register_handler() { return guard_sound; } -void guard(void *(*f)(void *), void *arg, void *const *const stack, void *const *const alloc, void **ret) { - stack_p = *stack; - alloc_p = *alloc; +void guard(void *(*f)(void *), void *arg, void *const stack, void *const alloc, void **ret) { + stack_p = stack; + alloc_p = alloc; if (_register_handler() != guard_sound) { err = guard_weird; goto fail; } - fprintf(stderr, "guard: installing guard page\r\n"); - if (guard_p == NULL && (err = _focus_guard()) != guard_sound) { - goto fail; + if (guard_p == NULL) { + fprintf(stderr, "guard: installing guard page\r\n"); + guard_err install_err = _focus_guard(); + if (install_err != guard_sound) { + fprintf(stderr, "guard: failed to install guard page\r\n"); + err = install_err; + goto fail; + } } + fprintf(stderr, "guard: guard page installed at %p\r\n", guard_p); *ret = f(arg); @@ -114,6 +122,7 @@ void guard(void *(*f)(void *), void *arg, void *const *const stack, void *const return; fail: + fprintf(stderr, "guard: error %d\r\n", err); *ret = (void *) &err; return; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 7e4b848d..3492e778 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -30,7 +30,7 @@ * error will be written to the `ret` pointer. The caller is then responsible * for handling this error and aborting with a `bail:meme`. */ -void guard(void *(*f)(void *), void *arg, void *const *const stack, void *const *const alloc, void **ret); +void guard(void *(*f)(void *), void *arg, void *const stack, void *const alloc, void **ret); typedef enum { guard_sound = 0, // job's done diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index a38a13a8..2698ade5 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -2,4 +2,22 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +extern "C" { + #[doc = " Execute the given closure `f` within the memory arena between the\n `stack` and `alloc` pointers, with guard page protection. Write either\n `f`'s succesful result or a `guard_err` to the given `ret` pointer.\n\n Memory\n ------\n The free memory arena between the `stack` and `alloc` pointers is part of a\n NockStack frame, which may either face east or west. If the frame faces\n east, the `stack` pointer will be greater than the `alloc` pointer. If it\n faces west, the `stack` pointer will be less than the `alloc` pointer.\n\n All the pages in the memory arena are marked clean (`PROT_READ | PROT_WRITE`)\n by default, with the exception of a single guard page in the middle of the\n arena, which is marked with `PROT_NONE`.\n\n Guard\n -----\n This function protects the free memory arena between the `stack` and `alloc`\n pointers with a guard page. A guard page is simply a single page of memory\n which is marked with `PROT_NONE`. Since all other pages are marked clean by\n default, a SIGSEGV will only be raised if the `f` function attempts to write\n to the guard page. When it does, the signal handler will attempt to re-center\n the guard page in the remaining free space left in the arena. If there is no\n more free space, then memory exhaustion has occurred and the `guard_spent`\n error will be written to the `ret` pointer. The caller is then responsible\n for handling this error and aborting with a `bail:meme`."] + pub fn guard( + f: ::std::option::Option< + unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void) -> *mut ::std::os::raw::c_void, + >, + arg: *mut ::std::os::raw::c_void, + stack: *const ::std::os::raw::c_void, + alloc: *const ::std::os::raw::c_void, + ret: *mut *mut ::std::os::raw::c_void, + ); +} +pub const guard_err_guard_sound: guard_err = 0; +pub const guard_err_guard_armor: guard_err = 1; +pub const guard_err_guard_weird: guard_err = 2; +pub const guard_err_guard_spent: guard_err = 3; +pub const guard_err_guard_erupt: guard_err = 4; +pub type guard_err = ::std::os::raw::c_uint; \ No newline at end of file From 7ff16576870d1c48ce88402f780047a29625d71f Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 23 Jan 2024 04:40:20 -0500 Subject: [PATCH 10/58] wip: earl-eye in the mornin --- rust/ares/src/interpreter.rs | 23 ++++++----- rust/ares_guard/c-src/guard.c | 78 +++++++++++++++++++++++++---------- rust/ares_guard/c-src/guard.h | 3 +- rust/ares_guard/src/lib.rs | 20 +-------- 4 files changed, 73 insertions(+), 51 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 2d79cec7..329af522 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -375,27 +375,28 @@ extern "C" fn rust_callback(arg: *mut c_void) -> *mut c_void { pub fn call_with_guard Result>( f: F, - stack: *const c_void, - alloc: *const c_void, - ret: *mut *mut c_void, + stack: *const *mut c_void, + alloc: *const *mut c_void, ) -> Result { let boxed_f = Box::new(f); + let res: Result = Err(Error::Deterministic(D(0))); + let mut result = Box::new(res); unsafe { - let result: *mut c_void = std::ptr::null_mut(); let raw_f = Box::into_raw(Box::new(boxed_f)); + let result_ptr = &mut result as *mut _ as *mut c_void; guard( Some(rust_callback), raw_f as *mut c_void, stack, alloc, - ret, + result_ptr, ); let _ = Box::from_raw(raw_f); - if !result.is_null() { + if !*result.is_null() { let err = *(result as *const guard_err); match err { 0 => { // sound @@ -419,6 +420,10 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res let snapshot = context.save(); let virtual_frame: *const u64 = context.stack.get_frame_pointer(); let mut res: Noun = D(0); + let stack_ptr = context.stack.get_stack_pointer() as *mut c_void; + let stack_ptr_ptr = &stack_ptr as *const *mut c_void; + let alloc_ptr = context.stack.get_alloc_pointer() as *mut c_void; + let alloc_ptr_ptr = &alloc_ptr as *const *mut c_void; // Setup stack for Nock computation unsafe { @@ -445,8 +450,6 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) let nock = assert_no_alloc(|| { ensure_alloc_counters(|| { - let stack_p = context.stack.get_stack_pointer(); - let alloc_p = context.stack.get_alloc_pointer(); call_with_guard(|| unsafe { push_formula(&mut context.stack, formula, true)?; @@ -987,8 +990,8 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }; } }, - stack_p as *const c_void, - alloc_p as *const c_void, + stack_ptr_ptr, + alloc_ptr_ptr, std::ptr::null_mut() as *mut *mut c_void, ) }) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 39e025ae..1704f142 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -1,8 +1,10 @@ +#include #include #include #include #include #include +#include #include "guard.h" @@ -10,24 +12,27 @@ #define GD_PAGEBITS 14ULL #define GD_PAGESIZE (1ULL << GD_PAGEBITS) /* 16K */ -static void *guard_p = NULL; -static void *stack_p = NULL; -static void *alloc_p = NULL; +static uint64_t *guard_p = 0; +static uint64_t **stack = 0; +static uint64_t **alloc = 0; volatile sig_atomic_t err = guard_sound; // Center the guard page. guard_err _focus_guard() { + uint64_t *stack_p = *stack; + uint64_t *alloc_p = *alloc; + // Check for strange situations. - if (stack_p == NULL || alloc_p == NULL) { + if (stack_p == 0 || alloc_p == 0) { fprintf(stderr, "guard: stack or alloc pointer is null\r\n"); return guard_weird; } // Unmark the old guard page. void *old_guard_p = guard_p; - if (old_guard_p != NULL && mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + if (old_guard_p != 0 && mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { return guard_armor; } @@ -56,8 +61,10 @@ guard_err _focus_guard() { } guard_err _slash_guard(void *si_addr) { - if (si_addr >= guard_p && si_addr < guard_p + GD_PAGESIZE) { - fprintf(stderr, "guard: slash\r\n"); + // fprintf(stderr, "guard: slash at %p\r\n", si_addr); + // fprintf(stderr, "guard: guard at %p\r\n", (void *) guard_p); + + if (si_addr >= (void *)guard_p && si_addr < (void *)guard_p + GD_PAGESIZE) { return _focus_guard(); } @@ -67,8 +74,6 @@ guard_err _slash_guard(void *si_addr) { void _signal_handler(int sig, siginfo_t *si, void *unused) { switch (sig) { case SIGSEGV: - fprintf(stderr, "guard: segfault at %p\r\n", si->si_addr); - fprintf(stderr, "guard: guard at %p\r\n", guard_p); err = _slash_guard(si->si_addr); break; case SIGINT: @@ -86,23 +91,24 @@ guard_err _register_handler() { sa.sa_sigaction = _signal_handler; sa.sa_mask = 0; - if (sigaction(SIGSEGV, &sa, NULL) || sigaction(SIGINT, &sa, NULL)) { + if (sigaction(SIGSEGV, &sa, 0) || sigaction(SIGINT, &sa, 0)) { return guard_weird; } return guard_sound; } -void guard(void *(*f)(void *), void *arg, void *const stack, void *const alloc, void **ret) { - stack_p = stack; - alloc_p = alloc; +void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *alloc_pp, void *ret) { + stack = (uint64_t**) stack_pp; + alloc = (uint64_t**) alloc_pp; - if (_register_handler() != guard_sound) { - err = guard_weird; - goto fail; - } + fprintf(stderr, "guard: stack at %p\r\n", (void *) stack); + fprintf(stderr, "guard: alloc at %p\r\n", (void *) alloc); - if (guard_p == NULL) { + fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack); + fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc); + + if (guard_p == 0) { fprintf(stderr, "guard: installing guard page\r\n"); guard_err install_err = _focus_guard(); if (install_err != guard_sound) { @@ -111,18 +117,48 @@ void guard(void *(*f)(void *), void *arg, void *const stack, void *const alloc, goto fail; } } - fprintf(stderr, "guard: guard page installed at %p\r\n", guard_p); + fprintf(stderr, "guard: guard page installed at %p\r\n", (void *) guard_p); + + if (_register_handler() != guard_sound) { + err = guard_weird; + goto fail; + } + + pthread_t thread; + int thread_err; + thread_err = pthread_create(&thread, NULL, f, arg); + if (thread_err != 0) { + err = guard_weird; + goto fail; + } + + while (err == guard_sound) { + if (pthread_kill(thread, 0) != 0) { + goto fail; + } + sleep(1); + } - *ret = f(arg); + void *thread_result; + pthread_join(thread, &thread_result); + *(void **)ret = thread_result; if (err != guard_sound) { goto fail; } + if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + err = guard_armor; + goto fail; + } + return; fail: + if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + fprintf(stderr, "guard: failed to remove guard page\r\n"); + } fprintf(stderr, "guard: error %d\r\n", err); - *ret = (void *) &err; + *(void **)ret = (void *) &err; return; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 3492e778..94ea94ee 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -1,6 +1,7 @@ #ifndef __GUARD_H__ #define __GUARD_H__ +#include /** * Execute the given closure `f` within the memory arena between the @@ -30,7 +31,7 @@ * error will be written to the `ret` pointer. The caller is then responsible * for handling this error and aborting with a `bail:meme`. */ -void guard(void *(*f)(void *), void *arg, void *const stack, void *const alloc, void **ret); +void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *alloc_pp, void *ret); typedef enum { guard_sound = 0, // job's done diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index 2698ade5..a38a13a8 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -2,22 +2,4 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] - -extern "C" { - #[doc = " Execute the given closure `f` within the memory arena between the\n `stack` and `alloc` pointers, with guard page protection. Write either\n `f`'s succesful result or a `guard_err` to the given `ret` pointer.\n\n Memory\n ------\n The free memory arena between the `stack` and `alloc` pointers is part of a\n NockStack frame, which may either face east or west. If the frame faces\n east, the `stack` pointer will be greater than the `alloc` pointer. If it\n faces west, the `stack` pointer will be less than the `alloc` pointer.\n\n All the pages in the memory arena are marked clean (`PROT_READ | PROT_WRITE`)\n by default, with the exception of a single guard page in the middle of the\n arena, which is marked with `PROT_NONE`.\n\n Guard\n -----\n This function protects the free memory arena between the `stack` and `alloc`\n pointers with a guard page. A guard page is simply a single page of memory\n which is marked with `PROT_NONE`. Since all other pages are marked clean by\n default, a SIGSEGV will only be raised if the `f` function attempts to write\n to the guard page. When it does, the signal handler will attempt to re-center\n the guard page in the remaining free space left in the arena. If there is no\n more free space, then memory exhaustion has occurred and the `guard_spent`\n error will be written to the `ret` pointer. The caller is then responsible\n for handling this error and aborting with a `bail:meme`."] - pub fn guard( - f: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void) -> *mut ::std::os::raw::c_void, - >, - arg: *mut ::std::os::raw::c_void, - stack: *const ::std::os::raw::c_void, - alloc: *const ::std::os::raw::c_void, - ret: *mut *mut ::std::os::raw::c_void, - ); -} -pub const guard_err_guard_sound: guard_err = 0; -pub const guard_err_guard_armor: guard_err = 1; -pub const guard_err_guard_weird: guard_err = 2; -pub const guard_err_guard_spent: guard_err = 3; -pub const guard_err_guard_erupt: guard_err = 4; -pub type guard_err = ::std::os::raw::c_uint; \ No newline at end of file +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); From 95d4023d7267a9a4c0e60de751d05f3425a31dda Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 23 Jan 2024 14:22:03 -0500 Subject: [PATCH 11/58] guard: kind of works --- rust/ares/src/interpreter.rs | 133 ++++++++++++++++++++++++---------- rust/ares_guard/c-src/guard.c | 85 ++++++++++++++-------- rust/ares_guard/c-src/guard.h | 25 ++++--- rust/ares_guard/src/lib.rs | 6 ++ 4 files changed, 170 insertions(+), 79 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 329af522..21ed0835 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -15,11 +15,12 @@ use crate::noun::{Atom, Cell, IndirectAtom, Noun, Slots, D, T}; use crate::serf::TERMINATOR; use crate::trace::{write_nock_trace, TraceInfo, TraceStack}; use crate::unifying_equality::unifying_equality; -use ares_guard::{guard, guard_err}; +use ares_guard::*; use ares_macros::tas; use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters}; use bitvec::prelude::{BitSlice, Lsb0}; -use either::Either::*; +use either::*; +use std::convert::TryFrom; use std::ffi::c_void; use std::result; use std::sync::atomic::Ordering; @@ -319,11 +320,25 @@ pub enum Error { } pub enum GuardError { - GuardSound = 0, - GuardArmor = 1, - GuardWeird = 2, - GuardSpent = 3, - GuardErupt = 4, + GuardSound = GUARD_SOUND as isize, + GuardArmor = GUARD_ARMOR as isize, + GuardWeird = GUARD_WEIRD as isize, + GuardSpent = GUARD_SPENT as isize, + GuardErupt = GUARD_ERUPT as isize, +} + +impl TryFrom for GuardError { + type Error = (); + fn try_from(value: u32) -> std::result::Result { + match value { + GUARD_SOUND => Ok(GuardError::GuardSound), + GUARD_ARMOR => Ok(GuardError::GuardArmor), + GUARD_WEIRD => Ok(GuardError::GuardWeird), + GUARD_SPENT => Ok(GuardError::GuardSpent), + GUARD_ERUPT => Ok(GuardError::GuardErupt), + _ => Err(()), + } + } } impl Preserve for Error { @@ -367,48 +382,89 @@ fn debug_assertions(stack: &mut NockStack, noun: Noun) { assert_no_junior_pointers!(stack, noun); } -extern "C" fn rust_callback(arg: *mut c_void) -> *mut c_void { - let closure: &mut Box *mut c_void> = - unsafe { &mut *(arg as *mut Box *mut c_void>) }; - closure() +use std::marker::PhantomData; + +pub struct CCallback<'closure> { + pub function: unsafe extern "C" fn(*mut c_void) -> *mut c_void, + pub user_data: *mut c_void, + + // without this it's too easy to accidentally drop the closure too soon + _lifetime: PhantomData<&'closure mut c_void>, +} + +impl<'closure> 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, + user_data: closure as *mut F as *mut c_void, + _lifetime: PhantomData, + } + } + + unsafe extern "C" fn call_closure(user_data: *mut c_void) -> *mut c_void where F: FnMut() -> Result { + let cb: &mut F = user_data.cast::().as_mut().unwrap(); + let mut v = (*cb)(); + let v_ptr = &mut v as *mut _ as *mut c_void; + v_ptr + } } +// fn main() { +// let mut v = Vec::new(); + +// // must assign to a variable to ensure it lives until the end of scope +// let closure = &mut |x: i32| { v.push(x) }; +// let c = CCallback::new(closure); + +// unsafe { (c.function)(c.user_data, 123) }; + +// assert_eq!(v, [123]); +// } + pub fn call_with_guard Result>( - f: F, + f: &mut F, stack: *const *mut c_void, alloc: *const *mut c_void, ) -> Result { - let boxed_f = Box::new(f); - let res: Result = Err(Error::Deterministic(D(0))); - let mut result = Box::new(res); + let c = CCallback::new(f); + let mut result: Result = Ok(D(0)); + let result_ptr = &mut result as *mut _ as *mut c_void; unsafe { - let raw_f = Box::into_raw(Box::new(boxed_f)); - let result_ptr = &mut result as *mut _ as *mut c_void; - guard( - Some(rust_callback), - raw_f as *mut c_void, + let err = guard( + Some(c.function as unsafe extern "C" fn(*mut c_void) -> *mut c_void), + c.user_data as *mut c_void, stack, alloc, result_ptr, ); - let _ = Box::from_raw(raw_f); - - if !*result.is_null() { - let err = *(result as *const guard_err); + if let Ok(err) = GuardError::try_from(err) { match err { - 0 => { // sound - let res = result as *mut Result; - *res - }, - // TODO: handle other errors explicitly. - _ => Err(Error::Deterministic(D(0))), + GuardError::GuardSound => { + return result; + } + GuardError::GuardArmor => { + return Err(Error::Deterministic(D(0))); + } + GuardError::GuardWeird => { + return Err(Error::Deterministic(D(0))); + } + GuardError::GuardSpent => { + return Err(Error::NonDeterministic(D(0))); + } + GuardError::GuardErupt => { + return Err(Error::NonDeterministic(D(0))); + } } - } - else { - (Box::from_raw(raw_f))() + } else { + return Err(Error::Deterministic(D(0))); } } } @@ -450,8 +506,10 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) let nock = assert_no_alloc(|| { ensure_alloc_counters(|| { - call_with_guard(|| unsafe { + let work_closure = &mut || unsafe { + eprint!("ares: entered closure\r\n"); push_formula(&mut context.stack, formula, true)?; + eprint!("ares: pushed formula\r\n"); loop { let work: NockWork = *context.stack.top(); @@ -989,11 +1047,8 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }, }; } - }, - stack_ptr_ptr, - alloc_ptr_ptr, - std::ptr::null_mut() as *mut *mut c_void, - ) + }; + call_with_guard(work_closure, stack_ptr_ptr, alloc_ptr_ptr) }) }); diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 1704f142..60ee7475 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -15,12 +15,13 @@ static uint64_t *guard_p = 0; static uint64_t **stack = 0; static uint64_t **alloc = 0; +static jmp_buf env_buffer; volatile sig_atomic_t err = guard_sound; - // Center the guard page. -guard_err _focus_guard() { +guard_err _focus_guard() +{ uint64_t *stack_p = *stack; uint64_t *alloc_p = *alloc; @@ -61,8 +62,8 @@ guard_err _focus_guard() { } guard_err _slash_guard(void *si_addr) { - // fprintf(stderr, "guard: slash at %p\r\n", si_addr); - // fprintf(stderr, "guard: guard at %p\r\n", (void *) guard_p); + fprintf(stderr, "guard: slash at %p\r\n", si_addr); + fprintf(stderr, "guard: guard at %p\r\n", (void *) guard_p); if (si_addr >= (void *)guard_p && si_addr < (void *)guard_p + GD_PAGESIZE) { return _focus_guard(); @@ -71,17 +72,24 @@ guard_err _slash_guard(void *si_addr) { return guard_weird; } -void _signal_handler(int sig, siginfo_t *si, void *unused) { +void _signal_handler(int sig, siginfo_t *si, void *unused) +{ switch (sig) { case SIGSEGV: + fprintf(stderr, "guard: sigsegv\r\n"); err = _slash_guard(si->si_addr); break; case SIGINT: + fprintf(stderr, "guard: sigint\r\n"); err = guard_erupt; break; default: break; } + + if (err != guard_sound) { + longjmp(env_buffer, 1); + } } guard_err _register_handler() { @@ -95,18 +103,27 @@ guard_err _register_handler() { return guard_weird; } + fprintf(stderr, "guard: registered handler\r\n"); return guard_sound; } -void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *alloc_pp, void *ret) { +guard_err guard( + void *(*f)(void *), + void *user_data, + void *const *stack_pp, + void *const *alloc_pp, + void *ret +) +{ stack = (uint64_t**) stack_pp; alloc = (uint64_t**) alloc_pp; + fprintf(stderr, "guard: f pointer at %p\r\n", (void *) f); fprintf(stderr, "guard: stack at %p\r\n", (void *) stack); fprintf(stderr, "guard: alloc at %p\r\n", (void *) alloc); - fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack); fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc); + fprintf(stderr, "guard: ret pointer at %p\r\n", (void *) ret); if (guard_p == 0) { fprintf(stderr, "guard: installing guard page\r\n"); @@ -124,27 +141,21 @@ void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *al goto fail; } - pthread_t thread; - int thread_err; - thread_err = pthread_create(&thread, NULL, f, arg); - if (thread_err != 0) { - err = guard_weird; - goto fail; + void *result; + if (setjmp(env_buffer) == 0) { + fprintf(stderr, "guard: calling f\r\n"); + result = f(user_data); } - - while (err == guard_sound) { - if (pthread_kill(thread, 0) != 0) { + else { + fprintf(stderr, "guard: jump buffer already set\r\n"); + if (err != guard_sound) { + fprintf(stderr, "guard: not sound\r\n"); goto fail; } - sleep(1); - } - - void *thread_result; - pthread_join(thread, &thread_result); - *(void **)ret = thread_result; - - if (err != guard_sound) { - goto fail; + else { + fprintf(stderr, "guard: assigning ret\r\n"); + *(void **)ret = result; + } } if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { @@ -152,13 +163,25 @@ void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *al goto fail; } - return; + return guard_sound; fail: if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { - fprintf(stderr, "guard: failed to remove guard page\r\n"); + fprintf(stderr, "guard: failed to uninstall guard page\r\n"); + } + switch (err) { + case guard_armor: + fprintf(stderr, "guard: armor error\r\n"); + break; + case guard_weird: + fprintf(stderr, "guard: weird error\r\n"); + break; + case guard_spent: + fprintf(stderr, "guard: spent error\r\n"); + break; + case guard_erupt: + fprintf(stderr, "guard: erupt error\r\n"); + break; } - fprintf(stderr, "guard: error %d\r\n", err); - *(void **)ret = (void *) &err; - return; + return err; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 94ea94ee..3b03231a 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -3,6 +3,15 @@ #include + +typedef enum { + guard_sound = 0, // job's done + guard_armor = 1, // mprotect + guard_weird = 2, // strange state + guard_spent = 3, // out of memory (bail:meme) + guard_erupt = 4, // sigint +} guard_err; + /** * Execute the given closure `f` within the memory arena between the * `stack` and `alloc` pointers, with guard page protection. Write either @@ -31,15 +40,13 @@ * error will be written to the `ret` pointer. The caller is then responsible * for handling this error and aborting with a `bail:meme`. */ -void guard(void *(*f)(void *), void *arg, void *const *stack_pp, void *const *alloc_pp, void *ret); - -typedef enum { - guard_sound = 0, // job's done - guard_armor = 1, // mprotect - guard_weird = 2, // strange state - guard_spent = 3, // out of memory (bail:meme) - guard_erupt = 4, // sigint -} guard_err; +guard_err guard( + void *(*f)(void *), + void *user_data, + void *const *stack_pp, + void *const *alloc_pp, + void *ret +); #endif diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index a38a13a8..14763230 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -3,3 +3,9 @@ #![allow(non_snake_case)] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +pub const GUARD_SOUND: u32 = guard_err_guard_sound; +pub const GUARD_ARMOR: u32 = guard_err_guard_armor; +pub const GUARD_WEIRD: u32 = guard_err_guard_weird; +pub const GUARD_SPENT: u32 = guard_err_guard_spent; +pub const GUARD_ERUPT: u32 = guard_err_guard_erupt; From a8ed832f6b9c0da0fa6d4ffb8b4ae5e3ba4a401f Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 23 Jan 2024 14:25:03 -0500 Subject: [PATCH 12/58] wip: restore cargo and remove eprints --- rust/ares/Cargo.toml | 4 ++-- rust/ares/src/interpreter.rs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 724ad997..ba1a0598 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 = "../rust-assert-no-alloc", features=["warn_debug"] } -# assert_no_alloc = { path = "../rust-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 21ed0835..71f8f837 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -507,9 +507,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res let nock = assert_no_alloc(|| { ensure_alloc_counters(|| { let work_closure = &mut || unsafe { - eprint!("ares: entered closure\r\n"); push_formula(&mut context.stack, formula, true)?; - eprint!("ares: pushed formula\r\n"); loop { let work: NockWork = *context.stack.top(); From 4ff53a1845679e892a17a5f13114fa4f19d5d50d Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 23 Jan 2024 15:31:36 -0500 Subject: [PATCH 13/58] wip: need more work on ret pointer --- rust/ares/src/interpreter.rs | 79 +++++++++++++++++++++++------------ rust/ares_guard/c-src/guard.c | 15 +++---- rust/ares_guard/c-src/guard.h | 2 +- 3 files changed, 62 insertions(+), 34 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 71f8f837..c605de03 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -393,11 +393,20 @@ pub struct CCallback<'closure> { } impl<'closure> CCallback<'closure> { - pub fn new(closure: &'closure mut F) -> Self where F: FnMut() -> Result { + 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>()); + 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, @@ -406,10 +415,15 @@ impl<'closure> CCallback<'closure> { } } - unsafe extern "C" fn call_closure(user_data: *mut c_void) -> *mut c_void where F: FnMut() -> Result { + unsafe extern "C" fn call_closure(user_data: *mut c_void) -> *mut c_void + where + F: FnMut() -> Result, + { let cb: &mut F = user_data.cast::().as_mut().unwrap(); let mut v = (*cb)(); + eprint!("ares: v: {:?}\r\n", v); let v_ptr = &mut v as *mut _ as *mut c_void; + eprint!("ares: v_ptr: {:p}\r\n", v_ptr); v_ptr } } @@ -422,7 +436,7 @@ impl<'closure> CCallback<'closure> { // let c = CCallback::new(closure); // unsafe { (c.function)(c.user_data, 123) }; - + // assert_eq!(v, [123]); // } @@ -434,20 +448,25 @@ pub fn call_with_guard Result>( let c = CCallback::new(f); let mut result: Result = Ok(D(0)); let result_ptr = &mut result as *mut _ as *mut c_void; + let result_ptr_ptr = result_ptr as *mut *mut c_void; - unsafe { + eprint!("ares: result_ptr_ptr: {:p}\r\n", result_ptr_ptr); + unsafe { let err = guard( Some(c.function as unsafe extern "C" fn(*mut c_void) -> *mut c_void), c.user_data as *mut c_void, stack, alloc, - result_ptr, + result_ptr_ptr, ); + eprint!("ares: result_ptr: {:p}\r\n", result_ptr); + if let Ok(err) = GuardError::try_from(err) { match err { GuardError::GuardSound => { + eprint!("ares: result: {:?}\n", result); return result; } GuardError::GuardArmor => { @@ -932,9 +951,9 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }, NockWork::Work11S(mut sint) => match sint.todo { Todo11S::ComputeResult => { - if let Some(ret) = - hint::match_pre_nock(context, subject, sint.tag, None, sint.body) - { + if let Some(ret) = hint::match_pre_nock( + context, subject, sint.tag, None, sint.body, + ) { match ret { Ok(found) => { res = found; @@ -1008,25 +1027,32 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res 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::(); + 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) => { + Error::Deterministic(trace) + | Error::ScryCrashed(trace) => { break Err(Error::ScryCrashed(trace)); } Error::NonDeterministic(_) => { @@ -1050,6 +1076,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }) }); + eprint!("nock: {:?}", nock); match nock { Ok(res) => Ok(res), Err(err) => Err(exit(context, &snapshot, virtual_frame, err)), diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 60ee7475..faa09f33 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -112,7 +112,7 @@ guard_err guard( void *user_data, void *const *stack_pp, void *const *alloc_pp, - void *ret + void **ret ) { stack = (uint64_t**) stack_pp; @@ -124,6 +124,7 @@ guard_err guard( fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack); fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc); fprintf(stderr, "guard: ret pointer at %p\r\n", (void *) ret); + fprintf(stderr, "guard: res pointer at %p\r\n", (void *) *ret); if (guard_p == 0) { fprintf(stderr, "guard: installing guard page\r\n"); @@ -147,21 +148,20 @@ guard_err guard( result = f(user_data); } else { - fprintf(stderr, "guard: jump buffer already set\r\n"); + fprintf(stderr, "guard: longjmp\r\n"); if (err != guard_sound) { - fprintf(stderr, "guard: not sound\r\n"); goto fail; } - else { - fprintf(stderr, "guard: assigning ret\r\n"); - *(void **)ret = result; - } } + fprintf(stderr, "guard: assigning ret to %p\r\n", result); + *ret = result; + if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { err = guard_armor; goto fail; } + fprintf(stderr, "guard: uninstalled guard page\r\n"); return guard_sound; @@ -169,6 +169,7 @@ guard_err guard( if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { fprintf(stderr, "guard: failed to uninstall guard page\r\n"); } + fprintf(stderr, "guard: uninstalled guard page\r\n"); switch (err) { case guard_armor: fprintf(stderr, "guard: armor error\r\n"); diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 3b03231a..10a40ab6 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -45,7 +45,7 @@ guard_err guard( void *user_data, void *const *stack_pp, void *const *alloc_pp, - void *ret + void **ret ); From 140d02cd5516f5c430dc484a262b26bb90ca44a9 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 23 Jan 2024 16:03:33 -0500 Subject: [PATCH 14/58] wip: got result value being passed correctly --- rust/ares/src/interpreter.rs | 19 +++++++++---------- rust/ares_guard/c-src/guard.c | 8 ++++---- rust/ares_guard/c-src/guard.h | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index c605de03..c21dbce1 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -421,9 +421,9 @@ impl<'closure> CCallback<'closure> { { let cb: &mut F = user_data.cast::().as_mut().unwrap(); let mut v = (*cb)(); - eprint!("ares: v: {:?}\r\n", v); + eprint!("call_closure: v: {:?}\r\n", v); let v_ptr = &mut v as *mut _ as *mut c_void; - eprint!("ares: v_ptr: {:p}\r\n", v_ptr); + eprint!("call_closure: v_ptr: {:p}\r\n", v_ptr); v_ptr } } @@ -447,10 +447,9 @@ pub fn call_with_guard Result>( ) -> Result { let c = CCallback::new(f); let mut result: Result = Ok(D(0)); - let result_ptr = &mut result as *mut _ as *mut c_void; - let result_ptr_ptr = result_ptr as *mut *mut c_void; - - eprint!("ares: result_ptr_ptr: {:p}\r\n", result_ptr_ptr); + let res_ptr = &mut result as *mut _ as *mut c_void; + let res_ptr_ptr = &res_ptr as *const *mut c_void; + eprint!("call_with_guard: before res_ptr: {:p}\r\n", res_ptr); unsafe { let err = guard( @@ -458,15 +457,16 @@ pub fn call_with_guard Result>( c.user_data as *mut c_void, stack, alloc, - result_ptr_ptr, + res_ptr_ptr, ); - eprint!("ares: result_ptr: {:p}\r\n", result_ptr); + eprint!("call_with_guard: after res_ptr: {:p}\r\n", res_ptr); if let Ok(err) = GuardError::try_from(err) { match err { GuardError::GuardSound => { - eprint!("ares: result: {:?}\n", result); + let result = *(res_ptr as *mut Result); + eprint!("call_with_guard: result: {:?}\r\n", result); return result; } GuardError::GuardArmor => { @@ -1076,7 +1076,6 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }) }); - eprint!("nock: {:?}", nock); match nock { Ok(res) => Ok(res), Err(err) => Err(exit(context, &snapshot, virtual_frame, err)), diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index faa09f33..1a9cd2f1 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -112,7 +112,7 @@ guard_err guard( void *user_data, void *const *stack_pp, void *const *alloc_pp, - void **ret + void *const *ret ) { stack = (uint64_t**) stack_pp; @@ -124,7 +124,6 @@ guard_err guard( fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack); fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc); fprintf(stderr, "guard: ret pointer at %p\r\n", (void *) ret); - fprintf(stderr, "guard: res pointer at %p\r\n", (void *) *ret); if (guard_p == 0) { fprintf(stderr, "guard: installing guard page\r\n"); @@ -154,8 +153,9 @@ guard_err guard( } } - fprintf(stderr, "guard: assigning ret to %p\r\n", result); - *ret = result; + fprintf(stderr, "guard: assigning *ret to %p\r\n", result); + *(void **)ret = result; + fprintf(stderr, "guard: assigned *ret to %p\r\n", *ret); if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { err = guard_armor; diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 10a40ab6..e68e5d12 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -45,7 +45,7 @@ guard_err guard( void *user_data, void *const *stack_pp, void *const *alloc_pp, - void **ret + void *const *ret ); From 0b863326596037b84b8468d7affdca69b055f71c Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 23 Jan 2024 16:10:27 -0500 Subject: [PATCH 15/58] wip: guard weird error --- rust/ares/src/interpreter.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index c21dbce1..d2b5a657 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -421,9 +421,9 @@ impl<'closure> CCallback<'closure> { { let cb: &mut F = user_data.cast::().as_mut().unwrap(); let mut v = (*cb)(); - eprint!("call_closure: v: {:?}\r\n", v); + // eprint!("call_closure: v: {:?}\r\n", v); let v_ptr = &mut v as *mut _ as *mut c_void; - eprint!("call_closure: v_ptr: {:p}\r\n", v_ptr); + // eprint!("call_closure: v_ptr: {:p}\r\n", v_ptr); v_ptr } } @@ -449,7 +449,7 @@ pub fn call_with_guard Result>( let mut result: Result = Ok(D(0)); let res_ptr = &mut result as *mut _ as *mut c_void; let res_ptr_ptr = &res_ptr as *const *mut c_void; - eprint!("call_with_guard: before res_ptr: {:p}\r\n", res_ptr); + // eprint!("call_with_guard: before res_ptr: {:p}\r\n", res_ptr); unsafe { let err = guard( @@ -460,13 +460,13 @@ pub fn call_with_guard Result>( res_ptr_ptr, ); - eprint!("call_with_guard: after res_ptr: {:p}\r\n", res_ptr); + // eprint!("call_with_guard: after res_ptr: {:p}\r\n", res_ptr); if let Ok(err) = GuardError::try_from(err) { match err { GuardError::GuardSound => { let result = *(res_ptr as *mut Result); - eprint!("call_with_guard: result: {:?}\r\n", result); + // eprint!("call_with_guard: result: {:?}\r\n", result); return result; } GuardError::GuardArmor => { From a28137d47257ca4157c077e973d9b796735303b4 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 24 Jan 2024 10:18:02 -0500 Subject: [PATCH 16/58] wip: cleanup `guard.c` printfs --- rust/ares_guard/c-src/guard.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 1a9cd2f1..40f7f994 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -58,14 +58,14 @@ guard_err _focus_guard() return guard_spent; } + fprintf(stderr, "guard: installed guard page at %p\r\n", (void *) guard_p); + return guard_sound; } guard_err _slash_guard(void *si_addr) { - fprintf(stderr, "guard: slash at %p\r\n", si_addr); - fprintf(stderr, "guard: guard at %p\r\n", (void *) guard_p); - if (si_addr >= (void *)guard_p && si_addr < (void *)guard_p + GD_PAGESIZE) { + fprintf(stderr, "guard: slash in guard\r\n"); return _focus_guard(); } @@ -103,7 +103,7 @@ guard_err _register_handler() { return guard_weird; } - fprintf(stderr, "guard: registered handler\r\n"); + // fprintf(stderr, "guard: registered handler\r\n"); return guard_sound; } @@ -118,15 +118,10 @@ guard_err guard( stack = (uint64_t**) stack_pp; alloc = (uint64_t**) alloc_pp; - fprintf(stderr, "guard: f pointer at %p\r\n", (void *) f); - fprintf(stderr, "guard: stack at %p\r\n", (void *) stack); - fprintf(stderr, "guard: alloc at %p\r\n", (void *) alloc); fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack); fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc); - fprintf(stderr, "guard: ret pointer at %p\r\n", (void *) ret); if (guard_p == 0) { - fprintf(stderr, "guard: installing guard page\r\n"); guard_err install_err = _focus_guard(); if (install_err != guard_sound) { fprintf(stderr, "guard: failed to install guard page\r\n"); @@ -153,15 +148,13 @@ guard_err guard( } } - fprintf(stderr, "guard: assigning *ret to %p\r\n", result); *(void **)ret = result; - fprintf(stderr, "guard: assigned *ret to %p\r\n", *ret); if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { err = guard_armor; goto fail; } - fprintf(stderr, "guard: uninstalled guard page\r\n"); + fprintf(stderr, "guard: sound; uninstalled guard page\r\n"); return guard_sound; @@ -169,7 +162,7 @@ guard_err guard( if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { fprintf(stderr, "guard: failed to uninstall guard page\r\n"); } - fprintf(stderr, "guard: uninstalled guard page\r\n"); + fprintf(stderr, "guard: fail; uninstalled guard page\r\n"); switch (err) { case guard_armor: fprintf(stderr, "guard: armor error\r\n"); From a0ddc1f89ca59e8a69744e706d71091898ad9764 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 24 Jan 2024 13:24:48 -0500 Subject: [PATCH 17/58] guard: boots a baby pill and sigint works too; uses box --- rust/ares/src/interpreter.rs | 41 ++++++++++++++--------------------- rust/ares_guard/c-src/guard.c | 24 ++++++++++---------- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index d2b5a657..8aadfe66 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -17,6 +17,7 @@ use crate::trace::{write_nock_trace, TraceInfo, TraceStack}; use crate::unifying_equality::unifying_equality; use ares_guard::*; use ares_macros::tas; +use assert_no_alloc::permit_alloc; use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters}; use bitvec::prelude::{BitSlice, Lsb0}; use either::*; @@ -420,36 +421,27 @@ impl<'closure> CCallback<'closure> { F: FnMut() -> Result, { let cb: &mut F = user_data.cast::().as_mut().unwrap(); - let mut v = (*cb)(); - // eprint!("call_closure: v: {:?}\r\n", v); - let v_ptr = &mut v as *mut _ as *mut c_void; - // eprint!("call_closure: v_ptr: {:p}\r\n", v_ptr); - v_ptr + let v = (*cb)(); + permit_alloc(|| { + // eprint!("call_closure: v: {:?}\r\n", v); + let v_box = Box::new(v); + let v_ptr = Box::into_raw(v_box); + // let v_ptr = &mut v as *mut _ as *mut c_void; + // eprint!("call_closure: v_ptr: {:p}\r\n", v_ptr); + v_ptr as *mut c_void + }) } } -// fn main() { -// let mut v = Vec::new(); - -// // must assign to a variable to ensure it lives until the end of scope -// let closure = &mut |x: i32| { v.push(x) }; -// let c = CCallback::new(closure); - -// unsafe { (c.function)(c.user_data, 123) }; - -// assert_eq!(v, [123]); -// } - pub fn call_with_guard Result>( f: &mut F, stack: *const *mut c_void, alloc: *const *mut c_void, ) -> Result { let c = CCallback::new(f); - let mut result: Result = Ok(D(0)); - let res_ptr = &mut result as *mut _ as *mut c_void; - let res_ptr_ptr = &res_ptr as *const *mut c_void; - // eprint!("call_with_guard: before res_ptr: {:p}\r\n", res_ptr); + let mut ret: Result = Ok(D(0)); + let ret_p = &mut ret as *mut _ as *mut c_void; + let ret_pp = &ret_p as *const *mut c_void; unsafe { let err = guard( @@ -457,15 +449,14 @@ pub fn call_with_guard Result>( c.user_data as *mut c_void, stack, alloc, - res_ptr_ptr, + ret_pp, ); - // eprint!("call_with_guard: after res_ptr: {:p}\r\n", res_ptr); - if let Ok(err) = GuardError::try_from(err) { match err { GuardError::GuardSound => { - let result = *(res_ptr as *mut Result); + let result = *(ret_p as *mut Result); + // eprint!("call_with_guard: ret_p: {:p}\r\n", ret_p); // eprint!("call_with_guard: result: {:?}\r\n", result); return result; } diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 40f7f994..0357d5a5 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -33,7 +33,8 @@ guard_err _focus_guard() // Unmark the old guard page. void *old_guard_p = guard_p; - if (old_guard_p != 0 && mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + if (old_guard_p != 0 + && mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { return guard_armor; } @@ -45,6 +46,7 @@ guard_err _focus_guard() guard_p = stack_p + ((alloc_p - stack_p) / 2); } else { + fprintf(stderr, "guard: weird; stack and alloc pointers are equal\r\n"); return guard_weird; } guard_p = (void *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1)); @@ -76,7 +78,7 @@ void _signal_handler(int sig, siginfo_t *si, void *unused) { switch (sig) { case SIGSEGV: - fprintf(stderr, "guard: sigsegv\r\n"); + fprintf(stderr, "guard: sigsegv at %p\r\n", si->si_addr); err = _slash_guard(si->si_addr); break; case SIGINT: @@ -88,6 +90,7 @@ void _signal_handler(int sig, siginfo_t *si, void *unused) } if (err != guard_sound) { + fprintf(stderr, "guard: error %d; long jumping\r\n", err); longjmp(env_buffer, 1); } } @@ -118,8 +121,8 @@ guard_err guard( stack = (uint64_t**) stack_pp; alloc = (uint64_t**) alloc_pp; - fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack); - fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc); + // fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack); + // fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc); if (guard_p == 0) { guard_err install_err = _focus_guard(); @@ -129,7 +132,6 @@ guard_err guard( goto fail; } } - fprintf(stderr, "guard: guard page installed at %p\r\n", (void *) guard_p); if (_register_handler() != guard_sound) { err = guard_weird; @@ -138,11 +140,9 @@ guard_err guard( void *result; if (setjmp(env_buffer) == 0) { - fprintf(stderr, "guard: calling f\r\n"); result = f(user_data); } else { - fprintf(stderr, "guard: longjmp\r\n"); if (err != guard_sound) { goto fail; } @@ -150,11 +150,11 @@ guard_err guard( *(void **)ret = result; - if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { - err = guard_armor; - goto fail; - } - fprintf(stderr, "guard: sound; uninstalled guard page\r\n"); + // if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + // err = guard_armor; + // goto fail; + // } + // fprintf(stderr, "guard: sound; uninstalled guard page\r\n"); return guard_sound; From fa316f664b2b25db836d73bfaeb8de88a0635bd4 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 24 Jan 2024 15:30:19 -0500 Subject: [PATCH 18/58] guard: add motes and use in all errors; disable sigint handling --- rust/ares/src/interpreter.rs | 140 ++++++++++++++++++--------------- rust/ares/src/jets.rs | 28 ++++--- rust/ares/src/jets/bits.rs | 6 +- rust/ares/src/jets/list.rs | 15 ++-- rust/ares/src/jets/lock/aes.rs | 4 +- rust/ares/src/jets/lock/ed.rs | 27 +++---- rust/ares/src/jets/lock/sha.rs | 69 ++++------------ rust/ares/src/jets/math.rs | 58 ++++---------- rust/ares/src/jets/nock.rs | 10 +-- rust/ares/src/jets/tree.rs | 36 +++------ rust/ares/src/serf.rs | 14 ++-- rust/ares_guard/c-src/guard.c | 31 ++++---- 12 files changed, 185 insertions(+), 253 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 8aadfe66..826f3827 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -312,12 +312,20 @@ impl Context { } } +#[derive(Clone, Copy, Debug)] +pub enum Mote { + Exit = tas!(b"exit") as isize, + Fail = tas!(b"fail") as isize, + Intr = tas!(b"intr") as isize, + Meme = tas!(b"meme") as isize, +} + #[derive(Clone, Copy, Debug)] pub enum Error { - ScryBlocked(Noun), // path - ScryCrashed(Noun), // trace - Deterministic(Noun), // trace - NonDeterministic(Noun), // trace + ScryBlocked(Noun), // path + ScryCrashed(Noun), // trace + Deterministic(Mote, Noun), // mote, trace + NonDeterministic(Mote, Noun), // mote, trace } pub enum GuardError { @@ -347,8 +355,8 @@ impl Preserve for Error { match self { Error::ScryBlocked(ref mut path) => path.preserve(stack), Error::ScryCrashed(ref mut trace) => trace.preserve(stack), - Error::Deterministic(ref mut trace) => trace.preserve(stack), - Error::NonDeterministic(ref mut trace) => trace.preserve(stack), + Error::Deterministic(_, ref mut trace) => trace.preserve(stack), + Error::NonDeterministic(_, ref mut trace) => trace.preserve(stack), } } @@ -356,26 +364,28 @@ impl Preserve for Error { match self { Error::ScryBlocked(ref path) => path.assert_in_stack(stack), Error::ScryCrashed(ref trace) => trace.assert_in_stack(stack), - Error::Deterministic(ref trace) => trace.assert_in_stack(stack), - Error::NonDeterministic(ref trace) => trace.assert_in_stack(stack), + Error::Deterministic(_, ref trace) => trace.assert_in_stack(stack), + Error::NonDeterministic(_, ref trace) => trace.assert_in_stack(stack), } } } impl From for Error { fn from(_: noun::Error) -> Self { - Error::Deterministic(D(0)) + Error::Deterministic(Mote::Exit, D(0)) } } impl From for Error { fn from(_: cold::Error) -> Self { - Error::Deterministic(D(0)) + Error::Deterministic(Mote::Exit, D(0)) } } pub type Result = result::Result; +const BAIL_EXIT: Result = Err(Error::Deterministic(Mote::Exit, D(0))); + #[allow(unused_variables)] fn debug_assertions(stack: &mut NockStack, noun: Noun) { assert_acyclic!(noun); @@ -461,20 +471,20 @@ pub fn call_with_guard Result>( return result; } GuardError::GuardArmor => { - return Err(Error::Deterministic(D(0))); + return Err(Error::Deterministic(Mote::Exit, D(0))); } GuardError::GuardWeird => { - return Err(Error::Deterministic(D(0))); + return Err(Error::Deterministic(Mote::Exit, D(0))); } GuardError::GuardSpent => { - return Err(Error::NonDeterministic(D(0))); + return Err(Error::NonDeterministic(Mote::Meme, D(0))); } GuardError::GuardErupt => { - return Err(Error::NonDeterministic(D(0))); + return Err(Error::NonDeterministic(Mote::Intr, D(0))); } } } else { - return Err(Error::Deterministic(D(0))); + return Err(Error::Deterministic(Mote::Exit, D(0))); } } } @@ -486,21 +496,21 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res let snapshot = context.save(); let virtual_frame: *const u64 = context.stack.get_frame_pointer(); let mut res: Noun = D(0); - let stack_ptr = context.stack.get_stack_pointer() as *mut c_void; - let stack_ptr_ptr = &stack_ptr as *const *mut c_void; - let alloc_ptr = context.stack.get_alloc_pointer() as *mut c_void; - let alloc_ptr_ptr = &alloc_ptr as *const *mut c_void; + let stack_p = context.stack.get_stack_pointer() as *mut c_void; + let alloc_p = context.stack.get_alloc_pointer() as *mut c_void; + let stack_pp = &stack_p as *const *mut c_void; + let alloc_pp = &alloc_p as *const *mut c_void; // Setup stack for Nock computation unsafe { - context.stack.frame_push(2); + (*context).stack.frame_push(2); // Bottom of mean stack - *(context.stack.local_noun_pointer(0)) = D(0); + *((*context).stack.local_noun_pointer(0)) = D(0); // Bottom of trace stack - *(context.stack.local_noun_pointer(1) as *mut *const TraceStack) = std::ptr::null(); + *((*context).stack.local_noun_pointer(1) as *mut *const TraceStack) = std::ptr::null(); - *(context.stack.push()) = NockWork::Done; + *((*context).stack.push()) = NockWork::Done; }; // DO NOT REMOVE THIS ASSERTION @@ -526,7 +536,6 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res write_trace(context); let stack = &mut context.stack; - debug_assertions(stack, orig_subject); debug_assertions(stack, subject); debug_assertions(stack, res); @@ -582,7 +591,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res context.stack.pop::(); } else { // Axis invalid for input Noun - break Err(Error::Deterministic(D(0))); + break BAIL_EXIT; } } NockWork::Work1(once) => { @@ -591,7 +600,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } NockWork::Work2(mut vale) => { if (*terminator).load(Ordering::Relaxed) { - break Err(Error::NonDeterministic(D(0))); + break Err(Error::NonDeterministic(Mote::Intr, D(0))); } match vale.todo { @@ -661,7 +670,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res context.stack.pop::(); } else { // Cannot increment (Nock 4) a cell - break Err(Error::Deterministic(D(0))); + break BAIL_EXIT; } } }, @@ -704,11 +713,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res push_formula(stack, cond.once, cond.tail)?; } else { // Test branch of Nock 6 must return 0 or 1 - break Err(Error::Deterministic(D(0))); + break BAIL_EXIT; } } else { // Test branch of Nock 6 must return a direct atom - break Err(Error::Deterministic(D(0))); + break BAIL_EXIT; } } }, @@ -764,7 +773,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }, NockWork::Work9(mut kale) => { if (*terminator).load(Ordering::Relaxed) { - break Err(Error::NonDeterministic(D(0))); + break Err(Error::NonDeterministic(Mote::Intr, D(0))); } match kale.todo { @@ -839,7 +848,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } } else { // Axis into core must be atom - break Err(Error::Deterministic(D(0))); + break BAIL_EXIT; } } Todo9::RestoreSubject => { @@ -1042,28 +1051,31 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } }, Err(error) => match error { - Error::Deterministic(trace) + Error::Deterministic(_, trace) | Error::ScryCrashed(trace) => { break Err(Error::ScryCrashed(trace)); } - Error::NonDeterministic(_) => { + Error::NonDeterministic(_, _) => { break Err(error); } Error::ScryBlocked(_) => { - break Err(Error::NonDeterministic(D(0))); + break Err(Error::NonDeterministic( + Mote::Fail, + D(0), + )); } }, } } else { // No scry handler - break Err(Error::Deterministic(D(0))); + break BAIL_EXIT; } } }, }; } }; - call_with_guard(work_closure, stack_ptr_ptr, alloc_ptr_ptr) + call_with_guard(work_closure, stack_pp, alloc_pp) }) }); @@ -1073,7 +1085,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } } -fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Result<(), Error> { +fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result { unsafe { if let Ok(formula_cell) = formula.as_cell() { // Formula @@ -1093,7 +1105,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res *stack.push() = NockWork::Work0(Nock0 { axis: axis_atom }); } else { // Axis for Nock 0 must be an atom - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; } } 1 => { @@ -1111,7 +1123,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res }); } else { // Argument to Nock 2 must be cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; }; } 3 => { @@ -1135,7 +1147,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res }); } else { // Argument to Nock 5 must be cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; }; } 6 => { @@ -1150,11 +1162,11 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res }); } else { // Argument tail to Nock 6 must be cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; }; } else { // Argument to Nock 6 must be cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; } } 7 => { @@ -1167,7 +1179,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res }); } else { // Argument to Nock 7 must be cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; }; } 8 => { @@ -1180,7 +1192,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res }); } else { // Argument to Nock 8 must be cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; }; } 9 => { @@ -1194,11 +1206,11 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res }); } else { // Axis for Nock 9 must be an atom - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; } } else { // Argument to Nock 9 must be cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; }; } 10 => { @@ -1213,15 +1225,15 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res }); } else { // Axis for Nock 10 must be an atom - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; } } else { // Heah of argument to Nock 10 must be a cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; }; } else { // Argument to Nock 10 must be a cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; }; } 11 => { @@ -1246,13 +1258,13 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res }); } else { // Hint tag must be an atom - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; } } }; } else { // Argument for Nock 11 must be cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; }; } 12 => { @@ -1264,26 +1276,26 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res }); } else { // Argument for Nock 12 must be cell - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; } } _ => { // Invalid formula opcode - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; } } } else { // Formula opcode must be direct atom - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; } } } } else { // Bad formula: atoms are not formulas - return Err(Error::Deterministic(D(0))); + return BAIL_EXIT; } } - Ok(()) + Ok(D(0)) } fn exit( @@ -1298,7 +1310,7 @@ fn exit( let stack = &mut context.stack; let mut preserve = match error { Error::ScryBlocked(path) => path, - Error::Deterministic(t) | Error::NonDeterministic(t) | Error::ScryCrashed(t) => { + Error::Deterministic(_, t) | Error::NonDeterministic(_, t) | Error::ScryCrashed(t) => { // Return $tang of traces let h = *(stack.local_noun_pointer(0)); T(stack, &[h, t]) @@ -1311,8 +1323,8 @@ fn exit( } match error { - Error::Deterministic(_) => Error::Deterministic(preserve), - Error::NonDeterministic(_) => Error::NonDeterministic(preserve), + Error::Deterministic(mote, _) => Error::Deterministic(mote, preserve), + Error::NonDeterministic(mote, _) => Error::NonDeterministic(mote, preserve), Error::ScryCrashed(_) => Error::ScryCrashed(preserve), Error::ScryBlocked(_) => error, } @@ -1512,7 +1524,7 @@ mod hint { // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, nock_res, jet_res); // let mean = T(stack, &[D(tas!(b"mean")), tape]); // mean_push(stack, mean); - Some(Err(Error::Deterministic(D(0)))) + Some(BAIL_EXIT) } else { Some(Ok(nock_res)) } @@ -1525,10 +1537,10 @@ mod hint { // mean_push(stack, mean); match error { - Error::NonDeterministic(_) => { - Some(Err(Error::NonDeterministic(D(0)))) + Error::NonDeterministic(mote, _) => { + Some(Err(Error::NonDeterministic(mote, D(0)))) } - _ => Some(Err(Error::Deterministic(D(0)))), + _ => Some(BAIL_EXIT), } } } @@ -1586,7 +1598,7 @@ mod hint { tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => { let terminator = Arc::clone(&TERMINATOR); if (*terminator).load(Ordering::Relaxed) { - return Some(Err(Error::NonDeterministic(D(0)))); + return Some(Err(Error::NonDeterministic(Mote::Intr, D(0)))); } let stack = &mut context.stack; diff --git a/rust/ares/src/jets.rs b/rust/ares/src/jets.rs index 00fc1a07..9d1a97da 100644 --- a/rust/ares/src/jets.rs +++ b/rust/ares/src/jets.rs @@ -15,7 +15,7 @@ pub mod serial; pub mod sort; pub mod tree; -use crate::interpreter::{Context, Error}; +use crate::interpreter::{Context, Error, Mote}; use crate::jets::bits::*; use crate::jets::cold::Cold; use crate::jets::form::*; @@ -79,7 +79,7 @@ impl From for JetErr { impl From for JetErr { fn from(_err: noun::Error) -> Self { - Self::Fail(Error::Deterministic(D(0))) + Self::Fail(Error::Deterministic(Mote::Exit, D(0))) } } @@ -190,6 +190,9 @@ pub mod util { use bitvec::prelude::{BitSlice, Lsb0}; use std::result; + pub const BAIL_EXIT: JetErr = JetErr::Fail(Error::Deterministic(Mote::Exit, D(0))); + pub const BAIL_FAIL: JetErr = JetErr::Fail(Error::NonDeterministic(Mote::Fail, D(0))); + /** * Address-based size checks. * Currently, only addresses indexable by the first 48 bits are reachable by @@ -201,13 +204,12 @@ pub mod util { pub fn checked_add(a: usize, b: usize) -> result::Result { a.checked_add(b) .filter(|x| x <= &MAX_BIT_LENGTH) - .ok_or(JetErr::Fail(Error::NonDeterministic(D(0)))) + .ok_or(BAIL_FAIL) } /// Performs subtraction that returns None on Noun size overflow pub fn checked_sub(a: usize, b: usize) -> result::Result { - a.checked_sub(b) - .ok_or(JetErr::Fail(Error::NonDeterministic(D(0)))) + a.checked_sub(b).ok_or(BAIL_FAIL) } pub fn checked_left_shift(bloq: usize, a: usize) -> result::Result { @@ -215,7 +217,7 @@ pub mod util { // Catch overflow if (res >> bloq) < a || res > MAX_BIT_LENGTH { - Err(JetErr::Fail(Error::NonDeterministic(D(0)))) + Err(BAIL_FAIL) } else { Ok(res) } @@ -232,15 +234,14 @@ pub mod util { } pub fn slot(noun: Noun, axis: u64) -> Result { - noun.slot(axis) - .map_err(|_e| JetErr::Fail(Error::Deterministic(D(0)))) + noun.slot(axis).map_err(|_e| BAIL_EXIT) } /// Extract a bloq and check that it's computable by the current system pub fn bloq(a: Noun) -> result::Result { let bloq = a.as_direct()?.data() as usize; if bloq >= 47 { - Err(JetErr::Fail(Error::NonDeterministic(D(0)))) + Err(BAIL_FAIL) } else { Ok(bloq) } @@ -380,10 +381,13 @@ pub mod util { match (actual_err, expected_err) { (Error::ScryBlocked(mut actual), Error::ScryBlocked(mut expected)) | (Error::ScryCrashed(mut actual), Error::ScryCrashed(mut expected)) - | (Error::Deterministic(mut actual), Error::Deterministic(mut expected)) | ( - Error::NonDeterministic(mut actual), - Error::NonDeterministic(mut expected), + Error::Deterministic(_, mut actual), + Error::Deterministic(_, mut expected), + ) + | ( + Error::NonDeterministic(_, mut actual), + Error::NonDeterministic(_, mut expected), ) => unsafe { assert!(unifying_equality( &mut context.stack, diff --git a/rust/ares/src/jets/bits.rs b/rust/ares/src/jets/bits.rs index 64bcf4d6..4c13d0f0 100644 --- a/rust/ares/src/jets/bits.rs +++ b/rust/ares/src/jets/bits.rs @@ -1,8 +1,8 @@ /** Bit arithmetic & logic jets */ -use crate::interpreter::{Context, Error}; +use crate::interpreter::Context; use crate::jets::util::*; -use crate::jets::{JetErr, Result}; +use crate::jets::Result; use crate::noun::{IndirectAtom, Noun, D}; use std::cmp; @@ -196,7 +196,7 @@ pub fn jet_rev(context: &mut Context, subject: Noun) -> Result { let boz = slot(arg, 2)?.as_atom()?.as_direct()?.data(); if boz >= 64 { - return Err(JetErr::Fail(Error::Deterministic(D(0)))); + return Err(BAIL_EXIT); } let boz = boz as usize; diff --git a/rust/ares/src/jets/list.rs b/rust/ares/src/jets/list.rs index c281d35d..bf40b8d8 100644 --- a/rust/ares/src/jets/list.rs +++ b/rust/ares/src/jets/list.rs @@ -25,7 +25,7 @@ pub fn jet_zing(context: &mut Context, subject: Noun) -> Result { } pub mod util { - use crate::interpreter::Error; + use crate::jets::util::BAIL_EXIT; use crate::jets::{JetErr, Result}; use crate::mem::NockStack; use crate::noun::{Cell, Noun, D, T}; @@ -56,7 +56,7 @@ pub mod util { if atom.as_bitslice().first_one().is_none() { break; } else { - return Err(JetErr::Fail(Error::Deterministic(D(0)))); + return Err(BAIL_EXIT); } } let cell = list.as_cell()?; @@ -98,9 +98,8 @@ pub mod util { #[cfg(test)] mod tests { use super::*; - use crate::interpreter::Error; use crate::jets::util::test::{assert_jet, assert_jet_err, init_context}; - use crate::jets::JetErr; + use crate::jets::util::BAIL_EXIT; use crate::noun::{D, T}; #[test] @@ -135,9 +134,9 @@ mod tests { ); assert_jet(c, jet_flop, sam, res); - assert_jet_err(c, jet_flop, D(1), JetErr::Fail(Error::Deterministic(D(0)))); + assert_jet_err(c, jet_flop, D(1), BAIL_EXIT); let sam = T(&mut c.stack, &[D(1), D(2), D(3)]); - assert_jet_err(c, jet_flop, sam, JetErr::Fail(Error::Deterministic(D(0)))); + assert_jet_err(c, jet_flop, sam, BAIL_EXIT); } #[test] @@ -149,9 +148,9 @@ mod tests { assert_jet(c, jet_lent, sam, D(3)); let sam = T(&mut c.stack, &[D(3), D(2), D(1), D(0)]); assert_jet(c, jet_lent, sam, D(3)); - assert_jet_err(c, jet_lent, D(1), JetErr::Fail(Error::Deterministic(D(0)))); + assert_jet_err(c, jet_lent, D(1), BAIL_EXIT); let sam = T(&mut c.stack, &[D(3), D(2), D(1)]); - assert_jet_err(c, jet_lent, sam, JetErr::Fail(Error::Deterministic(D(0)))); + assert_jet_err(c, jet_lent, sam, BAIL_EXIT); } #[test] diff --git a/rust/ares/src/jets/lock/aes.rs b/rust/ares/src/jets/lock/aes.rs index 8e67abbf..26d166b6 100644 --- a/rust/ares/src/jets/lock/aes.rs +++ b/rust/ares/src/jets/lock/aes.rs @@ -114,9 +114,9 @@ pub fn jet_sivc_de(context: &mut Context, subject: Noun) -> Result { } mod util { - use crate::interpreter::Error; use crate::jets::bits::util::met; use crate::jets::list; + use crate::jets::util::BAIL_FAIL; use crate::jets::{JetErr, Result}; use crate::mem::NockStack; use crate::noun::{Atom, IndirectAtom, Noun, D, T}; @@ -219,7 +219,7 @@ mod util { unsafe { let txt_len = match len.as_direct() { Ok(direct) => direct.data() as usize, - Err(_) => return Err(JetErr::Fail(Error::NonDeterministic(D(0)))), + Err(_) => return Err(BAIL_FAIL), }; let iv_bytes = &mut [0u8; 16]; diff --git a/rust/ares/src/jets/lock/ed.rs b/rust/ares/src/jets/lock/ed.rs index 5f3bc98f..54f75db4 100644 --- a/rust/ares/src/jets/lock/ed.rs +++ b/rust/ares/src/jets/lock/ed.rs @@ -1,9 +1,9 @@ -use crate::interpreter::{Context, Error}; +use crate::interpreter::Context; use crate::jets::bits::util::met; -use crate::jets::util::slot; +use crate::jets::util::{slot, BAIL_EXIT}; use crate::jets::{JetErr, Result}; use crate::mem::NockStack; -use crate::noun::{IndirectAtom, Noun, D, NO, YES}; +use crate::noun::{IndirectAtom, Noun, NO, YES}; use ares_crypto::ed25519::{ac_ed_puck, ac_ed_shar, ac_ed_sign, ac_ed_veri}; crate::gdb!(); @@ -14,7 +14,7 @@ pub fn jet_puck(context: &mut Context, subject: Noun) -> Result { let sed_len = met(3, sed); if sed_len > 32 { - return Err(JetErr::Fail(Error::Deterministic(D(0)))); + return Err(BAIL_EXIT); } unsafe { @@ -35,7 +35,7 @@ pub fn jet_shar(context: &mut Context, subject: Noun) -> Result { if met(3, sec_key) > 32 { // sek is size checked by +puck via +suck - return Err(JetErr::Fail(Error::Deterministic(D(0)))); + return Err(BAIL_EXIT); } if met(3, pub_key) > 32 { // pub is not size checked in Hoon, but it must be 32 bytes or less for @@ -69,7 +69,7 @@ pub fn jet_sign(context: &mut Context, subject: Noun) -> Result { let sed_bytes = sed.as_bytes(); let sed_len = sed_bytes.len(); if sed_len > 32 { - return Err(JetErr::Fail(Error::Deterministic(D(0)))); + return Err(BAIL_EXIT); }; let seed = &mut [0u8; 32]; seed[0..sed_len].copy_from_slice(sed_bytes); @@ -81,8 +81,7 @@ pub fn jet_sign(context: &mut Context, subject: Noun) -> Result { let (_msg_ida, message) = IndirectAtom::new_raw_mut_bytes(stack, msg_len); message.copy_from_slice(&msg.as_bytes()[0..msg_len]); ac_ed_sign(message, seed, sig); - } - else { + } else { ac_ed_sign(&[0u8; 0], seed, sig); } @@ -132,10 +131,7 @@ mod tests { fn test_puck() { let c = &mut init_context(); - let sam = A( - &mut c.stack, - &ubig!(_0x0), - ); + let sam = A(&mut c.stack, &ubig!(_0x0)); let ret = A( &mut c.stack, &ubig!(_0x29da598ba148c03aa643e21d77153265730d6f2ad0a8a3622da4b6cebc276a3b), @@ -158,10 +154,7 @@ mod tests { let c = &mut init_context(); let sam = T(&mut c.stack, &[D(0), D(0)]); - let ret = A( - &mut c.stack, - &ubig!(_0x0), - ); + let ret = A(&mut c.stack, &ubig!(_0x0)); assert_jet(c, jet_shar, sam, ret); let sam = T(&mut c.stack, &[D(234), D(234)]); @@ -175,7 +168,7 @@ mod tests { &mut c.stack, &ubig!(_0xfb099b0acc4d1ce37f9982a2ed331245e0cdfdf6979364b7676a142b8233e53b), ); - assert_jet_err(c, jet_shar, sam, JetErr::Fail(Error::Deterministic(D(0)))); + assert_jet_err(c, jet_shar, sam, BAIL_EXIT); } #[test] diff --git a/rust/ares/src/jets/lock/sha.rs b/rust/ares/src/jets/lock/sha.rs index 444a208c..b368e289 100644 --- a/rust/ares/src/jets/lock/sha.rs +++ b/rust/ares/src/jets/lock/sha.rs @@ -1,8 +1,8 @@ -use crate::interpreter::{Context, Error}; +use crate::interpreter::Context; use crate::jets::bits::util::met; -use crate::jets::util::slot; -use crate::jets::{JetErr, Result}; -use crate::noun::{IndirectAtom, Noun, D}; +use crate::jets::util::{slot, BAIL_FAIL}; +use crate::jets::Result; +use crate::noun::{IndirectAtom, Noun}; use ares_crypto::sha::{ac_sha1, ac_shal, ac_shas, ac_shay}; crate::gdb!(); @@ -25,14 +25,12 @@ pub fn jet_shas(context: &mut Context, subject: Noun) -> Result { let (_msg_ida, message) = IndirectAtom::new_raw_mut_bytes(stack, msg_len); message.copy_from_slice(&ruz.as_bytes()[0..msg_len]); ac_shas(message, salt, out); - } - else { + } else { ac_shas(&mut [], salt, out); } Ok(out_ida.normalize_as_atom().as_noun()) } - } pub fn jet_shax(context: &mut Context, subject: Noun) -> Result { @@ -48,8 +46,7 @@ pub fn jet_shax(context: &mut Context, subject: Noun) -> Result { let (mut _msg_ida, msg_copy) = IndirectAtom::new_raw_mut_bytes(stack, len); msg_copy.copy_from_slice(&msg.as_bytes()[0..len]); ac_shay(&mut (msg_copy)[0..len], out); - } - else { + } else { ac_shay(&mut [], out); } @@ -65,7 +62,7 @@ pub fn jet_shay(context: &mut Context, subject: Noun) -> Result { let width = match wid.as_direct() { Ok(direct) => direct.data() as usize, - Err(_) => return Err(JetErr::Fail(Error::NonDeterministic(D(0)))), + Err(_) => return Err(BAIL_FAIL), }; unsafe { @@ -74,8 +71,7 @@ pub fn jet_shay(context: &mut Context, subject: Noun) -> Result { let (mut _msg_ida, msg) = IndirectAtom::new_raw_mut_bytes(stack, width); msg.copy_from_slice(&dat.as_bytes()[0..width]); ac_shay(msg, out); - } - else { + } else { ac_shay(&mut [], out); } Ok(out_ida.normalize_as_atom().as_noun()) @@ -90,7 +86,7 @@ pub fn jet_shal(context: &mut Context, subject: Noun) -> Result { let _width = match wid.as_direct() { Ok(direct) => direct.data() as usize, - Err(_) => return Err(JetErr::Fail(Error::NonDeterministic(D(0)))), + Err(_) => return Err(BAIL_FAIL), }; let msg_len = met(3, dat); @@ -101,8 +97,7 @@ pub fn jet_shal(context: &mut Context, subject: Noun) -> Result { let (mut _msg_ida, msg) = IndirectAtom::new_raw_mut_bytes(stack, msg_len); msg.copy_from_slice(&dat.as_bytes()[0..msg_len]); ac_shal(msg, out); - } - else { + } else { ac_shal(&mut [], out); } Ok(ida.normalize_as_atom().as_noun()) @@ -117,7 +112,7 @@ pub fn jet_sha1(context: &mut Context, subject: Noun) -> Result { let width = match wid.as_direct() { Ok(direct) => direct.data() as usize, - Err(_) => return Err(JetErr::Fail(Error::NonDeterministic(D(0)))), + Err(_) => return Err(BAIL_FAIL), }; unsafe { @@ -276,24 +271,14 @@ mod tests { IndirectAtom::new_raw_bytes(&mut c.stack, 8, &big as *const u64 as *const u8) }; let sam = T(&mut c.stack, &[ida.as_noun(), D(478560413032)]); - assert_jet_err( - c, - jet_shay, - sam, - JetErr::Fail(Error::NonDeterministic(D(0))), - ); + assert_jet_err(c, jet_shay, sam, BAIL_FAIL); let big: u128 = (DIRECT_MAX as u128) << 64; let ida = unsafe { IndirectAtom::new_raw_bytes(&mut c.stack, 8, &big as *const u128 as *const u8) }; let sam = T(&mut c.stack, &[ida.as_noun(), D(478560413032)]); - assert_jet_err( - c, - jet_shay, - sam, - JetErr::Fail(Error::NonDeterministic(D(0))), - ); + assert_jet_err(c, jet_shay, sam, BAIL_FAIL); } #[test] @@ -333,24 +318,14 @@ mod tests { &ubig!(_0xa1d6eb6ef33f233ae6980ca7c4fc65f90fe1bdee11c730d41607b4747c83de73), ); let sam = T(&mut c.stack, &[wid, dat]); - assert_jet_err( - c, - jet_shal, - sam, - JetErr::Fail(Error::NonDeterministic(D(0))), - ); + assert_jet_err(c, jet_shal, sam, BAIL_FAIL); let wid = A( &mut c.stack, &ubig!(_0xa1d6eb6ef33f233ae6980ca7c4fc65f90fe1bdee11c730d41607b4747c83de72), ); let sam = T(&mut c.stack, &[wid, D(1)]); - assert_jet_err( - c, - jet_shal, - sam, - JetErr::Fail(Error::NonDeterministic(D(0))), - ); + assert_jet_err(c, jet_shal, sam, BAIL_FAIL); } #[test] @@ -382,23 +357,13 @@ mod tests { &ubig!(_0xa1d6eb6ef33f233ae6980ca7c4fc65f90fe1bdee11c730d41607b4747c83de73), ); let sam = T(&mut c.stack, &[wid, dat]); - assert_jet_err( - c, - jet_sha1, - sam, - JetErr::Fail(Error::NonDeterministic(D(0))), - ); + assert_jet_err(c, jet_sha1, sam, BAIL_FAIL); let wid = A( &mut c.stack, &ubig!(_0xa1d6eb6ef33f233ae6980ca7c4fc65f90fe1bdee11c730d41607b4747c83de72), ); let sam = T(&mut c.stack, &[wid, D(1)]); - assert_jet_err( - c, - jet_sha1, - sam, - JetErr::Fail(Error::NonDeterministic(D(0))), - ); + assert_jet_err(c, jet_sha1, sam, BAIL_FAIL); } } diff --git a/rust/ares/src/jets/math.rs b/rust/ares/src/jets/math.rs index f7b03bea..e97d2e5e 100644 --- a/rust/ares/src/jets/math.rs +++ b/rust/ares/src/jets/math.rs @@ -12,9 +12,9 @@ * Another approach is use a global custom allocator. This is fairly involved, but it would allow * us to use any library without worrying whether it allocates. */ -use crate::interpreter::{Context, Error}; +use crate::interpreter::Context; use crate::jets::util::*; -use crate::jets::{JetErr, Result}; +use crate::jets::Result; use crate::noun::{Atom, DirectAtom, IndirectAtom, Noun, D, DIRECT_MAX, NO, T, YES}; use either::{Left, Right}; use ibig::ops::DivRem; @@ -35,7 +35,7 @@ pub fn jet_dec(context: &mut Context, subject: Noun) -> Result { match atom.as_either() { Left(direct) => { if direct.data() == 0 { - Err(JetErr::Fail(Error::Deterministic(D(0)))) + Err(BAIL_EXIT) } else { Ok(unsafe { DirectAtom::new_unchecked(direct.data() - 1) }.as_noun()) } @@ -63,7 +63,7 @@ pub fn jet_dec(context: &mut Context, subject: Noun) -> Result { } } } else { - Err(JetErr::Fail(Error::Deterministic(D(0)))) + Err(BAIL_EXIT) } } @@ -74,7 +74,7 @@ pub fn jet_div(context: &mut Context, subject: Noun) -> Result { let b = slot(arg, 3)?.as_atom()?; if unsafe { b.as_noun().raw_equals(D(0)) } { - Err(JetErr::Fail(Error::Deterministic(D(0)))) + Err(BAIL_EXIT) } else if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) { Ok(unsafe { DirectAtom::new_unchecked(a.data() / b.data()) }.as_noun()) } else { @@ -92,7 +92,7 @@ pub fn jet_dvr(context: &mut Context, subject: Noun) -> Result { let b = slot(arg, 3)?.as_atom()?; if unsafe { b.as_noun().raw_equals(D(0)) } { - Err(JetErr::Fail(Error::Deterministic(D(0)))) + Err(BAIL_EXIT) } else { let (div, rem) = if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) { let (div, rem) = (a.data() / b.data(), a.data() % b.data()); @@ -245,7 +245,7 @@ pub fn jet_mod(context: &mut Context, subject: Noun) -> Result { let b = slot(arg, 3)?.as_atom()?; if unsafe { b.as_noun().raw_equals(D(0)) } { - Err(JetErr::Fail(Error::Deterministic(D(0)))) + Err(BAIL_EXIT) } else if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) { Ok(unsafe { DirectAtom::new_unchecked(a.data() % b.data()) }.as_noun()) } else { @@ -429,7 +429,7 @@ mod tests { let (a0, _a24, a63, _a96, a128) = atoms(s); assert_jet_ubig(c, jet_dec, a128, ubig!(0xdeadbeef12345678fedcba987654320f)); assert_jet(c, jet_dec, a63, D(0x7ffffffffffffffe)); - assert_jet_err(c, jet_dec, a0, JetErr::Fail(Error::Deterministic(D(0)))); + assert_jet_err(c, jet_dec, a0, BAIL_EXIT); } #[test] @@ -451,18 +451,8 @@ mod tests { _0x00000000000001000000000000000000000000000000000000000000000000000000000000000001 ); assert_common_jet(c, jet_div, &[atom_528, atom_264], res); - assert_common_jet_err( - c, - jet_div, - &[atom_63, atom_0], - JetErr::Fail(Error::Deterministic(D(0))), - ); - assert_common_jet_err( - c, - jet_div, - &[atom_0, atom_0], - JetErr::Fail(Error::Deterministic(D(0))), - ); + assert_common_jet_err(c, jet_div, &[atom_63, atom_0], BAIL_EXIT); + assert_common_jet_err(c, jet_div, &[atom_0, atom_0], BAIL_EXIT); } #[test] @@ -508,12 +498,7 @@ mod tests { let res = T(&mut c.stack, &[res_a, res_b]); assert_jet(c, jet_dvr, sam, res); - assert_common_jet_err( - c, - jet_dvr, - &[atom_63, atom_0], - JetErr::Fail(Error::Deterministic(D(0))), - ); + assert_common_jet_err(c, jet_dvr, &[atom_63, atom_0], BAIL_EXIT); } #[test] @@ -675,18 +660,8 @@ mod tests { assert_common_jet(c, jet_mod, &[atom_63, atom_24], ubig!(0x798385)); assert_common_jet(c, jet_mod, &[atom_128, atom_24], ubig!(0x3b2013)); assert_common_jet(c, jet_mod, &[atom_528, atom_264], ubig!(0x100)); - assert_common_jet_err( - c, - jet_mod, - &[atom_63, atom_0], - JetErr::Fail(Error::Deterministic(D(0))), - ); - assert_common_jet_err( - c, - jet_mod, - &[atom_0, atom_0], - JetErr::Fail(Error::Deterministic(D(0))), - ); + assert_common_jet_err(c, jet_mod, &[atom_63, atom_0], BAIL_EXIT); + assert_common_jet_err(c, jet_mod, &[atom_0, atom_0], BAIL_EXIT); } #[test] @@ -732,11 +707,6 @@ mod tests { ); assert_common_jet(c, jet_sub, &[atom_63, atom_63], ubig!(0)); assert_common_jet(c, jet_sub, &[atom_128, atom_128], ubig!(0)); - assert_common_jet_err( - c, - jet_sub, - &[atom_63, atom_96], - JetErr::Fail(Error::Deterministic(D(0))), - ); + assert_common_jet_err(c, jet_sub, &[atom_63, atom_96], BAIL_EXIT); } } diff --git a/rust/ares/src/jets/nock.rs b/rust/ares/src/jets/nock.rs index 33c54f5f..9c5964f6 100644 --- a/rust/ares/src/jets/nock.rs +++ b/rust/ares/src/jets/nock.rs @@ -71,7 +71,7 @@ pub fn jet_mute(context: &mut Context, subject: Noun) -> Result { pub mod util { use crate::hamt::Hamt; - use crate::interpreter::{interpret, Context, Error}; + use crate::interpreter::{interpret, Context, Error, Mote}; use crate::jets; use crate::jets::bits::util::rip; use crate::jets::form::util::scow; @@ -164,7 +164,7 @@ pub mod util { context.scry_stack = scry_snapshot; Ok(T(&mut context.stack, &[D(1), path])) } - Error::Deterministic(trace) => { + Error::Deterministic(_, trace) => { context.cache = cache_snapshot; context.scry_stack = scry_snapshot; Ok(T(&mut context.stack, &[D(2), trace])) @@ -185,12 +185,12 @@ pub mod util { // are identical, jet_mink() bails with Error::Deterministic. Otherwise, it forwards // the Error::ScryCrashed to the senior virtualization call. if unsafe { context.scry_stack.raw_equals(scry_snapshot) } { - Err(Error::Deterministic(trace)) + Err(Error::Deterministic(Mote::Exit, trace)) } else { Err(err) } } - Error::NonDeterministic(_) => { + Error::NonDeterministic(_, _) => { // We choose to restore the cache and scry stack even on NonDeterministic errors // to keep the logic all in one place (as opposed to having the serf reset them // manually ONLY for NonDeterministic errors). @@ -212,7 +212,7 @@ pub mod util { if (tag.data() != 2) | unsafe { original_list.raw_equals(D(0)) } { return Ok(tone); } else if original_list.atom().is_some() { - return Err(Error::Deterministic(D(0))); + return Err(Error::Deterministic(Mote::Exit, D(0))); } // XX: trim traces longer than 1024 frames diff --git a/rust/ares/src/jets/tree.rs b/rust/ares/src/jets/tree.rs index ede01cd6..c3ab905d 100644 --- a/rust/ares/src/jets/tree.rs +++ b/rust/ares/src/jets/tree.rs @@ -1,9 +1,9 @@ /** Tree jets */ -use crate::interpreter::{Context, Error}; +use crate::interpreter::Context; use crate::jets::bits::util::*; use crate::jets::util::*; -use crate::jets::{JetErr, Result}; +use crate::jets::Result; use crate::noun::{IndirectAtom, Noun, D}; crate::gdb!(); @@ -15,7 +15,7 @@ pub fn jet_cap(_context: &mut Context, subject: Noun) -> Result { unsafe { if met < 2 { - Err(JetErr::Fail(Error::Deterministic(D(0)))) + Err(BAIL_EXIT) } else if *(tom.as_bitslice().get_unchecked(met - 2)) { Ok(D(3)) } else { @@ -30,7 +30,7 @@ pub fn jet_mas(context: &mut Context, subject: Noun) -> Result { let met = met(0, tom); if met < 2 { - Err(JetErr::Fail(Error::Deterministic(D(0)))) + Err(BAIL_EXIT) } else { let out_bits = met - 1; let out_words = (out_bits + 63) >> 6; @@ -52,11 +52,11 @@ pub fn jet_peg(context: &mut Context, subject: Noun) -> Result { unsafe { if a.as_noun().raw_equals(D(0)) { - return Err(JetErr::Fail(Error::Deterministic(D(0)))); + return Err(BAIL_EXIT); }; if b.as_noun().raw_equals(D(0)) { - return Err(JetErr::Fail(Error::Deterministic(D(0)))); + return Err(BAIL_EXIT); }; } @@ -78,9 +78,7 @@ pub fn jet_peg(context: &mut Context, subject: Noun) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::interpreter::Error; use crate::jets::util::test::*; - use crate::jets::JetErr; use crate::mem::NockStack; use crate::noun::{Noun, D, DIRECT_MAX}; use ibig::ubig; @@ -129,8 +127,8 @@ mod tests { fn test_cap() { let c = &mut init_context(); - assert_jet_err(c, jet_cap, D(0), JetErr::Fail(Error::Deterministic(D(0)))); - assert_jet_err(c, jet_cap, D(1), JetErr::Fail(Error::Deterministic(D(0)))); + assert_jet_err(c, jet_cap, D(0), BAIL_EXIT); + assert_jet_err(c, jet_cap, D(1), BAIL_EXIT); assert_jet(c, jet_cap, D(2), D(2)); assert_jet(c, jet_cap, D(3), D(3)); @@ -150,8 +148,8 @@ mod tests { let a66 = atom_66(&mut c.stack); // Test invalid input - assert_jet_err(c, jet_mas, D(0), JetErr::Fail(Error::Deterministic(D(0)))); - assert_jet_err(c, jet_mas, D(1), JetErr::Fail(Error::Deterministic(D(0)))); + assert_jet_err(c, jet_mas, D(0), BAIL_EXIT); + assert_jet_err(c, jet_mas, D(1), BAIL_EXIT); // Test direct assert_jet(c, jet_mas, D(2), D(1)); @@ -177,18 +175,8 @@ mod tests { fn test_peg() { let c = &mut init_context(); - assert_common_jet_err( - c, - jet_peg, - &[atom_0, atom_1], - JetErr::Fail(Error::Deterministic(D(0))), - ); - assert_common_jet_err( - c, - jet_peg, - &[atom_1, atom_0], - JetErr::Fail(Error::Deterministic(D(0))), - ); + assert_common_jet_err(c, jet_peg, &[atom_0, atom_1], BAIL_EXIT); + assert_common_jet_err(c, jet_peg, &[atom_1, atom_0], BAIL_EXIT); // Test direct assert_common_jet_noun(c, jet_peg, &[pos_2, pos_3], D(5)); diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index b553771f..54eb26c7 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,6 +1,6 @@ use crate::hamt::Hamt; use crate::interpreter; -use crate::interpreter::{inc, interpret, Error}; +use crate::interpreter::{inc, interpret, Error, Mote}; use crate::jets::cold::Cold; use crate::jets::hot::{Hot, HotEntry}; use crate::jets::list::util::{lent, zing}; @@ -418,7 +418,7 @@ fn peek(context: &mut Context, ovo: Noun) -> Noun { } } -fn goof(context: &mut Context, traces: Noun) -> Noun { +fn goof(context: &mut Context, mote: Mote, traces: Noun) -> Noun { let trace = zing(&mut context.nock_context.stack, traces).unwrap(); let tone = Cell::new(&mut context.nock_context.stack, D(2), trace); let tang = mook(&mut context.nock_context, tone, false) @@ -427,7 +427,7 @@ fn goof(context: &mut Context, traces: Noun) -> Noun { // XX: noun::Error should use a bail enum system similar to u3m_bail motes; // might be able to replace NockErr with mote and map determinism to individual motes; // for, always set to %exit - T(&mut context.nock_context.stack, &[D(tas!(b"exit")), tang]) + T(&mut context.nock_context.stack, &[D(mote as u64), tang]) } /** Run slam; process stack trace to tang if error. @@ -451,8 +451,8 @@ fn soft(context: &mut Context, ovo: Noun, trace_name: Option) -> Result< match slam_res { Ok(res) => Ok(res), Err(error) => match error { - Error::Deterministic(trace) | Error::NonDeterministic(trace) => { - Err(goof(context, trace)) + Error::Deterministic(mote, trace) | Error::NonDeterministic(mote, trace) => { + Err(goof(context, mote, trace)) } Error::ScryBlocked(_) | Error::ScryCrashed(_) => { panic!("serf: soft: .^ invalid outside of virtual Nock") @@ -488,8 +488,8 @@ fn play_life(context: &mut Context, eve: Noun) { context.play_done(); } Err(error) => match error { - Error::Deterministic(trace) | Error::NonDeterministic(trace) => { - let goof = goof(context, trace); + Error::Deterministic(mote, trace) | Error::NonDeterministic(mote, trace) => { + let goof = goof(context, mote, trace); context.play_bail(goof); } Error::ScryBlocked(_) | Error::ScryCrashed(_) => { diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 0357d5a5..7daea60a 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -27,7 +27,7 @@ guard_err _focus_guard() // Check for strange situations. if (stack_p == 0 || alloc_p == 0) { - fprintf(stderr, "guard: stack or alloc pointer is null\r\n"); + // fprintf(stderr, "guard: stack or alloc pointer is null\r\n"); return guard_weird; } @@ -46,7 +46,7 @@ guard_err _focus_guard() guard_p = stack_p + ((alloc_p - stack_p) / 2); } else { - fprintf(stderr, "guard: weird; stack and alloc pointers are equal\r\n"); + // fprintf(stderr, "guard: weird; stack and alloc pointers are equal\r\n"); return guard_weird; } guard_p = (void *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1)); @@ -60,14 +60,14 @@ guard_err _focus_guard() return guard_spent; } - fprintf(stderr, "guard: installed guard page at %p\r\n", (void *) guard_p); + // fprintf(stderr, "guard: installed guard page at %p\r\n", (void *) guard_p); return guard_sound; } guard_err _slash_guard(void *si_addr) { if (si_addr >= (void *)guard_p && si_addr < (void *)guard_p + GD_PAGESIZE) { - fprintf(stderr, "guard: slash in guard\r\n"); + // fprintf(stderr, "guard: slash in guard\r\n"); return _focus_guard(); } @@ -78,11 +78,11 @@ void _signal_handler(int sig, siginfo_t *si, void *unused) { switch (sig) { case SIGSEGV: - fprintf(stderr, "guard: sigsegv at %p\r\n", si->si_addr); + // fprintf(stderr, "guard: sigsegv at %p\r\n", si->si_addr); err = _slash_guard(si->si_addr); break; case SIGINT: - fprintf(stderr, "guard: sigint\r\n"); + // fprintf(stderr, "guard: sigint\r\n"); err = guard_erupt; break; default: @@ -90,7 +90,7 @@ void _signal_handler(int sig, siginfo_t *si, void *unused) } if (err != guard_sound) { - fprintf(stderr, "guard: error %d; long jumping\r\n", err); + // fprintf(stderr, "guard: error %d; long jumping\r\n", err); longjmp(env_buffer, 1); } } @@ -102,7 +102,8 @@ guard_err _register_handler() { sa.sa_sigaction = _signal_handler; sa.sa_mask = 0; - if (sigaction(SIGSEGV, &sa, 0) || sigaction(SIGINT, &sa, 0)) { + // if (sigaction(SIGSEGV, &sa, 0) || sigaction(SIGINT, &sa, 0)) { + if (sigaction(SIGSEGV, &sa, 0)) { return guard_weird; } @@ -127,7 +128,7 @@ guard_err guard( if (guard_p == 0) { guard_err install_err = _focus_guard(); if (install_err != guard_sound) { - fprintf(stderr, "guard: failed to install guard page\r\n"); + // fprintf(stderr, "guard: failed to install guard page\r\n"); err = install_err; goto fail; } @@ -160,21 +161,21 @@ guard_err guard( fail: if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { - fprintf(stderr, "guard: failed to uninstall guard page\r\n"); + // fprintf(stderr, "guard: failed to uninstall guard page\r\n"); } - fprintf(stderr, "guard: fail; uninstalled guard page\r\n"); + // fprintf(stderr, "guard: fail; uninstalled guard page\r\n"); switch (err) { case guard_armor: - fprintf(stderr, "guard: armor error\r\n"); + // fprintf(stderr, "guard: armor error\r\n"); break; case guard_weird: - fprintf(stderr, "guard: weird error\r\n"); + // fprintf(stderr, "guard: weird error\r\n"); break; case guard_spent: - fprintf(stderr, "guard: spent error\r\n"); + // fprintf(stderr, "guard: spent error\r\n"); break; case guard_erupt: - fprintf(stderr, "guard: erupt error\r\n"); + // fprintf(stderr, "guard: erupt error\r\n"); break; } return err; From 680d2d254c0570e6d1f0874031bf2fbcb68b4452 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 30 Jan 2024 14:59:10 -0500 Subject: [PATCH 19/58] wip: debugging ~2024.1.30 --- rust/ares/src/interpreter.rs | 15 ++++++-------- rust/ares/src/serf.rs | 3 --- rust/ares_guard/c-src/guard.c | 38 ++++++++++++++++++----------------- rust/ares_pma/c-src/btree.c | 3 ++- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 826f3827..1572f4e8 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -17,8 +17,7 @@ use crate::trace::{write_nock_trace, TraceInfo, TraceStack}; use crate::unifying_equality::unifying_equality; use ares_guard::*; use ares_macros::tas; -use assert_no_alloc::permit_alloc; -use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters}; +use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters, permit_alloc}; use bitvec::prelude::{BitSlice, Lsb0}; use either::*; use std::convert::TryFrom; @@ -433,11 +432,8 @@ impl<'closure> CCallback<'closure> { let cb: &mut F = user_data.cast::().as_mut().unwrap(); let v = (*cb)(); permit_alloc(|| { - // eprint!("call_closure: v: {:?}\r\n", v); let v_box = Box::new(v); let v_ptr = Box::into_raw(v_box); - // let v_ptr = &mut v as *mut _ as *mut c_void; - // eprint!("call_closure: v_ptr: {:p}\r\n", v_ptr); v_ptr as *mut c_void }) } @@ -465,10 +461,11 @@ pub fn call_with_guard Result>( if let Ok(err) = GuardError::try_from(err) { match err { GuardError::GuardSound => { - let result = *(ret_p as *mut Result); - // eprint!("call_with_guard: ret_p: {:p}\r\n", ret_p); - // eprint!("call_with_guard: result: {:?}\r\n", result); - return result; + permit_alloc(|| { + let result_box = Box::from_raw(ret_p as *mut Result); + let result = *result_box; + return result; + }) } GuardError::GuardArmor => { return Err(Error::Deterministic(Mote::Exit, D(0))); diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 54eb26c7..96b8960c 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -424,9 +424,6 @@ fn goof(context: &mut Context, mote: Mote, traces: Noun) -> Noun { let tang = mook(&mut context.nock_context, tone, false) .expect("serf: goof: +mook crashed on bail") .tail(); - // XX: noun::Error should use a bail enum system similar to u3m_bail motes; - // might be able to replace NockErr with mote and map determinism to individual motes; - // for, always set to %exit T(&mut context.nock_context.stack, &[D(mote as u64), tang]) } diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 7daea60a..c7930f38 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -27,7 +27,7 @@ guard_err _focus_guard() // Check for strange situations. if (stack_p == 0 || alloc_p == 0) { - // fprintf(stderr, "guard: stack or alloc pointer is null\r\n"); + fprintf(stderr, "guard: stack or alloc pointer is null\r\n"); return guard_weird; } @@ -46,7 +46,7 @@ guard_err _focus_guard() guard_p = stack_p + ((alloc_p - stack_p) / 2); } else { - // fprintf(stderr, "guard: weird; stack and alloc pointers are equal\r\n"); + fprintf(stderr, "guard: weird; stack and alloc pointers are equal\r\n"); return guard_weird; } guard_p = (void *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1)); @@ -60,16 +60,17 @@ guard_err _focus_guard() return guard_spent; } - // fprintf(stderr, "guard: installed guard page at %p\r\n", (void *) guard_p); + fprintf(stderr, "guard: installed guard page at %p\r\n", (void *) guard_p); return guard_sound; } guard_err _slash_guard(void *si_addr) { if (si_addr >= (void *)guard_p && si_addr < (void *)guard_p + GD_PAGESIZE) { - // fprintf(stderr, "guard: slash in guard\r\n"); + fprintf(stderr, "guard: slash in guard\r\n"); return _focus_guard(); } + fprintf(stderr, "guard: slash outside guard\r\n"); return guard_weird; } @@ -78,11 +79,11 @@ void _signal_handler(int sig, siginfo_t *si, void *unused) { switch (sig) { case SIGSEGV: - // fprintf(stderr, "guard: sigsegv at %p\r\n", si->si_addr); + fprintf(stderr, "guard: sigsegv at %p\r\n", si->si_addr); err = _slash_guard(si->si_addr); break; case SIGINT: - // fprintf(stderr, "guard: sigint\r\n"); + fprintf(stderr, "guard: sigint\r\n"); err = guard_erupt; break; default: @@ -90,7 +91,7 @@ void _signal_handler(int sig, siginfo_t *si, void *unused) } if (err != guard_sound) { - // fprintf(stderr, "guard: error %d; long jumping\r\n", err); + fprintf(stderr, "guard: error %d; long jumping\r\n", err); longjmp(env_buffer, 1); } } @@ -104,6 +105,7 @@ guard_err _register_handler() { // if (sigaction(SIGSEGV, &sa, 0) || sigaction(SIGINT, &sa, 0)) { if (sigaction(SIGSEGV, &sa, 0)) { + fprintf(stderr, "guard: failed to register handler\r\n"); return guard_weird; } @@ -128,7 +130,7 @@ guard_err guard( if (guard_p == 0) { guard_err install_err = _focus_guard(); if (install_err != guard_sound) { - // fprintf(stderr, "guard: failed to install guard page\r\n"); + fprintf(stderr, "guard: failed to install guard page\r\n"); err = install_err; goto fail; } @@ -151,31 +153,31 @@ guard_err guard( *(void **)ret = result; - // if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { - // err = guard_armor; - // goto fail; - // } + if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + err = guard_armor; + goto fail; + } // fprintf(stderr, "guard: sound; uninstalled guard page\r\n"); return guard_sound; fail: if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { - // fprintf(stderr, "guard: failed to uninstall guard page\r\n"); + fprintf(stderr, "guard: failed to uninstall guard page\r\n"); } - // fprintf(stderr, "guard: fail; uninstalled guard page\r\n"); + fprintf(stderr, "guard: fail; uninstalled guard page\r\n"); switch (err) { case guard_armor: - // fprintf(stderr, "guard: armor error\r\n"); + fprintf(stderr, "guard: armor error\r\n"); break; case guard_weird: - // fprintf(stderr, "guard: weird error\r\n"); + fprintf(stderr, "guard: weird error\r\n"); break; case guard_spent: - // fprintf(stderr, "guard: spent error\r\n"); + fprintf(stderr, "guard: spent error\r\n"); break; case guard_erupt: - // fprintf(stderr, "guard: erupt error\r\n"); + fprintf(stderr, "guard: erupt error\r\n"); break; } return err; diff --git a/rust/ares_pma/c-src/btree.c b/rust/ares_pma/c-src/btree.c index ab266b4d..a225f72c 100644 --- a/rust/ares_pma/c-src/btree.c +++ b/rust/ares_pma/c-src/btree.c @@ -122,7 +122,7 @@ off2addr(vaof_t off) #define BT_NUMMETAS 2 /* 2 metapages */ #define BT_META_SECTION_WIDTH (BT_NUMMETAS * BT_PAGESIZE) #define BT_ADDRSIZE (BT_PAGESIZE << BT_PAGEWORD) -#define PMA_GROW_SIZE_p (1024) +#define PMA_GROW_SIZE_p (10000000) #define PMA_GROW_SIZE_b (BT_PAGESIZE * PMA_GROW_SIZE_p) #define BT_NOPAGE 0 @@ -1570,6 +1570,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); From dd70aadf6e4122e76ba2b702351ab4477428423f Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 30 Jan 2024 15:29:59 -0500 Subject: [PATCH 20/58] wip: add rust-assert-no-alloc --- rust/rust-assert-no-alloc | 1 - rust/rust-assert-no-alloc/.gitignore | 2 + rust/rust-assert-no-alloc/Cargo.toml | 32 +++ rust/rust-assert-no-alloc/LICENSE | 21 ++ rust/rust-assert-no-alloc/README.md | 143 ++++++++++++ rust/rust-assert-no-alloc/examples/main.rs | 34 +++ rust/rust-assert-no-alloc/src/lib.rs | 259 +++++++++++++++++++++ rust/rust-assert-no-alloc/tests/test.rs | 148 ++++++++++++ 8 files changed, 639 insertions(+), 1 deletion(-) delete mode 160000 rust/rust-assert-no-alloc create mode 100644 rust/rust-assert-no-alloc/.gitignore create mode 100644 rust/rust-assert-no-alloc/Cargo.toml create mode 100644 rust/rust-assert-no-alloc/LICENSE create mode 100644 rust/rust-assert-no-alloc/README.md create mode 100644 rust/rust-assert-no-alloc/examples/main.rs create mode 100644 rust/rust-assert-no-alloc/src/lib.rs create mode 100644 rust/rust-assert-no-alloc/tests/test.rs diff --git a/rust/rust-assert-no-alloc b/rust/rust-assert-no-alloc deleted file mode 160000 index 11f0f411..00000000 --- a/rust/rust-assert-no-alloc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 11f0f41123f7fcaf0ad1c23a6a17d97f4650e824 diff --git a/rust/rust-assert-no-alloc/.gitignore b/rust/rust-assert-no-alloc/.gitignore new file mode 100644 index 00000000..96ef6c0b --- /dev/null +++ b/rust/rust-assert-no-alloc/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/rust/rust-assert-no-alloc/Cargo.toml b/rust/rust-assert-no-alloc/Cargo.toml new file mode 100644 index 00000000..bbe18fb0 --- /dev/null +++ b/rust/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/rust-assert-no-alloc/LICENSE b/rust/rust-assert-no-alloc/LICENSE new file mode 100644 index 00000000..c108e4ad --- /dev/null +++ b/rust/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/rust-assert-no-alloc/README.md b/rust/rust-assert-no-alloc/README.md new file mode 100644 index 00000000..8411b1c9 --- /dev/null +++ b/rust/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/rust-assert-no-alloc/examples/main.rs b/rust/rust-assert-no-alloc/examples/main.rs new file mode 100644 index 00000000..7f43d579 --- /dev/null +++ b/rust/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/rust-assert-no-alloc/src/lib.rs b/rust/rust-assert-no-alloc/src/lib.rs new file mode 100644 index 00000000..aa42238d --- /dev/null +++ b/rust/rust-assert-no-alloc/src/lib.rs @@ -0,0 +1,259 @@ +/* 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, 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. +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)); +} + + + + +#[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/rust-assert-no-alloc/tests/test.rs b/rust/rust-assert-no-alloc/tests/test.rs new file mode 100644 index 00000000..d5f6c8da --- /dev/null +++ b/rust/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 54cea05ae2de54aa6cbc9b4a0ba50d4b505d86b5 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 30 Jan 2024 16:37:33 -0500 Subject: [PATCH 21/58] wip: linux debugging --- rust/ares_guard/build.rs | 2 +- rust/ares_guard/c-src/guard.c | 24 +++++++++++++++--------- rust/ares_guard/c-src/guard.h | 4 ++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/rust/ares_guard/build.rs b/rust/ares_guard/build.rs index 82057473..0c11bf3e 100644 --- a/rust/ares_guard/build.rs +++ b/rust/ares_guard/build.rs @@ -40,7 +40,6 @@ fn main() { .flag("-Wformat=2") .flag("-Wmissing-include-dirs") .flag("-Wnested-externs") - .flag("-Wold-style-definition") .flag("-Wpedantic") .flag("-Wredundant-decls") .flag("-Wshadow") @@ -48,6 +47,7 @@ fn main() { .flag("-Wno-unused-parameter") .flag("-Wno-pointer-arith") .flag("-Wno-strict-prototypes") + .flag("-Wno-unused-function") .try_compile("guard"); if let Err(err) = res { diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index c7930f38..289649ba 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include #include @@ -19,8 +17,10 @@ static jmp_buf env_buffer; volatile sig_atomic_t err = guard_sound; + // Center the guard page. -guard_err _focus_guard() +static guard_err +_focus_guard() { uint64_t *stack_p = *stack; uint64_t *alloc_p = *alloc; @@ -65,8 +65,10 @@ guard_err _focus_guard() return guard_sound; } -guard_err _slash_guard(void *si_addr) { - if (si_addr >= (void *)guard_p && si_addr < (void *)guard_p + GD_PAGESIZE) { +static guard_err +_slash_guard(void *addr) +{ + if (addr >= (void *)guard_p && addr < (void *)guard_p + GD_PAGESIZE) { fprintf(stderr, "guard: slash in guard\r\n"); return _focus_guard(); } @@ -75,7 +77,8 @@ guard_err _slash_guard(void *si_addr) { return guard_weird; } -void _signal_handler(int sig, siginfo_t *si, void *unused) +static void +_signal_handler(int sig, siginfo_t *si, void *unused) { switch (sig) { case SIGSEGV: @@ -96,12 +99,14 @@ void _signal_handler(int sig, siginfo_t *si, void *unused) } } -guard_err _register_handler() { +static guard_err +_register_handler() +{ struct sigaction sa; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = _signal_handler; - sa.sa_mask = 0; + sigemptyset(&sa.sa_mask); // if (sigaction(SIGSEGV, &sa, 0) || sigaction(SIGINT, &sa, 0)) { if (sigaction(SIGSEGV, &sa, 0)) { @@ -113,7 +118,8 @@ guard_err _register_handler() { return guard_sound; } -guard_err guard( +guard_err +guard( void *(*f)(void *), void *user_data, void *const *stack_pp, diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index e68e5d12..f91b1887 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -40,7 +40,8 @@ typedef enum { * error will be written to the `ret` pointer. The caller is then responsible * for handling this error and aborting with a `bail:meme`. */ -guard_err guard( +guard_err +guard( void *(*f)(void *), void *user_data, void *const *stack_pp, @@ -48,5 +49,4 @@ guard_err guard( void *const *ret ); - #endif From c07a280ec4a2cc69e5087b8425fa26b5b8759ebc Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 31 Jan 2024 15:43:41 -0500 Subject: [PATCH 22/58] wip: pull in `mem.rs` from `as/motes` --- rust/ares/src/mem.rs | 154 +++++++++++++++++++++++++++---------------- 1 file changed, 99 insertions(+), 55 deletions(-) diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 31f81c5c..a67e7557 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -278,8 +278,14 @@ impl NockStack { if self.pc { panic!("Allocation during cleanup phase is prohibited."); } - self.alloc_pointer = self.alloc_pointer.sub(words); - self.alloc_pointer + + let alloc = self.alloc_pointer.sub(words); + if alloc < self.stack_pointer { + ptr::null_mut() + } else { + self.alloc_pointer = alloc; + alloc + } } /** Bump the alloc pointer for an east frame to make space for an allocation */ @@ -287,9 +293,15 @@ impl NockStack { if self.pc { panic!("Allocation during cleanup phase is prohibited."); } + let alloc = self.alloc_pointer; - self.alloc_pointer = self.alloc_pointer.add(words); - alloc + let new_ap = self.alloc_pointer.add(words); + if new_ap > self.stack_pointer { + ptr::null_mut() + } else { + self.alloc_pointer = new_ap; + alloc + } } /** Allocate space for an indirect pointer in a west frame */ @@ -330,56 +342,49 @@ impl NockStack { } } - unsafe fn struct_alloc_in_previous_frame_west(&mut self, count: usize) -> *mut T { + 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(); - *(self.prev_alloc_pointer_pointer()) = - (*(self.prev_alloc_pointer_pointer())).add(word_size_of::() * count); - alloc as *mut T + 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 + } } - unsafe fn struct_alloc_in_previous_frame_east(&mut self, count: usize) -> *mut T { - // note that the allocation is on the east frame, and thus resembles raw_alloc_west - *(self.prev_alloc_pointer_pointer()) = - (*(self.prev_alloc_pointer_pointer())).sub(word_size_of::() * count); - *(self.prev_alloc_pointer_pointer()) as *mut T + 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 + } } - /** Allocates space in the previous frame for some number of T's. This calls pre_copy() - * first to ensure that the stack frame is in cleanup phase, which is the only time we - * should be allocating in a previous frame.*/ - pub unsafe fn struct_alloc_in_previous_frame(&mut self, count: usize) -> *mut T { + /** Allocate space in the previous stack frame. This calls pre_copy() first to ensure that the + * stack frame is in cleanup phase, which is the only time we should be allocating in a previous + * frame. */ + unsafe fn raw_alloc_in_previous_frame(&mut self, words: usize) -> *mut u64 { self.pre_copy(); if self.is_west() { - self.struct_alloc_in_previous_frame_west(count) + self.raw_alloc_in_previous_frame_west(words) } else { - self.struct_alloc_in_previous_frame_east(count) + self.raw_alloc_in_previous_frame_east(words) } } - unsafe fn indirect_alloc_in_previous_frame_west(&mut self, words: usize) -> *mut u64 { - let alloc = *self.prev_alloc_pointer_pointer(); - *(self.prev_alloc_pointer_pointer()) = - (*(self.prev_alloc_pointer_pointer())).add(words + 2); - alloc - } - - unsafe fn indirect_alloc_in_previous_frame_east(&mut self, words: usize) -> *mut u64 { - *(self.prev_alloc_pointer_pointer()) = - (*(self.prev_alloc_pointer_pointer())).sub(words + 2); - *self.prev_alloc_pointer_pointer() + /** Allocates space in the previous frame for some number of T's. */ + pub unsafe fn struct_alloc_in_previous_frame(&mut self, count: usize) -> *mut T { + self.raw_alloc_in_previous_frame(word_size_of::() * count) as *mut T } - /** Allocate space for an indirect atom in the previous stack frame. This call pre_copy() - * first to ensure that the stack frame is in cleanup phase, which is the only time we - * should be allocating in a previous frame. */ + /** Allocate space for an indirect atom in the previous stack frame. */ unsafe fn indirect_alloc_in_previous_frame(&mut self, words: usize) -> *mut u64 { - self.pre_copy(); - if self.is_west() { - self.indirect_alloc_in_previous_frame_west(words) - } else { - self.indirect_alloc_in_previous_frame_east(words) - } + self.raw_alloc_in_previous_frame(words + 2) } /** Allocate space for an alloc::Layout in a stack frame */ @@ -422,17 +427,27 @@ impl NockStack { * or not pre_copy() has been called.*/ unsafe fn pre_copy(&mut self) { if !self.pc { - *(self.free_slot(FRAME)) = *(self.slot_pointer(FRAME)); - *(self.free_slot(STACK)) = *(self.slot_pointer(STACK)); - *(self.free_slot(ALLOC)) = *(self.slot_pointer(ALLOC)); + let old_stack_pointer = self.stack_pointer; - self.pc = true; // 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)); } } @@ -629,15 +644,27 @@ impl NockStack { /** Push a frame onto the stack with 0 or more local variable slots. */ pub fn frame_push(&mut self, num_locals: usize) { + if self.pc { + panic!("frame_push during cleanup phase is prohibited."); + } + let current_frame_pointer = self.frame_pointer; let current_stack_pointer = self.stack_pointer; let current_alloc_pointer = self.alloc_pointer; unsafe { - self.frame_pointer = if self.is_west() { - current_alloc_pointer.sub(num_locals + RESERVED) + 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(); + } } else { - current_alloc_pointer.add(num_locals + RESERVED) - }; + self.frame_pointer = current_alloc_pointer.add(num_locals + RESERVED); + if self.frame_pointer >= current_stack_pointer { + // OOM + std::ptr::null::().read_volatile(); + } + } self.alloc_pointer = current_stack_pointer; self.stack_pointer = self.frame_pointer; @@ -645,8 +672,6 @@ impl NockStack { *(self.slot_pointer(STACK)) = current_stack_pointer as u64; *(self.slot_pointer(ALLOC)) = current_alloc_pointer as u64; } - - self.pc = false; } /** Run a closure inside a frame, popping regardless of the value returned by the closure. @@ -698,18 +723,37 @@ impl NockStack { } } - /** Push onto a west-oriented lightweight stack, moving the stack_pointer. - * */ + /** Push onto a west-oriented lightweight stack, moving the stack_pointer. */ unsafe fn push_west(&mut self) -> *mut T { + let ap = if self.pc { + *(self.prev_alloc_pointer_pointer()) + } else { + self.alloc_pointer + }; let alloc = self.stack_pointer; - self.stack_pointer = self.stack_pointer.add(word_size_of::()); - alloc as *mut T + let new_sp = self.stack_pointer.add(word_size_of::()); + if new_sp > ap { + ptr::null_mut() + } else { + self.stack_pointer = new_sp; + alloc as *mut T + } } /** Push onto an east-oriented ligthweight stack, moving the stack_pointer */ unsafe fn push_east(&mut self) -> *mut T { - self.stack_pointer = self.stack_pointer.sub(word_size_of::()); - self.stack_pointer as *mut T + let ap = if self.pc { + *(self.prev_alloc_pointer_pointer()) + } else { + self.alloc_pointer + }; + let alloc = self.stack_pointer.sub(word_size_of::()); + if alloc < ap { + ptr::null_mut() + } else { + self.stack_pointer = alloc; + alloc as *mut T + } } /** Pop a west-oriented lightweight stack, moving the stack pointer. */ From 96b22426e2af1acc6cfe805ffd2d4c78fa01661e Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 31 Jan 2024 15:43:58 -0500 Subject: [PATCH 23/58] wip: call previous handler on faults outside guard page --- rust/ares_guard/c-src/guard.c | 44 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 289649ba..15f0c31e 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -14,8 +14,8 @@ static uint64_t *guard_p = 0; static uint64_t **stack = 0; static uint64_t **alloc = 0; static jmp_buf env_buffer; - volatile sig_atomic_t err = guard_sound; +static void (*prev_sigsegv_handler)(int, siginfo_t *, void *); // Center the guard page. @@ -65,25 +65,28 @@ _focus_guard() return guard_sound; } -static guard_err -_slash_guard(void *addr) -{ - if (addr >= (void *)guard_p && addr < (void *)guard_p + GD_PAGESIZE) { - fprintf(stderr, "guard: slash in guard\r\n"); - return _focus_guard(); - } - fprintf(stderr, "guard: slash outside guard\r\n"); - - return guard_weird; -} - static void _signal_handler(int sig, siginfo_t *si, void *unused) { switch (sig) { case SIGSEGV: - fprintf(stderr, "guard: sigsegv at %p\r\n", si->si_addr); - err = _slash_guard(si->si_addr); + if (si->si_addr >= (void *)guard_p && + si->si_addr < (void *)guard_p + GD_PAGESIZE) + { + fprintf(stderr, "guard: fault in guard\r\n"); + err = _focus_guard(); + break; + } + else { + fprintf(stderr, "guard: fault outside guard\r\n"); + if (NULL != prev_sigsegv_handler) { + prev_sigsegv_handler(sig, si, unused); + break; + } + else { + err = guard_weird; + } + } break; case SIGINT: fprintf(stderr, "guard: sigint\r\n"); @@ -95,7 +98,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) if (err != guard_sound) { fprintf(stderr, "guard: error %d; long jumping\r\n", err); - longjmp(env_buffer, 1); + siglongjmp(env_buffer, 1); } } @@ -103,16 +106,17 @@ static guard_err _register_handler() { struct sigaction sa; - + struct sigaction prev_sa; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = _signal_handler; sigemptyset(&sa.sa_mask); + sigaddset(&(sa.sa_mask), SIGSEGV); - // if (sigaction(SIGSEGV, &sa, 0) || sigaction(SIGINT, &sa, 0)) { - if (sigaction(SIGSEGV, &sa, 0)) { + if (sigaction(SIGSEGV, &sa, &prev_sa)) { fprintf(stderr, "guard: failed to register handler\r\n"); return guard_weird; } + prev_sigsegv_handler = prev_sa.sa_sigaction; // fprintf(stderr, "guard: registered handler\r\n"); return guard_sound; @@ -148,7 +152,7 @@ guard( } void *result; - if (setjmp(env_buffer) == 0) { + if (sigsetjmp(env_buffer, 1) == 0) { result = f(user_data); } else { From 3620c7b8940f1cc85f68d57cd4beaf04cbe800b5 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Thu, 1 Feb 2024 09:24:11 -0500 Subject: [PATCH 24/58] Revert "wip: pull in `mem.rs` from `as/motes`" This reverts commit c07a280ec4a2cc69e5087b8425fa26b5b8759ebc. --- rust/ares/src/mem.rs | 154 ++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 99 deletions(-) diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index a67e7557..31f81c5c 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -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 */ @@ -342,49 +330,56 @@ impl NockStack { } } - unsafe fn raw_alloc_in_previous_frame_west(&mut self, words: usize) -> *mut u64 { + unsafe fn struct_alloc_in_previous_frame_west(&mut self, count: usize) -> *mut T { // 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(word_size_of::() * count); + alloc as *mut T } - 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 - } + unsafe fn struct_alloc_in_previous_frame_east(&mut self, count: usize) -> *mut T { + // note that the allocation is on the east frame, and thus resembles raw_alloc_west + *(self.prev_alloc_pointer_pointer()) = + (*(self.prev_alloc_pointer_pointer())).sub(word_size_of::() * count); + *(self.prev_alloc_pointer_pointer()) as *mut T } - /** Allocate space in the previous stack frame. This calls pre_copy() first to ensure that the - * stack frame is in cleanup phase, which is the only time we should be allocating in a previous - * frame. */ - unsafe fn raw_alloc_in_previous_frame(&mut self, words: usize) -> *mut u64 { + /** Allocates space in the previous frame for some number of T's. This calls pre_copy() + * first to ensure that the stack frame is in cleanup phase, which is the only time we + * should be allocating in a previous frame.*/ + pub unsafe fn struct_alloc_in_previous_frame(&mut self, count: usize) -> *mut T { self.pre_copy(); if self.is_west() { - self.raw_alloc_in_previous_frame_west(words) + self.struct_alloc_in_previous_frame_west(count) } else { - self.raw_alloc_in_previous_frame_east(words) + self.struct_alloc_in_previous_frame_east(count) } } - /** Allocates space in the previous frame for some number of T's. */ - pub unsafe fn struct_alloc_in_previous_frame(&mut self, count: usize) -> *mut T { - self.raw_alloc_in_previous_frame(word_size_of::() * count) as *mut T + unsafe fn indirect_alloc_in_previous_frame_west(&mut self, words: usize) -> *mut u64 { + let alloc = *self.prev_alloc_pointer_pointer(); + *(self.prev_alloc_pointer_pointer()) = + (*(self.prev_alloc_pointer_pointer())).add(words + 2); + alloc } - /** Allocate space for an indirect atom in the previous stack frame. */ + unsafe fn indirect_alloc_in_previous_frame_east(&mut self, words: usize) -> *mut u64 { + *(self.prev_alloc_pointer_pointer()) = + (*(self.prev_alloc_pointer_pointer())).sub(words + 2); + *self.prev_alloc_pointer_pointer() + } + + /** Allocate space for an indirect atom in the previous stack frame. This call pre_copy() + * first to ensure that the stack frame is in cleanup phase, which is the only time we + * should be allocating in a previous frame. */ unsafe fn indirect_alloc_in_previous_frame(&mut self, words: usize) -> *mut u64 { - self.raw_alloc_in_previous_frame(words + 2) + self.pre_copy(); + if self.is_west() { + self.indirect_alloc_in_previous_frame_west(words) + } else { + self.indirect_alloc_in_previous_frame_east(words) + } } /** Allocate space for an alloc::Layout in a stack frame */ @@ -427,27 +422,17 @@ 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; + *(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); - 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)); } } @@ -644,27 +629,15 @@ impl NockStack { /** Push a frame onto the stack with 0 or more local variable slots. */ pub fn frame_push(&mut self, num_locals: usize) { - if self.pc { - panic!("frame_push during cleanup phase is prohibited."); - } - let current_frame_pointer = self.frame_pointer; 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; @@ -672,6 +645,8 @@ impl NockStack { *(self.slot_pointer(STACK)) = current_stack_pointer as u64; *(self.slot_pointer(ALLOC)) = current_alloc_pointer as u64; } + + self.pc = false; } /** Run a closure inside a frame, popping regardless of the value returned by the closure. @@ -723,37 +698,18 @@ impl NockStack { } } - /** Push onto a west-oriented lightweight stack, moving the stack_pointer. */ + /** Push onto a west-oriented lightweight stack, moving the stack_pointer. + * */ unsafe fn push_west(&mut self) -> *mut T { - let ap = if self.pc { - *(self.prev_alloc_pointer_pointer()) - } else { - self.alloc_pointer - }; let alloc = self.stack_pointer; - let new_sp = self.stack_pointer.add(word_size_of::()); - if new_sp > ap { - ptr::null_mut() - } else { - self.stack_pointer = new_sp; - alloc as *mut T - } + self.stack_pointer = self.stack_pointer.add(word_size_of::()); + alloc as *mut T } /** Push onto an east-oriented ligthweight stack, moving the stack_pointer */ unsafe fn push_east(&mut self) -> *mut T { - let ap = if self.pc { - *(self.prev_alloc_pointer_pointer()) - } else { - self.alloc_pointer - }; - let alloc = self.stack_pointer.sub(word_size_of::()); - if alloc < ap { - ptr::null_mut() - } else { - self.stack_pointer = alloc; - alloc as *mut T - } + self.stack_pointer = self.stack_pointer.sub(word_size_of::()); + self.stack_pointer as *mut T } /** Pop a west-oriented lightweight stack, moving the stack pointer. */ From 07ee84917580e24d193ae1acfd9679480074bf6f Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Fri, 2 Feb 2024 21:26:28 -0500 Subject: [PATCH 25/58] guard: experiment with frame boundary callbacks --- rust/ares/src/interpreter.rs | 108 ++++++++++++++++++++++++++-------- rust/ares/src/mem.rs | 12 +++- rust/ares_guard/c-src/guard.c | 86 ++++++++++++++------------- rust/ares_guard/c-src/guard.h | 10 ++-- 4 files changed, 147 insertions(+), 69 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 1572f4e8..e6519851 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -21,7 +21,7 @@ use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters, permit_alloc}; use bitvec::prelude::{BitSlice, Lsb0}; use either::*; use std::convert::TryFrom; -use std::ffi::c_void; +use std::ffi::{c_void, c_ulonglong}; use std::result; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -394,20 +394,61 @@ fn debug_assertions(stack: &mut NockStack, noun: Noun) { use std::marker::PhantomData; -pub struct CCallback<'closure> { - pub function: unsafe extern "C" fn(*mut c_void) -> *mut c_void, +/// See: https://users.rust-lang.org/t/passing-a-closure-to-an-external-c-ffi-library/100271/2 +pub struct BoundsCallback<'closure> { + pub function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong, + pub bounds_data: *mut c_void, + + // without this it's too easy to accidentally drop the closure too soon + _lifetime: PhantomData<&'closure mut c_void>, +} + +impl<'closure> BoundsCallback<'closure> { + pub fn new(closure: &'closure mut F) -> Self + where + F: FnMut(*mut c_void) -> *const c_ulonglong, + { + let function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong = Self::call_closure::; + + debug_assert_eq!( + std::mem::size_of::<&'closure mut F>(), + std::mem::size_of::<*const c_void>() + ); + debug_assert_eq!( + std::mem::size_of_val(&function), + std::mem::size_of::<*const c_void>() + ); + + Self { + function, + bounds_data: closure as *mut F as *mut c_void, + _lifetime: PhantomData, + } + } + + unsafe extern "C" fn call_closure(bounds_data: *mut c_void, context_p: *mut c_void) -> *const c_ulonglong + where + F: FnMut(*mut c_void) -> *const c_ulonglong, + { + let cb: &mut F = bounds_data.cast::().as_mut().unwrap(); + (*cb)(context_p) + } +} + +pub struct WorkCallback<'closure> { + pub function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut c_void, pub user_data: *mut c_void, // without this it's too easy to accidentally drop the closure too soon _lifetime: PhantomData<&'closure mut c_void>, } -impl<'closure> CCallback<'closure> { +impl<'closure> WorkCallback<'closure> { pub fn new(closure: &'closure mut F) -> Self where - F: FnMut() -> Result, + F: FnMut(*mut c_void) -> Result, { - let function: unsafe extern "C" fn(*mut c_void) -> *mut c_void = Self::call_closure::; + let function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut c_void = Self::call_closure::; debug_assert_eq!( std::mem::size_of::<&'closure mut F>(), @@ -425,12 +466,12 @@ impl<'closure> CCallback<'closure> { } } - unsafe extern "C" fn call_closure(user_data: *mut c_void) -> *mut c_void + unsafe extern "C" fn call_closure(user_data: *mut c_void, context_p: *mut c_void) -> *mut c_void where - F: FnMut() -> Result, + F: FnMut(*mut c_void) -> Result, { let cb: &mut F = user_data.cast::().as_mut().unwrap(); - let v = (*cb)(); + let v = (*cb)(context_p); permit_alloc(|| { let v_box = Box::new(v); let v_ptr = Box::into_raw(v_box); @@ -439,22 +480,29 @@ impl<'closure> CCallback<'closure> { } } -pub fn call_with_guard Result>( +pub fn call_with_guard Result, G: FnMut(*mut c_void) -> *const c_ulonglong, H: FnMut(*mut c_void) -> *const c_ulonglong>( f: &mut F, - stack: *const *mut c_void, - alloc: *const *mut c_void, + low_f: &mut G, + high_f: &mut H, + context: &mut Context, ) -> Result { - let c = CCallback::new(f); + let work = WorkCallback::new(f); + let low = BoundsCallback::new(low_f); + let high = BoundsCallback::new(high_f); + let context_p = context as *mut Context as *mut c_void; + let mut ret: Result = Ok(D(0)); let ret_p = &mut ret as *mut _ as *mut c_void; let ret_pp = &ret_p as *const *mut c_void; unsafe { let err = guard( - Some(c.function as unsafe extern "C" fn(*mut c_void) -> *mut c_void), - c.user_data as *mut c_void, - stack, - alloc, + Some(work.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut c_void), + work.user_data as *mut c_void, + Some(low.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong), + Some(high.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong), + high.bounds_data as *mut c_void, + context_p, ret_pp, ); @@ -488,15 +536,12 @@ pub fn call_with_guard Result>( /** Interpret nock */ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Result { + // print the addresses of the context.stack.stack_pointer and context.stack.alloc_pointer let terminator = Arc::clone(&TERMINATOR); let orig_subject = subject; // for debugging let snapshot = context.save(); let virtual_frame: *const u64 = context.stack.get_frame_pointer(); let mut res: Noun = D(0); - let stack_p = context.stack.get_stack_pointer() as *mut c_void; - let alloc_p = context.stack.get_alloc_pointer() as *mut c_void; - let stack_pp = &stack_p as *const *mut c_void; - let alloc_pp = &alloc_p as *const *mut c_void; // Setup stack for Nock computation unsafe { @@ -523,7 +568,24 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) let nock = assert_no_alloc(|| { ensure_alloc_counters(|| { - let work_closure = &mut || unsafe { + let low_f = &mut |context_p: *mut c_void| { + let context = unsafe { &mut *(context_p as *mut Context) }; + if context.stack.is_west() { + context.stack.get_stack_pointer() as *const c_ulonglong + } else { + context.stack.get_alloc_pointer() as *const c_ulonglong + } + }; + let high_f = &mut |context_p: *mut c_void| { + let context = unsafe { &mut *(context_p as *mut Context) }; + if context.stack.is_west() { + context.stack.get_alloc_pointer() as *const c_ulonglong + } else { + context.stack.get_stack_pointer() as *const c_ulonglong + } + }; + let work_closure = &mut |context_p: *mut c_void| unsafe { + let context = &mut *(context_p as *mut Context); push_formula(&mut context.stack, formula, true)?; loop { @@ -1072,7 +1134,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }; } }; - call_with_guard(work_closure, stack_pp, alloc_pp) + call_with_guard(work_closure, low_f, high_f, context) }) }); diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 31f81c5c..58838b16 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -147,11 +147,21 @@ impl NockStack { self.stack_pointer } + /** Current stack pointer's address */ + pub fn get_stack_pointer_pointer(&self) -> *const *mut u64 { + &self.stack_pointer as *const *mut u64 + } + /** Current alloc pointer of this NockStack */ pub fn get_alloc_pointer(&self) -> *const u64 { self.alloc_pointer } + /** Current alloc pointer's address */ + pub fn get_alloc_pointer_pointer(&self) -> *const *mut u64 { + &self.alloc_pointer as *const *mut u64 + } + /** Start of the memory range for this NockStack */ pub fn get_start(&self) -> *const u64 { self.start @@ -255,7 +265,7 @@ impl NockStack { } } - /** Pointer to where the previous stack pointer is saved in a frame */ + /** Pointer to where the previous alloc pointer is saved in a frame */ unsafe fn prev_alloc_pointer_pointer(&self) -> *mut *mut u64 { if !self.pc { self.slot_pointer(ALLOC) as *mut *mut u64 diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 15f0c31e..c10d1d04 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -11,23 +11,30 @@ #define GD_PAGESIZE (1ULL << GD_PAGEBITS) /* 16K */ static uint64_t *guard_p = 0; -static uint64_t **stack = 0; -static uint64_t **alloc = 0; static jmp_buf env_buffer; volatile sig_atomic_t err = guard_sound; static void (*prev_sigsegv_handler)(int, siginfo_t *, void *); +static const uint64_t *(*low)(void *, void *) = 0; +static const uint64_t *(*high)(void *, void *) = 0; +static void *bounds = 0; +static void *context = 0; // Center the guard page. static guard_err _focus_guard() { - uint64_t *stack_p = *stack; - uint64_t *alloc_p = *alloc; + const uint64_t *low_p = low(bounds, context); + const uint64_t *high_p = high(bounds, context); + + // Check if we're spent already. + if (low_p == high_p || low_p > high_p) { + return guard_spent; + } // Check for strange situations. - if (stack_p == 0 || alloc_p == 0) { - fprintf(stderr, "guard: stack or alloc pointer is null\r\n"); + if (low_p == 0 || high_p == 0) { + fprintf(stderr, "guard: low or high bound pointer is null\r\n"); return guard_weird; } @@ -38,18 +45,9 @@ _focus_guard() return guard_armor; } - // Place the new guard page in the center. - if (stack_p > alloc_p) { - guard_p = stack_p - ((stack_p - alloc_p) / 2); - } - else if (stack_p < alloc_p) { - guard_p = stack_p + ((alloc_p - stack_p) / 2); - } - else { - fprintf(stderr, "guard: weird; stack and alloc pointers are equal\r\n"); - return guard_weird; - } - guard_p = (void *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1)); + // Place the new guard page in the low-aligned center. + guard_p = (uint64_t *)low_p + ((high_p - low_p) / 2); + guard_p = (uint64_t *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1)); // Mark the new guard page. if (guard_p != old_guard_p) { @@ -60,8 +58,6 @@ _focus_guard() return guard_spent; } - fprintf(stderr, "guard: installed guard page at %p\r\n", (void *) guard_p); - return guard_sound; } @@ -73,12 +69,12 @@ _signal_handler(int sig, siginfo_t *si, void *unused) if (si->si_addr >= (void *)guard_p && si->si_addr < (void *)guard_p + GD_PAGESIZE) { - fprintf(stderr, "guard: fault in guard\r\n"); + fprintf(stderr, "guard: fault in guard: %p\r\n", si->si_addr); err = _focus_guard(); break; } else { - fprintf(stderr, "guard: fault outside guard\r\n"); + fprintf(stderr, "guard: fault outside guard %p\r\n", si->si_addr); if (NULL != prev_sigsegv_handler) { prev_sigsegv_handler(sig, si, unused); break; @@ -124,26 +120,34 @@ _register_handler() guard_err guard( - void *(*f)(void *), - void *user_data, - void *const *stack_pp, - void *const *alloc_pp, + void *(*work_f)(void *, void *), + void *work_data, + const uint64_t *(*low_f)(void *, void *), + const uint64_t *(*high_f)(void *, void *), + void *bounds_data, + void *context_p, void *const *ret ) { - stack = (uint64_t**) stack_pp; - alloc = (uint64_t**) alloc_pp; + // Set globals. + low = low_f; + high = high_f; + bounds= bounds_data; + context = context_p; - // fprintf(stderr, "guard: stack pointer at %p\r\n", (void *) *stack); - // fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *) *alloc); + const uint64_t *low_p = low_f(bounds_data, context_p); + const uint64_t *high_p = high_f(bounds_data, context_p); - if (guard_p == 0) { - guard_err install_err = _focus_guard(); - if (install_err != guard_sound) { - fprintf(stderr, "guard: failed to install guard page\r\n"); - err = install_err; - goto fail; - } + // fprintf(stderr, "guard: low: %p high: %p\r\n", (void *)low_p, (void *)high_p); + + const unsigned long free_mb = (unsigned long)(high_p - low_p) / 1024 / 1024; + // fprintf(stderr, "guard: free space: %lu MB\r\n", free_mb); + + guard_err focus_err = _focus_guard(); + if (focus_err != guard_sound && focus_err != guard_spent) { + fprintf(stderr, "guard: failed to install guard page\r\n"); + err = focus_err; + goto fail; } if (_register_handler() != guard_sound) { @@ -153,7 +157,7 @@ guard( void *result; if (sigsetjmp(env_buffer, 1) == 0) { - result = f(user_data); + result = work_f(work_data, context_p); } else { if (err != guard_sound) { @@ -167,15 +171,15 @@ guard( err = guard_armor; goto fail; } - // fprintf(stderr, "guard: sound; uninstalled guard page\r\n"); return guard_sound; fail: - if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + if (guard_p != NULL && + mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) + { fprintf(stderr, "guard: failed to uninstall guard page\r\n"); } - fprintf(stderr, "guard: fail; uninstalled guard page\r\n"); switch (err) { case guard_armor: fprintf(stderr, "guard: armor error\r\n"); diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index f91b1887..73547011 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -42,10 +42,12 @@ typedef enum { */ guard_err guard( - void *(*f)(void *), - void *user_data, - void *const *stack_pp, - void *const *alloc_pp, + void *(*work_f)(void *, void *), + void *work_data, + const uint64_t *(*low_f)(void *, void *), + const uint64_t *(*high_f)(void *, void *), + void *bounds_data, + void *context_p, void *const *ret ); From 49278d0d896b3fbb7c041ccb0e17617a33a2b150 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Sat, 3 Feb 2024 10:54:45 +0900 Subject: [PATCH 26/58] stack: add debug helper functions to NockStack --- rust/ares/src/mem.rs | 104 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 58838b16..96a2396c 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -844,6 +844,110 @@ impl NockStack { } } } + + /** + * Debugging + * + * The below functions are useful for debugging NockStack issues. */ + + + /** + * Walk down the NockStack, printing frames. Absolutely no safety checks are peformed, as the + * purpose is to discover garbage data; just print pointers until the bottom of the NockStack + * (i.e. a null frame pointer) is encountered. Possible to crash, if a frame pointer gets + * written over. + */ + pub fn print_frames(&mut self) { + let mut fp = self.frame_pointer; + let mut sp = self.stack_pointer; + let mut ap = self.alloc_pointer; + let mut c = 0u64; + + eprintln!("\r start = {:p}", self.start); + + loop { + c += 1; + + eprintln!("\r {}:", c); + eprintln!("\r frame_pointer = {:p}", fp); + eprintln!("\r stack_pointer = {:p}", sp); + eprintln!("\r alloc_pointer = {:p}", ap); + + if fp.is_null() { + break; + } + + unsafe { + if fp < ap { + sp = *(fp.sub(STACK + 1) as *mut *mut u64); + ap = *(fp.sub(ALLOC + 1) as *mut *mut u64); + fp = *(fp.sub(FRAME + 1) as *mut *mut u64); + } else { + sp = *(fp.add(STACK) as *mut *mut u64); + ap = *(fp.add(ALLOC) as *mut *mut u64); + fp = *(fp.add(FRAME) as *mut *mut u64); + } + } + } + } + + /** + * Sanity check every frame of the NockStack. Most useful paired with a gdb session set to + * catch rust_panic. + */ + pub fn assert_sane(&mut self) { + let start = self.start; + let limit = unsafe { self.start.add(self.size) }; + let mut fp = self.frame_pointer; + let mut sp = self.stack_pointer; + let mut ap = self.alloc_pointer; + let mut ought_west: bool = fp < ap; + + loop { + // fp is null iff sp is null + assert!(!(fp.is_null() ^ sp.is_null())); + + // ap should never be null + assert!(!ap.is_null()); + + if fp.is_null() { + break; + } + + // all pointers must be between start and size + assert!(fp as *const u64 >= start); + assert!(fp as *const u64 <= limit); + assert!(sp as *const u64 >= start); + assert!(sp as *const u64 <= limit); + assert!(ap as *const u64 >= start); + assert!(ap as *const u64 <= limit); + + // frames should flip between east-west correctly + assert!((fp < ap) == ought_west); + + // sp should be between fp and ap + if ought_west { + assert!(sp >= fp); + assert!(sp < ap); + } else { + assert!(sp <= fp); + assert!(sp > ap); + } + + unsafe { + if ought_west { + sp = *(fp.sub(STACK + 1) as *mut *mut u64); + ap = *(fp.sub(ALLOC + 1) as *mut *mut u64); + fp = *(fp.sub(FRAME + 1) as *mut *mut u64); + } else { + sp = *(fp.add(STACK) as *mut *mut u64); + ap = *(fp.add(ALLOC) as *mut *mut u64); + fp = *(fp.add(FRAME) as *mut *mut u64); + } + } + ought_west = !ought_west; + } + } } impl NounAllocator for NockStack { From 0a8bcfe16f1cf1ca4e3ff43b87470a5ad6192768 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 5 Feb 2024 09:41:04 -0500 Subject: [PATCH 27/58] wip: more debugging --- rust/ares/Cargo.toml | 6 +- rust/ares/src/interpreter.rs | 210 +++++++++++++++++----------------- rust/ares_guard/Cargo.toml | 3 + rust/ares_guard/c-src/guard.c | 14 ++- 4 files changed, 125 insertions(+), 108 deletions(-) diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index ba1a0598..aa71ecc2 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -44,10 +44,12 @@ name = "ares" path = "src/main.rs" [profile.dev] -opt-level = 3 +opt-level = 0 +debug = true [profile.dev.package."*"] -opt-level = 3 +opt-level = 0 +debug = true # run with e.g. 'cargo build --features check_forwarding,check_acyclic' [features] diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index e6519851..24f76da2 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -569,38 +569,38 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res let nock = assert_no_alloc(|| { ensure_alloc_counters(|| { let low_f = &mut |context_p: *mut c_void| { - let context = unsafe { &mut *(context_p as *mut Context) }; - if context.stack.is_west() { - context.stack.get_stack_pointer() as *const c_ulonglong + let bounds_ctx = unsafe { &mut *(context_p as *mut Context) }; + if bounds_ctx.stack.is_west() { + bounds_ctx.stack.get_stack_pointer() as *const c_ulonglong } else { - context.stack.get_alloc_pointer() as *const c_ulonglong + bounds_ctx.stack.get_alloc_pointer() as *const c_ulonglong } }; let high_f = &mut |context_p: *mut c_void| { - let context = unsafe { &mut *(context_p as *mut Context) }; - if context.stack.is_west() { - context.stack.get_alloc_pointer() as *const c_ulonglong + let bounds_ctx = unsafe { &mut *(context_p as *mut Context) }; + if bounds_ctx.stack.is_west() { + bounds_ctx.stack.get_alloc_pointer() as *const c_ulonglong } else { - context.stack.get_stack_pointer() as *const c_ulonglong + bounds_ctx.stack.get_stack_pointer() as *const c_ulonglong } }; let work_closure = &mut |context_p: *mut c_void| unsafe { - let context = &mut *(context_p as *mut Context); - push_formula(&mut context.stack, formula, true)?; + let work_ctx = &mut *(context_p as *mut Context); + push_formula(&mut work_ctx.stack, formula, true)?; loop { - let work: NockWork = *context.stack.top(); + let work: NockWork = *work_ctx.stack.top(); match work { NockWork::Done => { - write_trace(context); + write_trace(work_ctx); - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; 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 work_ctx.cache); + stack.preserve(&mut work_ctx.cold); + stack.preserve(&mut work_ctx.warm); stack.preserve(&mut res); stack.frame_pop(); @@ -610,16 +610,16 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res break Ok(res); } NockWork::Ret => { - write_trace(context); + write_trace(work_ctx); - let stack = &mut context.stack; + let stack = &mut work_ctx.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 work_ctx.cache); + stack.preserve(&mut work_ctx.cold); + stack.preserve(&mut work_ctx.warm); stack.preserve(&mut res); stack.frame_pop(); @@ -629,17 +629,17 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res NockWork::WorkCons(mut cons) => match cons.todo { TodoCons::ComputeHead => { cons.todo = TodoCons::ComputeTail; - *context.stack.top() = NockWork::WorkCons(cons); - push_formula(&mut context.stack, cons.head, false)?; + *work_ctx.stack.top() = NockWork::WorkCons(cons); + push_formula(&mut work_ctx.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)?; + *work_ctx.stack.top() = NockWork::WorkCons(cons); + push_formula(&mut work_ctx.stack, cons.tail, false)?; } TodoCons::Cons => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; res = T(stack, &[cons.head, res]); stack.pop::(); } @@ -647,7 +647,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res NockWork::Work0(zero) => { if let Ok(noun) = subject.slot_atom(zero.axis) { res = noun; - context.stack.pop::(); + work_ctx.stack.pop::(); } else { // Axis invalid for input Noun break BAIL_EXIT; @@ -655,7 +655,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } NockWork::Work1(once) => { res = once.noun; - context.stack.pop::(); + work_ctx.stack.pop::(); } NockWork::Work2(mut vale) => { if (*terminator).load(Ordering::Relaxed) { @@ -665,17 +665,17 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res match vale.todo { Todo2::ComputeSubject => { vale.todo = Todo2::ComputeFormula; - *context.stack.top() = NockWork::Work2(vale); - push_formula(&mut context.stack, vale.subject, false)?; + *work_ctx.stack.top() = NockWork::Work2(vale); + push_formula(&mut work_ctx.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)?; + *work_ctx.stack.top() = NockWork::Work2(vale); + push_formula(&mut work_ctx.stack, vale.formula, false)?; } Todo2::ComputeResult => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; if vale.tail { stack.pop::(); subject = vale.subject; @@ -695,7 +695,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } } Todo2::RestoreSubject => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; subject = vale.subject; stack.pop::(); @@ -709,24 +709,24 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res NockWork::Work3(mut thee) => match thee.todo { Todo3::ComputeChild => { thee.todo = Todo3::ComputeType; - *context.stack.top() = NockWork::Work3(thee); - push_formula(&mut context.stack, thee.child, false)?; + *work_ctx.stack.top() = NockWork::Work3(thee); + push_formula(&mut work_ctx.stack, thee.child, false)?; } Todo3::ComputeType => { res = if res.is_cell() { D(0) } else { D(1) }; - context.stack.pop::(); + work_ctx.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)?; + *work_ctx.stack.top() = NockWork::Work4(four); + push_formula(&mut work_ctx.stack, four.child, false)?; } Todo4::Increment => { if let Ok(atom) = res.as_atom() { - res = inc(&mut context.stack, atom).as_noun(); - context.stack.pop::(); + res = inc(&mut work_ctx.stack, atom).as_noun(); + work_ctx.stack.pop::(); } else { // Cannot increment (Nock 4) a cell break BAIL_EXIT; @@ -736,17 +736,17 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res NockWork::Work5(mut five) => match five.todo { Todo5::ComputeLeftChild => { five.todo = Todo5::ComputeRightChild; - *context.stack.top() = NockWork::Work5(five); - push_formula(&mut context.stack, five.left, false)?; + *work_ctx.stack.top() = NockWork::Work5(five); + push_formula(&mut work_ctx.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)?; + *work_ctx.stack.top() = NockWork::Work5(five); + push_formula(&mut work_ctx.stack, five.right, false)?; } Todo5::TestEquals => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; let saved_value_ptr = &mut five.left; res = if unifying_equality(stack, &mut res, saved_value_ptr) { D(0) @@ -759,11 +759,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res NockWork::Work6(mut cond) => match cond.todo { Todo6::ComputeTest => { cond.todo = Todo6::ComputeBranch; - *context.stack.top() = NockWork::Work6(cond); - push_formula(&mut context.stack, cond.test, false)?; + *work_ctx.stack.top() = NockWork::Work6(cond); + push_formula(&mut work_ctx.stack, cond.test, false)?; } Todo6::ComputeBranch => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; stack.pop::(); if let Left(direct) = res.as_either_direct_allocated() { if direct.data() == 0 { @@ -783,11 +783,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res NockWork::Work7(mut pose) => match pose.todo { Todo7::ComputeSubject => { pose.todo = Todo7::ComputeResult; - *context.stack.top() = NockWork::Work7(pose); - push_formula(&mut context.stack, pose.subject, false)?; + *work_ctx.stack.top() = NockWork::Work7(pose); + push_formula(&mut work_ctx.stack, pose.subject, false)?; } Todo7::ComputeResult => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; if pose.tail { stack.pop::(); subject = res; @@ -802,17 +802,17 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } Todo7::RestoreSubject => { subject = pose.subject; - context.stack.pop::(); + work_ctx.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)?; + *work_ctx.stack.top() = NockWork::Work8(pins); + push_formula(&mut work_ctx.stack, pins.pin, false)?; } Todo8::ComputeResult => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; if pins.tail { subject = T(stack, &[res, subject]); stack.pop::(); @@ -827,7 +827,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } Todo8::RestoreSubject => { subject = pins.pin; - context.stack.pop::(); + work_ctx.stack.pop::(); } }, NockWork::Work9(mut kale) => { @@ -838,21 +838,21 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res match kale.todo { Todo9::ComputeCore => { kale.todo = Todo9::ComputeResult; - *context.stack.top() = NockWork::Work9(kale); - push_formula(&mut context.stack, kale.core, false)?; + *work_ctx.stack.top() = NockWork::Work9(kale); + push_formula(&mut work_ctx.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, + if let Some((jet, _path)) = work_ctx.warm.find_jet( + &mut work_ctx.stack, &mut res, &mut formula, ) { - match jet(context, res) { + match jet(work_ctx, res) { Ok(jet_res) => { res = jet_res; - context.stack.pop::(); + work_ctx.stack.pop::(); continue; } Err(JetErr::Punt) => {} @@ -863,16 +863,16 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } }; - let stack = &mut context.stack; + let stack = &mut work_ctx.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 work_ctx.trace_info.is_some() { if let Some(path) = - context.cold.matches(stack, &mut res) + work_ctx.cold.matches(stack, &mut res) { append_trace(stack, path); }; @@ -897,9 +897,9 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res // We could trace on 2 as well, but 2 only comes from Hoon via // '.*', so we can assume it's never directly used to invoke // jetted code. - if context.trace_info.is_some() { + if work_ctx.trace_info.is_some() { if let Some(path) = - context.cold.matches(stack, &mut res) + work_ctx.cold.matches(stack, &mut res) { append_trace(stack, path); }; @@ -911,7 +911,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } } Todo9::RestoreSubject => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; subject = kale.core; stack.pop::(); @@ -926,35 +926,35 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res match diet.todo { Todo10::ComputeTree => { diet.todo = Todo10::ComputePatch; // should we compute patch then tree? - *context.stack.top() = NockWork::Work10(diet); - push_formula(&mut context.stack, diet.tree, false)?; + *work_ctx.stack.top() = NockWork::Work10(diet); + push_formula(&mut work_ctx.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)?; + *work_ctx.stack.top() = NockWork::Work10(diet); + push_formula(&mut work_ctx.stack, diet.patch, false)?; } Todo10::Edit => { res = edit( - &mut context.stack, + &mut work_ctx.stack, diet.axis.as_bitslice(), res, diet.tree, ); - context.stack.pop::(); + work_ctx.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, + work_ctx, subject, dint.tag, dint.hint, dint.body, ) { match ret { Ok(found) => { res = found; - context.stack.pop::(); + work_ctx.stack.pop::(); } Err(err) => { break Err(err); @@ -962,13 +962,13 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } } else { dint.todo = Todo11D::ComputeResult; - *context.stack.top() = NockWork::Work11D(dint); - push_formula(&mut context.stack, dint.hint, false)?; + *work_ctx.stack.top() = NockWork::Work11D(dint); + push_formula(&mut work_ctx.stack, dint.hint, false)?; } } Todo11D::ComputeResult => { if let Some(ret) = hint::match_pre_nock( - context, + work_ctx, subject, dint.tag, Some((dint.hint, res)), @@ -977,7 +977,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res match ret { Ok(found) => { res = found; - context.stack.pop::(); + work_ctx.stack.pop::(); } Err(err) => { break Err(err); @@ -985,18 +985,18 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } } else { if dint.tail { - context.stack.pop::(); + work_ctx.stack.pop::(); } else { dint.todo = Todo11D::Done; dint.hint = res; - *context.stack.top() = NockWork::Work11D(dint); + *work_ctx.stack.top() = NockWork::Work11D(dint); } - push_formula(&mut context.stack, dint.body, dint.tail)?; + push_formula(&mut work_ctx.stack, dint.body, dint.tail)?; } } Todo11D::Done => { if let Some(found) = hint::match_post_nock( - context, + work_ctx, subject, dint.tag, Some(dint.hint), @@ -1005,18 +1005,18 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res ) { res = found; } - context.stack.pop::(); + work_ctx.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, + work_ctx, subject, sint.tag, None, sint.body, ) { match ret { Ok(found) => { res = found; - context.stack.pop::(); + work_ctx.stack.pop::(); } Err(err) => { break Err(err); @@ -1024,46 +1024,46 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } } else { if sint.tail { - context.stack.pop::(); + work_ctx.stack.pop::(); } else { sint.todo = Todo11S::Done; - *context.stack.top() = NockWork::Work11S(sint); + *work_ctx.stack.top() = NockWork::Work11S(sint); } - push_formula(&mut context.stack, sint.body, sint.tail)?; + push_formula(&mut work_ctx.stack, sint.body, sint.tail)?; } } Todo11S::Done => { if let Some(found) = hint::match_post_nock( - context, subject, sint.tag, None, sint.body, res, + work_ctx, subject, sint.tag, None, sint.body, res, ) { res = found; } - context.stack.pop::(); + work_ctx.stack.pop::(); } }, NockWork::Work12(mut scry) => match scry.todo { Todo12::ComputeReff => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; scry.todo = Todo12::ComputePath; *stack.top() = NockWork::Work12(scry); push_formula(stack, scry.reff, false)?; } Todo12::ComputePath => { - let stack = &mut context.stack; + let stack = &mut work_ctx.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() { + if let Some(cell) = work_ctx.scry_stack.cell() { scry.path = res; - let scry_stack = context.scry_stack; + let scry_stack = work_ctx.scry_stack; let scry_handler = cell.head(); let scry_gate = scry_handler.as_cell()?; - let payload = T(&mut context.stack, &[scry.reff, res]); + let payload = T(&mut work_ctx.stack, &[scry.reff, res]); let scry_core = T( - &mut context.stack, + &mut work_ctx.stack, &[ scry_gate.head(), payload, @@ -1071,13 +1071,13 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res ], ); let scry_form = - T(&mut context.stack, &[D(9), D(2), D(1), scry_core]); + T(&mut work_ctx.stack, &[D(9), D(2), D(1), scry_core]); - context.scry_stack = cell.tail(); + work_ctx.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) { + match interpret(work_ctx, D(0), scry_form) { Ok(noun) => match noun.as_either_atom_cell() { Left(atom) => { if atom.as_noun().raw_equals(D(0)) { @@ -1089,7 +1089,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res Right(cell) => { match cell.tail().as_either_atom_cell() { Left(_) => { - let stack = &mut context.stack; + let stack = &mut work_ctx.stack; let hunk = T( stack, &[ @@ -1103,8 +1103,8 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res } Right(cell) => { res = cell.tail(); - context.scry_stack = scry_stack; - context.stack.pop::(); + work_ctx.scry_stack = scry_stack; + work_ctx.stack.pop::(); } } } diff --git a/rust/ares_guard/Cargo.toml b/rust/ares_guard/Cargo.toml index 5389a204..1031ab06 100644 --- a/rust/ares_guard/Cargo.toml +++ b/rust/ares_guard/Cargo.toml @@ -10,3 +10,6 @@ edition = "2021" [build-dependencies] bindgen = "0.69.1" cc = "1.0" + +[provile.dev] +opt-level = 0 \ No newline at end of file diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index c10d1d04..40413680 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -64,6 +64,18 @@ _focus_guard() static void _signal_handler(int sig, siginfo_t *si, void *unused) { + if (guard_p == NULL) { + fprintf(stderr, "guard: no guard page\r\n"); + err = guard_weird; + return; + } + + if (si == NULL) { + fprintf(stderr, "guard: no signal info\r\n"); + err = guard_weird; + return; + } + switch (sig) { case SIGSEGV: if (si->si_addr >= (void *)guard_p && @@ -140,7 +152,7 @@ guard( // fprintf(stderr, "guard: low: %p high: %p\r\n", (void *)low_p, (void *)high_p); - const unsigned long free_mb = (unsigned long)(high_p - low_p) / 1024 / 1024; + // const unsigned long free_mb = (unsigned long)(high_p - low_p) / 1024 / 1024; // fprintf(stderr, "guard: free space: %lu MB\r\n", free_mb); guard_err focus_err = _focus_guard(); From fdc3010169917d7f93f9f8fdaea93d82a27ffede Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 5 Feb 2024 13:25:42 -0500 Subject: [PATCH 28/58] wip: only center guard on fault in it --- rust/ares/Cargo.toml | 6 ++--- rust/ares/src/serf.rs | 2 +- rust/ares_guard/c-src/guard.c | 43 +++++++++++++++++------------------ 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index aa71ecc2..ba1a0598 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -44,12 +44,10 @@ name = "ares" path = "src/main.rs" [profile.dev] -opt-level = 0 -debug = true +opt-level = 3 [profile.dev.package."*"] -opt-level = 0 -debug = true +opt-level = 3 # run with e.g. 'cargo build --features check_forwarding,check_acyclic' [features] diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 96b8960c..ef83d79a 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -143,7 +143,7 @@ impl Context { snapshot: Option, constant_hot_state: &[HotEntry], ) -> Self { - let mut stack = NockStack::new(1024 << 10 << 10, 0); + let mut stack = NockStack::new(256 << 10 << 10, 0); let newt = Newt::new(); let cache = Hamt::::new(&mut stack); diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 40413680..26f6beeb 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -1,6 +1,8 @@ #include #include +#include #include +#include #include #include @@ -38,11 +40,14 @@ _focus_guard() return guard_weird; } - // Unmark the old guard page. + // Unmark the old guard page if one exists. void *old_guard_p = guard_p; - if (old_guard_p != 0 - && mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { - return guard_armor; + if (old_guard_p != NULL) { + fprintf(stderr, "guard: retiring old guard page\r\n"); + if (old_guard_p != 0 + && mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { + return guard_armor; + } } // Place the new guard page in the low-aligned center. @@ -51,10 +56,13 @@ _focus_guard() // Mark the new guard page. if (guard_p != old_guard_p) { + fprintf(stderr, "guard: focused guard page\r\n"); if (mprotect(guard_p, GD_PAGESIZE, PROT_NONE) == -1) { return guard_armor; } } else { + fprintf(stderr, "guard: spent; exiting\r\n"); + exit(1); return guard_spent; } @@ -81,12 +89,12 @@ _signal_handler(int sig, siginfo_t *si, void *unused) if (si->si_addr >= (void *)guard_p && si->si_addr < (void *)guard_p + GD_PAGESIZE) { - fprintf(stderr, "guard: fault in guard: %p\r\n", si->si_addr); + fprintf(stderr, "guard: hit: %p\r\n", si->si_addr); err = _focus_guard(); break; } else { - fprintf(stderr, "guard: fault outside guard %p\r\n", si->si_addr); + fprintf(stderr, "guard: weird hit: %p\r\n", si->si_addr); if (NULL != prev_sigsegv_handler) { prev_sigsegv_handler(sig, si, unused); break; @@ -126,7 +134,6 @@ _register_handler() } prev_sigsegv_handler = prev_sa.sa_sigaction; - // fprintf(stderr, "guard: registered handler\r\n"); return guard_sound; } @@ -150,16 +157,13 @@ guard( const uint64_t *low_p = low_f(bounds_data, context_p); const uint64_t *high_p = high_f(bounds_data, context_p); - // fprintf(stderr, "guard: low: %p high: %p\r\n", (void *)low_p, (void *)high_p); - - // const unsigned long free_mb = (unsigned long)(high_p - low_p) / 1024 / 1024; - // fprintf(stderr, "guard: free space: %lu MB\r\n", free_mb); - - guard_err focus_err = _focus_guard(); - if (focus_err != guard_sound && focus_err != guard_spent) { - fprintf(stderr, "guard: failed to install guard page\r\n"); - err = focus_err; - goto fail; + if (guard_p == NULL) { + guard_err focus_err = _focus_guard(); + if (focus_err != guard_sound && focus_err != guard_spent) { + fprintf(stderr, "guard: failed to install guard page\r\n"); + err = focus_err; + goto fail; + } } if (_register_handler() != guard_sound) { @@ -179,11 +183,6 @@ guard( *(void **)ret = result; - if (mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { - err = guard_armor; - goto fail; - } - return guard_sound; fail: From 3f6d76b6992c6abced8aadea366d5f2ad9e8ce8f Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 5 Feb 2024 17:29:44 -0500 Subject: [PATCH 29/58] wip: sigbus on guard hit? --- rust/ares/Cargo.toml | 4 ++-- rust/ares/src/serf.rs | 2 +- rust/ares_guard/c-src/guard.c | 20 ++++++++++++-------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index ba1a0598..733b43fd 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -44,10 +44,10 @@ name = "ares" path = "src/main.rs" [profile.dev] -opt-level = 3 +opt-level = 0 [profile.dev.package."*"] -opt-level = 3 +opt-level = 0 # run with e.g. 'cargo build --features check_forwarding,check_acyclic' [features] diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index ef83d79a..3ca28221 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -143,7 +143,7 @@ impl Context { snapshot: Option, constant_hot_state: &[HotEntry], ) -> Self { - let mut stack = NockStack::new(256 << 10 << 10, 0); + let mut stack = NockStack::new(128 << 10 << 10, 0); let newt = Newt::new(); let cache = Hamt::::new(&mut stack); diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 26f6beeb..0ee039d5 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -50,20 +50,24 @@ _focus_guard() } } - // Place the new guard page in the low-aligned center. + // Calculate the new center for the guard page. guard_p = (uint64_t *)low_p + ((high_p - low_p) / 2); guard_p = (uint64_t *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1)); - // Mark the new guard page. - if (guard_p != old_guard_p) { - fprintf(stderr, "guard: focused guard page\r\n"); + // Place the new guard page or return if we're spent. + bool spent = false; + const bool same = old_guard_p == guard_p; + const bool left = (high_p - low_p) > GD_PAGESIZE; + if (same && !left) { + fprintf(stderr, "guard: spent: %p; left: %u\r\n", guard_p, left); + return guard_spent; + } + else { + fprintf(stderr, "guard: high: %p; low: %p\r\n", high_p, low_p); + fprintf(stderr, "guard: focused: %p; left: %u\r\n", guard_p, left); if (mprotect(guard_p, GD_PAGESIZE, PROT_NONE) == -1) { return guard_armor; } - } else { - fprintf(stderr, "guard: spent; exiting\r\n"); - exit(1); - return guard_spent; } return guard_sound; From 3500ccac9a13c20595554157529eb5c7ed4da9c0 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Tue, 6 Feb 2024 22:11:42 +0900 Subject: [PATCH 30/58] pill: add new pill which triggers OOM error --- 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 zcmaJ|30zZG_Erf=lq_$wVjuyMLPx7$BLWGV7~>xqwJq&XMR7?gS}Kb{0dXlx0tsW$ zqE$<+c1T^ySeMkgV6j4oD4^0{tp)*2z$z*r?zo`;bMJd0!FHyojBi0VE|FgC2ChwTWW=z7{M@BEn!{~6=)Q;km+^GZjLRY@VYQQ;*I^)$`M&Mcb7HO4{X>TTP`Xv zjcxwedM$tbe%;PRazdBDdQlz`dS7+XpXr|H)|0wW*~PQEeHE?S^(|GzT?m*|iddIj|=( zdqP=+v{4x(q1_W}Okg{$6om);Y zjIFRQ(e?$E=%&!$RfWcAS>NS*-5}~sG2YP+h*NA?1TER|s74_R6dEcxjF;qI@#?F+ zR@5x%MIj@+P~U&LAjs}|nCZ+$F&8;l(H%oGEs zrSa8eQf5frgVu&gIfi9VP&_MIQiZ(mdTaeo-3o4STq>uS^r$8yy0zt8lhoNE0A zpXjC3XZdq2`QBA4n9W+1_u^alZwPDTKIKC@lj&s%d*O1E=z@OrWOUaXj z_#FeU90I-{=|^yKn~Ek%FJ3^q)IBtbRQwBE-Rza64|N;o5dLY>lbj~W=Ng4_M4D;i z(iq`BWXD{U^urLr+q&65Q}=BWL@yDZ2{)EJ?l{s8-v%lfSxYOAH;h1ZoISutT+0nI8I`>N(axc#~!%?$k0@~*gTUz0J zqplJ4IDSX@Ks4umU(Pvmhjg#1rzR^K{1JWp$3fG*Fl?K-RnJYk1oXS&O}YjYwF#9H zw{LcyVe@fe=qmkmRYR=vX{?-P4?fo|$g!UR+1u`-cu+zahScypWefwjwOv)(L8 zRW0jQb!VuKnv%Su@5fHl%`F3`kK4*l_)(KsBb-gV13tx{%@r(C;WMskAE@G;Fm2pmAoBwEm^~Z)S=#;c7Q7=Roj!&1W@;k&n zx!wXG(9ZDOx;=as`x;GNzBbeo^6 z6@xExlMiSVgRg=8k$c{Ux2dZei(J#doE=(v^cd3i>M~J*Zgv=sJ$miL9_*$(go^VE zJKv4ybDE%!n#^&XvD3J>bop>=gY(99H&r#?XRrT*b0mZCf1y>d5ft77-y!YU!-nvp zU-$@OnRV6fPT(F0kQ+TiZl9_AWX!Ui3Z;%X zmLX*4Aanu$xAuh5?!=DbXqHJGgZD0)JM1MFGL<0{HcTcf z(RQ7&Nt{iEH8?UhpicWC*o+>st<3Z8~`N( zz#Vm>;wd;KxijrQJaSBj*Xz4scN+ycGK1*&FVdPzh7Y3X-_G%j=+M6045*84<3vj) z^O8Q6g?$qo#?Phx(R`i4Ye+SKOrK{j=p{Ydew!Hm4EjG>oV|j!{4zX)?hpgw0p}Bt zzO-pz{uA__x1ZSIbaT^uIP<2*F4atv-5mv?romesDtUIcWN$m3r>~U zb`-eQEqK^XtL}C?6db`AmzF*989|%xm`S4p&~=yLLDM<5 zITlx^H?ViY!>9W{T+EI$8^USnj^o6c#e&ijWw`CB{_9^w8BT8>>KPR~NgiZ-={QEI z9v{Q#v~8`{Hj&K^0Pb;AdsBFteRr~dtlYG;M610g^dOSv>gtb^VUT}KoC>gLKdx5$ z2%dA*sc@h=w#{arFc6GIY0q(4nL+;a+ez`9V@{{qsT?0W#neuma$ecAxMYLYW-FeDaM59a+eyW+ zyy4q2#IE|SF#&dBqS>NYSB0~?oxCfqf#Ae7#P&1xI_>I`b=nq{({+#bba(j(tmtgn zkAhO@dy|UaC7wDNb~xLVJKA%rPUz%xqdoV)!x=1fzwb4{_ikts<#%!iw?>iV5uk(Z z@YT_tkKwU6K~8=++O@^${Lyf;(efPW9`0{hkW3K@+dcd{Pf6@t`59q+bL=$9Ijerd zSf{_->$$vVwuS&a2eHDs_vQ zjM~cE*KFBde`VT*!Mj`*2D3Q)NSs9+&NIXGamhYygTCp4vfC*JXR&|EJ>;1M7m0F( z!v*KA4__Ssv^AyNIj*bzbg&`GF?9HsQM*?8DzkliILUwdyUXb07ZVq+o#ORexB)cQL{{G@ReJ<+fLhG$ZpE5 z^E~~hV|_{<%LxdN1b1~GvGxm|OGT`UmkfMA6RI>Edp;7JLSLL*>@|C(_lC=49;!l_ z+HuEo+8FDSi^n?5zAl+bUzc3NiHdM?xK_`q+*Z%6rheb6K~D;XGM$2<)icDUI=HZ3 zFjNQcg9UlsC)-g1bXxQ_X_*vDR)P~M{vO;x-sz(OkK4VkVod+xji$N{&S7IQ>-cjj72@?lrX!aAC;Q zh*ObS*O97TOgzr26+}R?h^9}LXWIEUdwP?lueVvic;Jvw~NRX51 zljUTGH|1fmZ<_9#E0df)^p!8|`TaBJa9W%g!&sa+YQE#F7i$Ra#Tv8NafZc-#e$qp zp#6@}_w)#H2@X6*uhn*S>Dn^MI=26vU92(iOE)T|(YpSCAFcgk;(C^=Zix}tk@HJR zd8HfmtDH9yyNrmT_sb~0@?YWMk2HxB@AYO%=Q$OSj<8+NnhSf|?3H(;@-^S|l{dlD zTl$^jHhpnodw5=2zxPUGedTSUjMw19SAG*7|NZryuS<;KRQ$|UL+#X;`TXQ7;i4UD z#?_JrvV~k1Zn#7W8ztbN4Ws;fdu@`pJ9+vljii5w37x)6dbhG}kx!?{| z!w#Adr|l4~9;2P>xYEsaMcmDG2Cp=yYaa~&AE&Ja<;3~P^ArEW`fc#X6;1%UMdVew zMR~a2C*JT$db@Ih<7O5wEM{G-GlV*a+8e+T^OjWiOCVcVJ99^SEP^6|ciw3?ZZ$Z9vqhym6wsF-O~@F& z+9jyKJqLumypC?R8B{q`MxqV7K4J_sF6GrKB~01B^IL@bK69V!2CpzlCPH1uXsCL> zfOBnt7yT|2qdTEOhO5#NZEG1br}%_T0~rL>~%Rs+?bBNz&*#7eqsA^d@ z_(0t-U)|_~e!M5-Z46U@m+S_Kx|d z;_*o76R0SO!WdnH0183bfP0uvDkX{Ea;v_-Db9BLEUnpz_R-9o8q`Rr(>Nj-RKfhq}02R^+uG%Jca)ZHsWgHzBZ^ z#>@>XyaC^8^z@um=yG+I$7i@MwvB+&t?Hc@pwP7ZE{aDlx*;o=831L`*EY12f$zx! zBV(gfTD3+l1mGN;}m%H-q!EpKe|&^nLTWo5aufu(%?rQIV5$D!9OX7sKcQBoiNkqYn>y5SrJ8*0jqng6=A7yZn=XVcF9z zuH=q5OPT}8N&YoLjT9{9JC8%MF}-P;USoK$T%nQgl)!FA^t+ ztPrw5!60Z6Ml2y!P$W-+T7z$0uV z2UDlW)h-!88L;jR0uN3o22_*3xRfdFMpQWD7xJxgpK1tjq?g%)TSlA#xHqa58tWdu zpQhcs=ZP@!jutCltg8^t2K~q`p~qy1E$*wY;8FAU3eaLh9-i{@GYRriu99|_CurnS zDA^(2K-&V@LWVaGbSF%3P?IR;Pj*{{gyFk}`Qtk1#&sY&>qeeNfP*9(|9bogNb2*6 zfOc;XN!Tqd-0@P3Pc3E_E5>QiA87ovYa|{OZ`PQ*X8|IFJlqx~=pj${7W1X@Lyks* zVd9=x_Yc%1KsGr$zYmdQ;y2lpSW~%T{~eXpC-1ry;00qJrT#D?ehPZrr@$fgSNNESgn$4`sruclm%g1!##BaI9CB$(WP8F*5cbX^V(e z>A>mWXDn`4LwFju2T!jP9gl<~7H$JXb~o^0xY6^=8hoG z{G5cmuM|DNM&OOKVP+&FvnNF*S=f`zpTOGj^%@~R?BWHixC+;&_nynAm;0T10k_($p>WP0$nc#IB;@1TAdre*2avo?3Sc za-3Tg`#F3Y$~O_dYPSa^RIXcu8adpAeUkeUa}ejp1v0<@<`@X zYkvipI9g6>?v(%7B!0(0JvJspoU=kHWbZyWB$w04P9!km5^r^G%P{LjK|R{UVl zb(9sYxcvrtP0`~d+)Nn$6K)B7As?h9djs;S1@fx4*lA?v)z+}Vw6_wPP`nSvj^U#$f6=a;np=CrCdyL#bPy|ie?#K*9_7keE{?>gL+9HM6L*yGMHdd{BVwP72n%Y%Ln>2+_Vqn{#zwgDKSGKt+%&AFvRZH#u# z;Jo3`(5ZTBDg1ooVNp&z*>+=&s%8(Gde43g z;|XY4nyTu-kY@R@-EwP4MgjmR?l_)wjiRNQj#;5&(@{0{q4~lC0+X;K53x-Ul|+C+ zX0t07x*=AMM*hlRb;rIn-=PJQviVTp|zUsdLMB0 zS(P$rr(MTzkXb(MU@y*;Gf{53{xN*MMMV%;m!UBtWr z9Gs{i`1+G53Ga^~BqE!pT2T5W1y0NSH0L#jm#3FQL9 zs&!Uz;R~QS*G_yCj=I-C?#h*8vMASbS=V_`ZlJi<=(D!Rk!VNAyMT)ZD$HwuI*Z@% zL*&Qq4Zb&Qe?jt2)paW++i&nENthugHd7o_SjNW!6DndYGJ~=BZ&L6S=$fLg7}voNDS?Fa#Lhyr0rl@en039UzkpAQ?y=cEwSnrD(`&6Q$9^wI z8)ng9X=q|X$Q*3}ScJOakkPV1YG>x?^$dNes_Zk=tfaEN>T{7aZyE4-57btzop}GQ zaP|LD7NqrBp+yN~9fnEJ=!c)n&C9e$)F=z)xg_=H-4&7`gWk{v!~p3&6gO@(xAZDb zXat29i(q!3TxMtuz?H#GT`2Q#J(3w#KY+3goDC{w?%5w4ru_SOY|0h!gA@9LP2#5y z>>)I^LmTi4#siplp0jW1P}HOnqn@JHGBfNY*m+77RT=HZ0OT{@N6gr#1@WvZ#oXozCc1AnMIToGK?+R{fZFdfTU22hW&HY z^&4hj;YD(ovZ<|XghEF%05!IS2}mQ+*ci+Db^QTTEb=wmfg-@jfaI{KG4|7~u@Ttj zZHU_YISWE-W}c2F>mHdw*|-3%0NV7zcnf>XqA|=6P-)(N;%cO~Wm^q5fS>Ztxabx< zHs?G?i4-Li>Y4b*a8qzd6l|65m-$*434;f(Q;O<#73^2CLq zDb*k=sGh@r48vD}0qo-ryhWkp8*ET&Ez8bTrK1Th73dY`j>5bMX7lJka2jxj-DJ__ zos$j9ka5(OP)C+_nJ{<9_Ep#_!=aRuqG6@ZZ?K4{4e^^sbmH_^fkd4oNHZx0mZly)Y#K<1Q$1^rdH*2@`s# zUZ7&EXem&j!karuMr7+nkIMF!zvusezuW4x&P1I?AC5Dz<`T7ha= zd_^d1f+Cz~23&i>R^Nff4xZ-e&#>`a2=kI58D?!TV+B*%=h+}OOzIbQzLvTK_}a67 zrj_mnJ|h}B4ri*C*jZ$%Hck7NGil5srX~YG13cRGlEQW0|3&aD@jvI4C0|pAEBdf} z2d7}G*`3WKr{@=P3kM&B{6i8OmZX5RB)!e0&76zc1(|9K_JbC3plEsYOemz9 z>P0F?uDJgnoGXqi6I9@#3%>wg#{z~@vadK*O#6z{c??&d@C4E}`o?Fj&e;K#bN_m| zCn7ttOe&2mi^!JtWD^f-up*4F3#bn!N)n#mAh@`3zACt0hXe8p_y6F)YcwypPiRp@ z_>l4i$_fmZo^~|)r@6N}2;aFEs-RdycR`LQ9KXxqIY%kaLBo2;F)h7_jm@@bJ}6H| zgOJLmR0>pYEL*jWG~u{baiggThngzWWU zCg`$wOPH{rWdG>4xqza0#@NTwpjH4HD(llPWa^^lP_MK_FonG+$x$TXlgqM+2I``i z?0+L_#e=Ybpxkc+d0$wt_+BFYapHL!8ruX7b>6$G=cKbpct`k*1xq{770GX`zyqw1 z>Q^APj7Af^|Gp4Z2t;fGrNPF#uNTnOChvr8#AG+1 zH=AUQw9Z$gAKIZ{(IDk3vW3+`)N3Z*K|NbiuSpW2HHj0Nd#U6IYKw5~Lsamt7e$eM zEz}Mu0v^f>;bU+j=g;VEw^PXbhAHXkuV6LAcF`7Eks+lO9BmV|Nzm+oq9PFxoirQr zzrk#$UPUiaM|0dR9n6SP8v>f+4svhdlAp+!!7b1K@%jg=#fv3aXv8KUiuQl-HK`6G z@GL;p6wHZeY*}zV0d3JW5WjcgtcnmyFn3r7Ovs@zJv_SsxnIoiWu}owGY)tInnR#> zn_ex%gi{T@9OOoi4r&_yN#567_2pMw?V&SJ0D#s-yoADAV-^6*s(J^es^PFFf2R(M zRatv!YE*@j*NGYNNS3{NkY+QF*ybW_`idmWd%Yq@ibITkq(w3(nc@VEO!|^F7 zErGw1=tf<$k^zh(fgGSMSAIyoUz0l(E85WOTKf=&AYXg4_B^>W(yfeTVdN3gKq2qv zQ)q1$H)?I!fLlw%H4U6EVfGp#9}%bj;Y}c_M{m7Cxf6V^X9o2SaqKy~1iIg~AA$qZ zeGjF9oB^7pHC0@9mjfLG>|uj!D6K6yLLsh8b;=;wE7Y_@R1Grvg2@Z8b;D&((L@uP zSVV4%#d{$B^)IP-V}EJqyhxHb9bhFxPV>R!<+!*;oek0?AQUbf#Z==&kCu8W(f>@&x^Ek*~;V}5+uzh;kJN*p_yR-9a-sxG-eCgm5kdaZx4yv ziozIvF<>9OQqEsQ1_Y1+nM#7%MVm<6y`efmb=LA8pdiQnokT@1G&?A)dr|+Hcvlwy zFxiXQZil2Bec#A?fF=FqSf#}yv5bhaW6v?Az;mMHE)hOY|jSv#<~g;8k(p@f)B+{bMD}Aez+5IWK~wmbcO-;FLWnYhmm=sJu9S$ z#e6Ps8g#utHjXv{0fQ)q56^(mjk-k4t+l3Hr-1=dMzEKsY-kv-9Dq_6pg+3fm}_2P zH|_07_Pqgwe56!Bkv!f80#?z@_8Mv#MTqx@T>!vH zHc+$ai`qz5i#e1%epiW8d{Mt2WleEh-v|$6M)79I|L_YZ=(_cnD~+YA`GDaAjWBNm z4NLTUA@~XvM3e-pv=P{xu=s-0#Io#PMQQF z7pT|K^V=zJlVShL 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 00000000..de9b4f40 --- /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 bac7919bd99b0816395f31bbe6d71387205a8944 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 6 Feb 2024 11:53:27 -0500 Subject: [PATCH 31/58] wip: style --- rust/ares/src/interpreter.rs | 53 ++++++++++++++++++++++++----------- rust/ares/src/mem.rs | 21 +++++++------- rust/ares_guard/c-src/guard.c | 17 +++++++---- 3 files changed, 58 insertions(+), 33 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 24f76da2..b831d1c5 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -21,7 +21,7 @@ use assert_no_alloc::{assert_no_alloc, ensure_alloc_counters, permit_alloc}; use bitvec::prelude::{BitSlice, Lsb0}; use either::*; use std::convert::TryFrom; -use std::ffi::{c_void, c_ulonglong}; +use std::ffi::{c_ulonglong, c_void}; use std::result; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -408,7 +408,8 @@ impl<'closure> BoundsCallback<'closure> { where F: FnMut(*mut c_void) -> *const c_ulonglong, { - let function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong = Self::call_closure::; + let function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong = + Self::call_closure::; debug_assert_eq!( std::mem::size_of::<&'closure mut F>(), @@ -426,7 +427,10 @@ impl<'closure> BoundsCallback<'closure> { } } - unsafe extern "C" fn call_closure(bounds_data: *mut c_void, context_p: *mut c_void) -> *const c_ulonglong + unsafe extern "C" fn call_closure( + bounds_data: *mut c_void, + context_p: *mut c_void, + ) -> *const c_ulonglong where F: FnMut(*mut c_void) -> *const c_ulonglong, { @@ -448,7 +452,8 @@ impl<'closure> WorkCallback<'closure> { where F: FnMut(*mut c_void) -> Result, { - let function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut c_void = Self::call_closure::; + let function: unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut c_void = + Self::call_closure::; debug_assert_eq!( std::mem::size_of::<&'closure mut F>(), @@ -466,7 +471,10 @@ impl<'closure> WorkCallback<'closure> { } } - unsafe extern "C" fn call_closure(user_data: *mut c_void, context_p: *mut c_void) -> *mut c_void + unsafe extern "C" fn call_closure( + user_data: *mut c_void, + context_p: *mut c_void, + ) -> *mut c_void where F: FnMut(*mut c_void) -> Result, { @@ -480,33 +488,44 @@ impl<'closure> WorkCallback<'closure> { } } -pub fn call_with_guard Result, G: FnMut(*mut c_void) -> *const c_ulonglong, H: FnMut(*mut c_void) -> *const c_ulonglong>( - f: &mut F, +pub fn call_with_guard< + F: FnMut(*mut c_void) -> Result, + G: FnMut(*mut c_void) -> *const c_ulonglong, + H: FnMut(*mut c_void) -> *const c_ulonglong, +>( + work_f: &mut F, low_f: &mut G, high_f: &mut H, - context: &mut Context, + context_p: *mut Context, ) -> Result { - let work = WorkCallback::new(f); + let work = WorkCallback::new(work_f); let low = BoundsCallback::new(low_f); let high = BoundsCallback::new(high_f); - let context_p = context as *mut Context as *mut c_void; let mut ret: Result = Ok(D(0)); let ret_p = &mut ret as *mut _ as *mut c_void; let ret_pp = &ret_p as *const *mut c_void; unsafe { - let err = guard( + let guard_error = guard( Some(work.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut c_void), work.user_data as *mut c_void, - Some(low.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong), - Some(high.function as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong), + Some( + low.function + as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong, + ), + Some( + high.function + as unsafe extern "C" fn(*mut c_void, *mut c_void) -> *const c_ulonglong, + ), high.bounds_data as *mut c_void, - context_p, + context_p as *mut c_void, ret_pp, ); - if let Ok(err) = GuardError::try_from(err) { + eprint!("call_with_guard: error: {}\n", guard_error); + + if let Ok(err) = GuardError::try_from(guard_error) { match err { GuardError::GuardSound => { permit_alloc(|| { @@ -584,7 +603,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res bounds_ctx.stack.get_stack_pointer() as *const c_ulonglong } }; - let work_closure = &mut |context_p: *mut c_void| unsafe { + let work_f = &mut |context_p: *mut c_void| unsafe { let work_ctx = &mut *(context_p as *mut Context); push_formula(&mut work_ctx.stack, formula, true)?; @@ -1134,7 +1153,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }; } }; - call_with_guard(work_closure, low_f, high_f, context) + call_with_guard(work_f, low_f, high_f, context as *mut Context) }) }); diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 96a2396c..2e74688b 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -847,24 +847,23 @@ impl NockStack { /** * Debugging - * + * * The below functions are useful for debugging NockStack issues. */ - - /** - * Walk down the NockStack, printing frames. Absolutely no safety checks are peformed, as the - * purpose is to discover garbage data; just print pointers until the bottom of the NockStack - * (i.e. a null frame pointer) is encountered. Possible to crash, if a frame pointer gets - * written over. - */ + /** + * Walk down the NockStack, printing frames. Absolutely no safety checks are peformed, as the + * purpose is to discover garbage data; just print pointers until the bottom of the NockStack + * (i.e. a null frame pointer) is encountered. Possible to crash, if a frame pointer gets + * written over. + */ pub fn print_frames(&mut self) { let mut fp = self.frame_pointer; let mut sp = self.stack_pointer; let mut ap = self.alloc_pointer; let mut c = 0u64; - + eprintln!("\r start = {:p}", self.start); - + loop { c += 1; @@ -906,7 +905,7 @@ impl NockStack { loop { // fp is null iff sp is null assert!(!(fp.is_null() ^ sp.is_null())); - + // ap should never be null assert!(!ap.is_null()); diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 0ee039d5..74739676 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -117,7 +117,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) } if (err != guard_sound) { - fprintf(stderr, "guard: error %d; long jumping\r\n", err); + fprintf(stderr, "guard: long jumping\r\n"); siglongjmp(env_buffer, 1); } } @@ -178,6 +178,8 @@ guard( void *result; if (sigsetjmp(env_buffer, 1) == 0) { result = work_f(work_data, context_p); + *(void **)ret = result; + return guard_sound; } else { if (err != guard_sound) { @@ -185,16 +187,21 @@ guard( } } - *(void **)ret = result; - - return guard_sound; - fail: if (guard_p != NULL && mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { fprintf(stderr, "guard: failed to uninstall guard page\r\n"); } + + // Restore the previous signal handler. + struct sigaction sa; + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = prev_sigsegv_handler; + sigemptyset(&sa.sa_mask); + sigaddset(&(sa.sa_mask), SIGSEGV); + sigaction(SIGSEGV, &sa, NULL); + switch (err) { case guard_armor: fprintf(stderr, "guard: armor error\r\n"); From e086fbd3888354788c3c2d8bf21d7d7e713aebf7 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 7 Feb 2024 18:00:27 +0900 Subject: [PATCH 32/58] stack: safe partial commit of mem.rs changes from motes PR --- rust/ares/src/mem.rs | 59 +++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 2e74688b..d448958a 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -340,56 +340,39 @@ impl NockStack { } } - unsafe fn struct_alloc_in_previous_frame_west(&mut self, count: usize) -> *mut T { + 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(); - *(self.prev_alloc_pointer_pointer()) = - (*(self.prev_alloc_pointer_pointer())).add(word_size_of::() * count); - alloc as *mut T + *(self.prev_alloc_pointer_pointer()) = (*(self.prev_alloc_pointer_pointer())).add(words); + alloc } - unsafe fn struct_alloc_in_previous_frame_east(&mut self, count: usize) -> *mut T { - // note that the allocation is on the east frame, and thus resembles raw_alloc_west - *(self.prev_alloc_pointer_pointer()) = - (*(self.prev_alloc_pointer_pointer())).sub(word_size_of::() * count); - *(self.prev_alloc_pointer_pointer()) as *mut T + 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 + *(self.prev_alloc_pointer_pointer()) = (*(self.prev_alloc_pointer_pointer())).sub(words); + *(self.prev_alloc_pointer_pointer()) } - /** Allocates space in the previous frame for some number of T's. This calls pre_copy() - * first to ensure that the stack frame is in cleanup phase, which is the only time we - * should be allocating in a previous frame.*/ - pub unsafe fn struct_alloc_in_previous_frame(&mut self, count: usize) -> *mut T { + /** Allocate space in the previous stack frame. This calls pre_copy() first to ensure that the + * stack frame is in cleanup phase, which is the only time we should be allocating in a previous + * frame. */ + unsafe fn raw_alloc_in_previous_frame(&mut self, words: usize) -> *mut u64 { self.pre_copy(); if self.is_west() { - self.struct_alloc_in_previous_frame_west(count) + self.raw_alloc_in_previous_frame_west(words) } else { - self.struct_alloc_in_previous_frame_east(count) + self.raw_alloc_in_previous_frame_east(words) } } - unsafe fn indirect_alloc_in_previous_frame_west(&mut self, words: usize) -> *mut u64 { - let alloc = *self.prev_alloc_pointer_pointer(); - *(self.prev_alloc_pointer_pointer()) = - (*(self.prev_alloc_pointer_pointer())).add(words + 2); - alloc - } - - unsafe fn indirect_alloc_in_previous_frame_east(&mut self, words: usize) -> *mut u64 { - *(self.prev_alloc_pointer_pointer()) = - (*(self.prev_alloc_pointer_pointer())).sub(words + 2); - *self.prev_alloc_pointer_pointer() + /** Allocates space in the previous frame for some number of T's. */ + pub unsafe fn struct_alloc_in_previous_frame(&mut self, count: usize) -> *mut T { + self.raw_alloc_in_previous_frame(word_size_of::() * count) as *mut T } - /** Allocate space for an indirect atom in the previous stack frame. This call pre_copy() - * first to ensure that the stack frame is in cleanup phase, which is the only time we - * should be allocating in a previous frame. */ + /** Allocate space for an indirect atom in the previous stack frame. */ unsafe fn indirect_alloc_in_previous_frame(&mut self, words: usize) -> *mut u64 { - self.pre_copy(); - if self.is_west() { - self.indirect_alloc_in_previous_frame_west(words) - } else { - self.indirect_alloc_in_previous_frame_east(words) - } + self.raw_alloc_in_previous_frame(words + 2) } /** Allocate space for an alloc::Layout in a stack frame */ @@ -639,6 +622,10 @@ impl NockStack { /** Push a frame onto the stack with 0 or more local variable slots. */ pub fn frame_push(&mut self, num_locals: usize) { + if self.pc { + panic!("frame_push during cleanup phase is prohibited."); + } + let current_frame_pointer = self.frame_pointer; let current_stack_pointer = self.stack_pointer; let current_alloc_pointer = self.alloc_pointer; @@ -655,8 +642,6 @@ impl NockStack { *(self.slot_pointer(STACK)) = current_stack_pointer as u64; *(self.slot_pointer(ALLOC)) = current_alloc_pointer as u64; } - - self.pc = false; } /** Run a closure inside a frame, popping regardless of the value returned by the closure. From c43b3e4cb4158c6fbf045311b94260b3177e1d36 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 7 Feb 2024 16:09:57 -0500 Subject: [PATCH 33/58] wip: refactoring and remove sigint handling --- rust/ares/src/interpreter.rs | 10 +-- rust/ares/src/serf.rs | 2 +- rust/ares_guard/c-src/guard.c | 141 +++++++++++----------------------- rust/ares_guard/c-src/guard.h | 10 +-- rust/ares_guard/src/lib.rs | 1 - 5 files changed, 53 insertions(+), 111 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index b831d1c5..8bdb04ad 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -332,7 +332,6 @@ pub enum GuardError { GuardArmor = GUARD_ARMOR as isize, GuardWeird = GUARD_WEIRD as isize, GuardSpent = GUARD_SPENT as isize, - GuardErupt = GUARD_ERUPT as isize, } impl TryFrom for GuardError { @@ -343,7 +342,6 @@ impl TryFrom for GuardError { GUARD_ARMOR => Ok(GuardError::GuardArmor), GUARD_WEIRD => Ok(GuardError::GuardWeird), GUARD_SPENT => Ok(GuardError::GuardSpent), - GUARD_ERUPT => Ok(GuardError::GuardErupt), _ => Err(()), } } @@ -523,8 +521,6 @@ pub fn call_with_guard< ret_pp, ); - eprint!("call_with_guard: error: {}\n", guard_error); - if let Ok(err) = GuardError::try_from(guard_error) { match err { GuardError::GuardSound => { @@ -535,7 +531,8 @@ pub fn call_with_guard< }) } GuardError::GuardArmor => { - return Err(Error::Deterministic(Mote::Exit, D(0))); + // XX + panic!("guard: couldn't place guard page\r\n"); } GuardError::GuardWeird => { return Err(Error::Deterministic(Mote::Exit, D(0))); @@ -543,9 +540,6 @@ pub fn call_with_guard< GuardError::GuardSpent => { return Err(Error::NonDeterministic(Mote::Meme, D(0))); } - GuardError::GuardErupt => { - return Err(Error::NonDeterministic(Mote::Intr, D(0))); - } } } else { return Err(Error::Deterministic(Mote::Exit, D(0))); diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 3ca28221..a982fe03 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -143,7 +143,7 @@ impl Context { snapshot: Option, constant_hot_state: &[HotEntry], ) -> Self { - let mut stack = NockStack::new(128 << 10 << 10, 0); + let mut stack = NockStack::new(64 << 10 << 10, 0); let newt = Newt::new(); let cache = Hamt::::new(&mut stack); diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 74739676..9b6d9840 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -14,9 +14,7 @@ static uint64_t *guard_p = 0; static jmp_buf env_buffer; -volatile sig_atomic_t err = guard_sound; static void (*prev_sigsegv_handler)(int, siginfo_t *, void *); - static const uint64_t *(*low)(void *, void *) = 0; static const uint64_t *(*high)(void *, void *) = 0; static void *bounds = 0; @@ -29,33 +27,20 @@ _focus_guard() const uint64_t *low_p = low(bounds, context); const uint64_t *high_p = high(bounds, context); - // Check if we're spent already. - if (low_p == high_p || low_p > high_p) { + if (low_p >= high_p ) { return guard_spent; } - // Check for strange situations. if (low_p == 0 || high_p == 0) { fprintf(stderr, "guard: low or high bound pointer is null\r\n"); return guard_weird; } - // Unmark the old guard page if one exists. void *old_guard_p = guard_p; - if (old_guard_p != NULL) { - fprintf(stderr, "guard: retiring old guard page\r\n"); - if (old_guard_p != 0 - && mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) { - return guard_armor; - } - } - // Calculate the new center for the guard page. guard_p = (uint64_t *)low_p + ((high_p - low_p) / 2); guard_p = (uint64_t *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1)); - // Place the new guard page or return if we're spent. - bool spent = false; const bool same = old_guard_p == guard_p; const bool left = (high_p - low_p) > GD_PAGESIZE; if (same && !left) { @@ -63,62 +48,51 @@ _focus_guard() return guard_spent; } else { - fprintf(stderr, "guard: high: %p; low: %p\r\n", high_p, low_p); - fprintf(stderr, "guard: focused: %p; left: %u\r\n", guard_p, left); if (mprotect(guard_p, GD_PAGESIZE, PROT_NONE) == -1) { return guard_armor; } } + if (old_guard_p != NULL && + mprotect(old_guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) + { + return guard_armor; + } + return guard_sound; } static void _signal_handler(int sig, siginfo_t *si, void *unused) { + if (sig != SIGSEGV) { + fprintf(stderr, "guard: weird signal: %d\r\n", sig); + return; + } + if (guard_p == NULL) { fprintf(stderr, "guard: no guard page\r\n"); - err = guard_weird; return; } if (si == NULL) { fprintf(stderr, "guard: no signal info\r\n"); - err = guard_weird; return; } - switch (sig) { - case SIGSEGV: - if (si->si_addr >= (void *)guard_p && - si->si_addr < (void *)guard_p + GD_PAGESIZE) - { - fprintf(stderr, "guard: hit: %p\r\n", si->si_addr); - err = _focus_guard(); - break; - } - else { - fprintf(stderr, "guard: weird hit: %p\r\n", si->si_addr); - if (NULL != prev_sigsegv_handler) { - prev_sigsegv_handler(sig, si, unused); - break; - } - else { - err = guard_weird; - } - } - break; - case SIGINT: - fprintf(stderr, "guard: sigint\r\n"); - err = guard_erupt; - break; - default: - break; - } - - if (err != guard_sound) { - fprintf(stderr, "guard: long jumping\r\n"); - siglongjmp(env_buffer, 1); + if (si->si_addr >= (void *)guard_p && + si->si_addr < (void *)guard_p + GD_PAGESIZE) + { + fprintf(stderr, "guard: hit: %p\r\n", si->si_addr); + guard_err err = _focus_guard(); + if (err != guard_sound) { + fprintf(stderr, "guard: jump\r\n"); + siglongjmp(env_buffer, err); + } + } + else { + fprintf(stderr, "guard: weird hit: %p\r\n", si->si_addr); + return; } } @@ -152,69 +126,44 @@ guard( void *const *ret ) { - // Set globals. + guard_err err; + low = low_f; high = high_f; bounds= bounds_data; context = context_p; - const uint64_t *low_p = low_f(bounds_data, context_p); - const uint64_t *high_p = high_f(bounds_data, context_p); - - if (guard_p == NULL) { - guard_err focus_err = _focus_guard(); - if (focus_err != guard_sound && focus_err != guard_spent) { - fprintf(stderr, "guard: failed to install guard page\r\n"); - err = focus_err; - goto fail; - } + err = _focus_guard(); + if (guard_p == NULL && err != guard_sound && err != guard_spent) { + fprintf(stderr, "guard: failed to install\r\n"); + goto fail; } - if (_register_handler() != guard_sound) { - err = guard_weird; + if ((err = _register_handler()) != guard_sound) { + fprintf(stderr, "guard: failed to register handler\r\n"); goto fail; } - void *result; - if (sigsetjmp(env_buffer, 1) == 0) { - result = work_f(work_data, context_p); - *(void **)ret = result; + err = sigsetjmp(env_buffer, 1); + if (err == guard_start) { + *(void **)ret = work_f(work_data, context_p); return guard_sound; } else { - if (err != guard_sound) { - goto fail; - } + goto fail; } fail: - if (guard_p != NULL && - mprotect(guard_p, GD_PAGESIZE, PROT_READ | PROT_WRITE) == -1) + // Restore the previous signal handler. { - fprintf(stderr, "guard: failed to uninstall guard page\r\n"); + struct sigaction sa; + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = prev_sigsegv_handler; + sigemptyset(&sa.sa_mask); + sigaddset(&(sa.sa_mask), SIGSEGV); + sigaction(SIGSEGV, &sa, NULL); } - - // Restore the previous signal handler. - struct sigaction sa; - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = prev_sigsegv_handler; - sigemptyset(&sa.sa_mask); - sigaddset(&(sa.sa_mask), SIGSEGV); - sigaction(SIGSEGV, &sa, NULL); - switch (err) { - case guard_armor: - fprintf(stderr, "guard: armor error\r\n"); - break; - case guard_weird: - fprintf(stderr, "guard: weird error\r\n"); - break; - case guard_spent: - fprintf(stderr, "guard: spent error\r\n"); - break; - case guard_erupt: - fprintf(stderr, "guard: erupt error\r\n"); - break; - } + fprintf(stderr, "guard: fail: %d\r\n", err); return err; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 73547011..5d653893 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -5,11 +5,11 @@ typedef enum { - guard_sound = 0, // job's done - guard_armor = 1, // mprotect - guard_weird = 2, // strange state - guard_spent = 3, // out of memory (bail:meme) - guard_erupt = 4, // sigint + guard_start = 0, // setjmp + guard_sound = 1, // good/done + guard_armor = 2, // mprotect + guard_weird = 3, // strange state + guard_spent = 4, // out of memory (bail:meme) } guard_err; /** diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index 14763230..aba1b5e1 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -8,4 +8,3 @@ pub const GUARD_SOUND: u32 = guard_err_guard_sound; pub const GUARD_ARMOR: u32 = guard_err_guard_armor; pub const GUARD_WEIRD: u32 = guard_err_guard_weird; pub const GUARD_SPENT: u32 = guard_err_guard_spent; -pub const GUARD_ERUPT: u32 = guard_err_guard_erupt; From 116f9e8eef32d5d6ca2ae2aa7beee1cbfa93e7ed Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Thu, 8 Feb 2024 11:37:47 -0500 Subject: [PATCH 34/58] wip: minor --- rust/ares/Cargo.lock | 10 ++-------- rust/ares_crypto/Cargo.toml | 4 ++-- rust/ares_guard/c-src/guard.c | 4 ++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/rust/ares/Cargo.lock b/rust/ares/Cargo.lock index d7dac6b2..d9a3794a 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_crypto/Cargo.toml b/rust/ares_crypto/Cargo.toml index e2b895bf..2b893d2e 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" +assert_no_alloc = { path = "../rust-assert-no-alloc" } # use this when debugging requires allocation (e.g. eprintln) -# assert_no_alloc = {version="1.1.2", features=["warn_debug"]} +# assert_no_alloc = { path = "../rust-assert-no-alloc", features=["warn_debug"] } ibig = "0.3.6" # ed25519 diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 9b6d9840..6542d9ba 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -42,9 +42,9 @@ _focus_guard() guard_p = (uint64_t *)((uintptr_t)guard_p & ~(GD_PAGESIZE - 1)); const bool same = old_guard_p == guard_p; - const bool left = (high_p - low_p) > GD_PAGESIZE; + const bool left = (uint64_t)(high_p - low_p) > GD_PAGESIZE; if (same && !left) { - fprintf(stderr, "guard: spent: %p; left: %u\r\n", guard_p, left); + fprintf(stderr, "guard: spent: %p; left: %u\r\n", (void *)guard_p, left); return guard_spent; } else { From 4035867ea0cda3dd57dec411590065b2ea220f2b Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Thu, 8 Feb 2024 17:36:10 -0500 Subject: [PATCH 35/58] wip: wrap last block in `interpret` with guard --- rust/ares/src/interpreter.rs | 45 ++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 75559975..cb85b6a1 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -569,6 +569,23 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res *((*context).stack.push()) = NockWork::Done; }; + let low_f = &mut |context_p: *mut c_void| { + let bounds_ctx = unsafe { &mut *(context_p as *mut Context) }; + if bounds_ctx.stack.is_west() { + bounds_ctx.stack.get_stack_pointer() as *const c_ulonglong + } else { + bounds_ctx.stack.get_alloc_pointer() as *const c_ulonglong + } + }; + let high_f = &mut |context_p: *mut c_void| { + let bounds_ctx = unsafe { &mut *(context_p as *mut Context) }; + if bounds_ctx.stack.is_west() { + bounds_ctx.stack.get_alloc_pointer() as *const c_ulonglong + } else { + bounds_ctx.stack.get_stack_pointer() as *const c_ulonglong + } + }; + // DO NOT REMOVE THIS ASSERTION // // If you need to allocate for debugging, wrap the debugging code in @@ -582,22 +599,6 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) let nock = assert_no_alloc(|| { ensure_alloc_counters(|| { - let low_f = &mut |context_p: *mut c_void| { - let bounds_ctx = unsafe { &mut *(context_p as *mut Context) }; - if bounds_ctx.stack.is_west() { - bounds_ctx.stack.get_stack_pointer() as *const c_ulonglong - } else { - bounds_ctx.stack.get_alloc_pointer() as *const c_ulonglong - } - }; - let high_f = &mut |context_p: *mut c_void| { - let bounds_ctx = unsafe { &mut *(context_p as *mut Context) }; - if bounds_ctx.stack.is_west() { - bounds_ctx.stack.get_alloc_pointer() as *const c_ulonglong - } else { - bounds_ctx.stack.get_stack_pointer() as *const c_ulonglong - } - }; let work_f = &mut |context_p: *mut c_void| unsafe { let work_ctx = &mut *(context_p as *mut Context); push_formula(&mut work_ctx.stack, formula, true)?; @@ -1152,10 +1153,14 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res }) }); - match nock { - Ok(res) => Ok(res), - Err(err) => Err(exit(context, &snapshot, virtual_frame, err)), - } + let match_f = &mut |context_p: *mut c_void| unsafe { + let match_ctx = &mut *(context_p as *mut Context); + match nock { + Ok(res) => Ok(res), + Err(err) => Err(exit(match_ctx, &snapshot, virtual_frame, err)), + } + }; + call_with_guard(match_f, low_f, high_f, context as *mut Context) } fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result { From 1df75a2caba10d456f9e8a43c1e86b6e2b37d97f Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 14 Feb 2024 12:50:37 +0900 Subject: [PATCH 36/58] 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 | 984 +++++++++--------- rust/ares/src/serf.rs | 23 +- rust/ares_crypto/Cargo.toml | 4 +- rust/ares_guard/c-src/guard.c | 212 ++-- 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, 614 insertions(+), 654 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 02fe6dd5..274c0b57 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 8928d112..3156e0d2 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 186ba3d8..d7859caa 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)?; - - 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; - } + 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); - 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); - 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(); - mean_frame_push(stack, 0); - *stack.push() = NockWork::Ret; - push_formula(stack, res, true)?; - } + debug_assertions(stack, orig_subject); + debug_assertions(stack, res); + + break Ok(res); } - Todo2::RestoreSubject => { + NockWork::Ret => { + write_trace(context); + let stack = &mut context.stack; + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); - subject = vale.subject; - stack.pop::(); + 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, subject); debug_assertions(stack, 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::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)?; } - } - 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)?; + NockWork::Work1(once) => { + res = once.noun; + context.stack.pop::(); } - } - 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); - let stack = &mut context.stack; - if kale.tail { - stack.pop::(); + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); - // We could trace on 2 as well, but 2 only comes from Hoon via - // '.*', so we can assume it's never directly used to invoke - // jetted code. - if context.trace_info.is_some() { - if let Some(path) = context.cold.matches(stack, &mut res) { - append_trace(stack, path); - }; - }; + mean_frame_push(stack, 0); + *stack.push() = NockWork::Ret; + push_formula(stack, res, true)?; + } + } + Todo2::RestoreSubject => { + let stack = &mut context.stack; - subject = res; - push_formula(stack, formula, true)?; - } else { - kale.todo = Todo9::RestoreSubject; - kale.core = subject; - *stack.top() = NockWork::Work9(kale); + subject = vale.subject; + stack.pop::(); 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; + 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::(); - } - Err(err) => { - break Err(err); + } else { + // Cannot increment (Nock 4) a cell + break BAIL_EXIT; } } - } 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::(); + }, + 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)?; + } else { + // Test branch of Nock 6 must return 0 or 1 + break BAIL_EXIT; + } + } else { + // Test branch of Nock 6 must return a direct atom + break BAIL_EXIT; } - Err(err) => { - break Err(err); + } + }, + 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)?; } } - } else { - if dint.tail { + Todo7::RestoreSubject => { + subject = pose.subject; 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); + }, + 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)?; } } - } else { - if sint.tail { + Todo8::RestoreSubject => { + subject = pins.pin; context.stack.pop::(); - } else { - sint.todo = Todo11S::Done; - *context.stack.top() = NockWork::Work11S(sint); } - push_formula(&mut context.stack, sint.body, sint.tail)?; + }, + 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); + }; + }; + } + } 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); + } + } } - } - Todo11S::Done => { - if let Some(found) = - hint::match_post_nock(context, subject, sint.tag, None, sint.body, res) - { - res = found; + 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::(); + } + } } - 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))); + 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); } } - 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 { + 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::(); } - Right(cell) => { - res = cell.tail(); - context.scry_stack = scry_stack; + 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(error) => match error { - Error::Deterministic(_, trace) | Error::ScryCrashed(trace) => { - break Err(Error::ScryCrashed(trace)); + Err(err) => { + break Err(err); + } } - Error::NonDeterministic(_, _) => { - break Err(error); + } else { + if sint.tail { + context.stack.pop::(); + } else { + sint.todo = Todo11S::Done; + *context.stack.top() = NockWork::Work11S(sint); } - Error::ScryBlocked(_) => { - break BAIL_FAIL; + 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; + } } - } 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 8da4e927..70632f14 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 a52b57bb..3ee2ab1e 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 8aa71f49..4557041d 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; @@ -182,106 +170,58 @@ _register_handler(struct sigaction *prev_sa) } int32_t -_setup( - GuardState **gs_p, - const uintptr_t *const stack_pp, - const uintptr_t *const alloc_pp +guard( + callback f, + void *closure, + const uintptr_t *const s_pp, + const uintptr_t *const a_pp, + void ** ret ) { - GuardState *gs; - int32_t err = 0; + int32_t err = 0; + int32_t td_err; - assert(*gs_p == NULL); + 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)); - // 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; + guard_p = 0; + stack_pp = s_pp; + alloc_pp = a_pp; // Initialize the guard page - if ((err = _focus_guard(&(gs->guard_p), *stack_pp, *alloc_pp))) { + if ((err = _focus_guard())) { fprintf(stderr, "guard: setup: _focus_guard error\r\n"); - return err; + goto done; } // Register guard page signal handler - if ((err = _register_handler(&(gs->prev_sa)))) { + if ((err = _register_handler())) { 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))) { + 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); + // Clean up + if (guard_p != 0) { + td_err = _unmark_page((void *)guard_p); + } - fprintf(stderr, "guard: return\r\n"); - return err; + if (sigaction(SIGSEGV, &prev_sa, NULL)) { + fprintf(stderr, "guard: teardown: sigaction error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + td_err = guard_sigaction | errno; + } + + if (!err) { + err = td_err; } + + fprintf(stderr, "guard: return\r\n"); + return err; } diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index fd083438..ae238154 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 38e6c3c7..c10fb5aa 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 4b2efd2b..9ecf982c 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 00f6a924..0e2b0aa2 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 From 5efbc074e205cb7a768ea4519a5a6f2a3136b5c9 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 14 Feb 2024 19:06:04 +0900 Subject: [PATCH 37/58] Enable guard page + bail:meme with stack traces --- rust/ares/src/guard.rs | 14 ---- rust/ares/src/interpreter.rs | 1 + rust/ares_guard/c-src/guard.c | 107 ++++++++++++++++++--------- rust/ares_guard/c-src/guard.h | 9 +++ rust/rust-assert-no-alloc/src/lib.rs | 9 --- 5 files changed, 80 insertions(+), 60 deletions(-) diff --git a/rust/ares/src/guard.rs b/rust/ares/src/guard.rs index 843a4d8a..4ac09d43 100644 --- a/rust/ares/src/guard.rs +++ b/rust/ares/src/guard.rs @@ -43,15 +43,6 @@ impl<'closure> CCallback<'closure> { { 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, @@ -91,12 +82,7 @@ pub fn call_with_guard Result>( 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 diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index d7859caa..12bb2298 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -1191,6 +1191,7 @@ fn exit( Error::Deterministic(_, t) | Error::NonDeterministic(_, t) | Error::ScryCrashed(t) => { // Return $tang of traces let h = *(stack.local_noun_pointer(0)); + // XX: Small chance of clobbering something important after OOM? T(stack, &[h, t]) } }; diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 4557041d..ac37c059 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -15,10 +15,10 @@ #define GD_PAGE_MASK (GD_PAGE_SIZE - 1) #define GD_PAGE_ROUND_DOWN(foo) (foo & (~GD_PAGE_MASK)) -static uintptr_t guard_p; -static const uintptr_t *stack_pp; -static const uintptr_t *alloc_pp; -static jmp_buf env_buffer; +static uintptr_t guard_p = 0; +static const uintptr_t *stack_pp = NULL; +static const uintptr_t *alloc_pp = NULL; +static BufListNode *buffer_list = NULL; static struct sigaction prev_sa; static int32_t @@ -113,7 +113,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) if (sig != SIGSEGV) { fprintf(stderr, "guard: sig_handle: invalid signal\r\n"); // XX: do we even want to jump? if this is fatal error, maybe just die now - siglongjmp(env_buffer, guard_signal); + siglongjmp(buffer_list->buffer, guard_signal); } sig_addr = (uintptr_t)si->si_addr; @@ -126,7 +126,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) err = _focus_guard(); if (err) { fprintf(stderr, "guard: sig_handle: focus error\r\n"); - siglongjmp(env_buffer, err); + siglongjmp(buffer_list->buffer, err); } } else { @@ -159,7 +159,7 @@ _register_handler() // sigemptyset(&sa.sa_mask); // sigaddset(&(sa.sa_mask), SIGSEGV); - // XX: should assert that prev_sa doesn't have a handler in it, but it's not a pointer so non-trivial + // XX: should assert that prev_sa is uninitialized, 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)); @@ -177,51 +177,84 @@ guard( const uintptr_t *const a_pp, void ** ret ) { - int32_t err = 0; - int32_t td_err; + BufListNode *new_buffer; + int32_t err = 0; + int32_t td_err = 0; - 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)); + fprintf(stderr, "guard: setup: stack pointer at %p\r\n", (void *)(*s_pp)); + fprintf(stderr, "guard: setup: alloc pointer at %p\r\n", (void *)(*a_pp)); - guard_p = 0; - stack_pp = s_pp; - alloc_pp = a_pp; + if (guard_p == 0) { + assert(buffer_list == NULL); - // Initialize the guard page - if ((err = _focus_guard())) { - fprintf(stderr, "guard: setup: _focus_guard error\r\n"); - goto done; + 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 exit; + } + + // Register guard page signal handler + if ((err = _register_handler())) { + fprintf(stderr, "guard: setup _register_handler error\r\n"); + goto clean; + } + } else { + assert(buffer_list != NULL); } - // Register guard page signal handler - if ((err = _register_handler())) { - fprintf(stderr, "guard: setup: _register_handler error\r\n"); - goto done; + // Setup new longjmp buffer + new_buffer = (BufListNode *)malloc(sizeof(BufListNode)); + if (new_buffer == NULL) { + fprintf(stderr, "guard: malloc error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + err = guard_malloc | errno; + goto skip; } + new_buffer->next = buffer_list; + buffer_list = new_buffer; // Run given closure fprintf(stderr, "guard: run\r\n"); - if (!(err = sigsetjmp(env_buffer, 1))) { + if (!(err = sigsetjmp(buffer_list->buffer, 1))) { *ret = f(closure); } -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; - } + // Restore previous longjmp buffer + buffer_list = buffer_list->next; + free((void *)new_buffer); + +skip: + // If no more guarded closures, then... + if (buffer_list == NULL) { + // Remove new signal handler + if (sigaction(SIGSEGV, &prev_sa, NULL)) { + fprintf(stderr, "guard: sigaction error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + td_err = guard_sigaction | errno; + + if (!err) { + err = td_err; + } + } - if (!err) { - err = td_err; +clean: + // Unmark guard page + assert(guard_p != 0); + td_err = _unmark_page((void *)guard_p); + if (td_err) { + fprintf(stderr, "guard: unmark error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + if (!err) { + err = td_err; + } + } + guard_p = 0; } +exit: 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 ae238154..a2f610ff 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -1,8 +1,17 @@ #ifndef __GUARD_H__ #define __GUARD_H__ +#include #include +/** + * + */ +typedef struct _buf_list_node { + jmp_buf buffer; + struct _buf_list_node *next; +} BufListNode; + /** * Error codes and flags. * diff --git a/rust/rust-assert-no-alloc/src/lib.rs b/rust/rust-assert-no-alloc/src/lib.rs index 0e2b0aa2..8192d560 100644 --- a/rust/rust-assert-no-alloc/src/lib.rs +++ b/rust/rust-assert-no-alloc/src/lib.rs @@ -91,7 +91,6 @@ 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 @@ -149,14 +148,6 @@ pub fn reset_violation_count() { ALLOC_VIOLATION_COUNT.with(|c| c.set(0)); } -pub fn reset_counters() { - ALLOC_FORBID_COUNT.with(|c| c.set(0)); - ALLOC_PERMIT_COUNT.with(|c| c.set(0)); - - #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] - ALLOC_VIOLATION_COUNT.with(|c| c.set(0)); -} - From fcb1ba51da48e62b0003bd7115bcac902d59de3d Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 14 Feb 2024 10:51:13 -0800 Subject: [PATCH 38/58] wip: test `interpret_with_guard` --- rust/ares/src/guard.rs | 11 +- rust/ares/src/interpreter.rs | 961 +++++++++++++++++------------------ rust/ares/src/serf.rs | 5 +- 3 files changed, 486 insertions(+), 491 deletions(-) diff --git a/rust/ares/src/guard.rs b/rust/ares/src/guard.rs index 4ac09d43..5977495c 100644 --- a/rust/ares/src/guard.rs +++ b/rust/ares/src/guard.rs @@ -1,5 +1,5 @@ -use crate::interpreter::{Error, Mote, Result}; -use crate::noun::D; +use crate::interpreter::{interpret, Context, Error, Mote, Result}; +use crate::noun::{Noun, D}; use ares_guard::*; use assert_no_alloc::permit_alloc; use std::convert::TryFrom; @@ -98,3 +98,10 @@ pub fn call_with_guard Result>( } } } + +pub fn interpret_with_guard(context: &mut Context, eve: Noun, lyf: Noun) -> Result { + 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; + + call_with_guard(stack_pp, alloc_pp, &mut || interpret(context, eve, lyf)) +} diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 12bb2298..30bb51bf 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -1,7 +1,6 @@ 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; @@ -402,558 +401,546 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res // // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) let nock = assert_no_alloc(|| { - ensure_alloc_counters(|| { - let 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); + ensure_alloc_counters(|| unsafe { + push_formula(&mut context.stack, formula, true)?; - let stack = &mut context.stack; - debug_assertions(stack, orig_subject); - debug_assertions(stack, subject); - debug_assertions(stack, res); + loop { + let work: NockWork = *context.stack.top(); + match work { + NockWork::Done => { + write_trace(context); - stack.preserve(&mut context.cache); - stack.preserve(&mut context.cold); - stack.preserve(&mut context.warm); - stack.preserve(&mut res); - stack.frame_pop(); + let stack = &mut context.stack; + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); - debug_assertions(stack, orig_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(); - break Ok(res); - } - NockWork::Ret => { - write_trace(context); + 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; - 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); + 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; } - 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)?; + + 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)?; } - TodoCons::ComputeTail => { - cons.todo = TodoCons::Cons; - cons.head = res; - *context.stack.top() = NockWork::WorkCons(cons); - push_formula(&mut context.stack, cons.tail, 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)?; + } } - TodoCons::Cons => { + Todo2::RestoreSubject => { let stack = &mut context.stack; - res = T(stack, &[cons.head, res]); + + subject = vale.subject; stack.pop::(); + + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); } - }, - NockWork::Work0(zero) => { - if let Ok(noun) = subject.slot_atom(zero.axis) { - res = noun; + } + } + 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 { - // Axis invalid for input Noun + // Cannot increment (Nock 4) a cell + break BAIL_EXIT; + } + } + }, + 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)?; + } else { + // Test branch of Nock 6 must return 0 or 1 + break BAIL_EXIT; + } + } else { + // Test branch of Nock 6 must return a direct atom break BAIL_EXIT; } } - NockWork::Work1(once) => { - res = once.noun; + }, + 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::Work2(mut vale) => { - if (*terminator).load(Ordering::Relaxed) { - break BAIL_INTR; + }, + 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()); + } + } + } + }; - 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 { + if kale.tail { stack.pop::(); - subject = vale.subject; - push_formula(stack, res, 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); + }; + }; + + subject = res; + push_formula(stack, formula, true)?; } else { - vale.todo = Todo2::RestoreSubject; - std::mem::swap(&mut vale.subject, &mut subject); - *stack.top() = NockWork::Work2(vale); + 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, res, true)?; + 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); + }; + }; } - } - 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); - } - } - } - 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 + // Axis into core must be atom break BAIL_EXIT; } } - }, - 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 => { + Todo9::RestoreSubject => { let stack = &mut context.stack; + + subject = kale.core; 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; - } - } 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::(); + + debug_assertions(stack, orig_subject); + debug_assertions(stack, subject); + debug_assertions(stack, res); } - }, - 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)?; + } + } + 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)?; } - 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)?; - } + Todo10::ComputePatch => { + diet.todo = Todo10::Edit; + diet.tree = res; + *context.stack.top() = NockWork::Work10(diet); + push_formula(&mut context.stack, diet.patch, false)?; } - Todo8::RestoreSubject => { - subject = pins.pin; + Todo10::Edit => { + res = edit( + &mut context.stack, + diet.axis.as_bitslice(), + res, + diet.tree, + ); 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); - }; - }; - } - } else { - // Axis into core must be atom - break BAIL_EXIT; + } + } + 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); } } - 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); - } + } else { + dint.todo = Todo11D::ComputeResult; + *context.stack.top() = NockWork::Work11D(dint); + push_formula(&mut context.stack, dint.hint, false)?; } } - 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)?; + 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); + } } - Todo10::Edit => { - res = edit( - &mut context.stack, - diet.axis.as_bitslice(), - res, - diet.tree, - ); + } else { + if dint.tail { 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; + dint.todo = Todo11D::Done; + dint.hint = res; *context.stack.top() = NockWork::Work11D(dint); - push_formula(&mut context.stack, dint.hint, false)?; } + push_formula(&mut context.stack, dint.body, dint.tail)?; } - 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::(); + } + Todo11D::Done => { + if let Some(found) = hint::match_post_nock( + context, + subject, + dint.tag, + Some(dint.hint), + dint.body, + res, + ) { + res = found; } - }, - 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::(); + } + }, + 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::(); - } else { - sint.todo = Todo11S::Done; - *context.stack.top() = NockWork::Work11S(sint); } - push_formula(&mut context.stack, sint.body, sint.tail)?; + Err(err) => { + break Err(err); + } } - } - Todo11S::Done => { - if let Some(found) = hint::match_post_nock( - context, subject, sint.tag, None, sint.body, res, - ) { - res = found; + } else { + if sint.tail { + context.stack.pop::(); + } else { + sint.todo = Todo11S::Done; + *context.stack.top() = NockWork::Work11S(sint); } - context.stack.pop::(); + push_formula(&mut context.stack, sint.body, sint.tail)?; } - }, - 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)?; + } + Todo11S::Done => { + if let Some(found) = hint::match_post_nock( + context, subject, sint.tag, None, sint.body, res, + ) { + res = found; } - 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)); + 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))); } - Error::NonDeterministic(_, _) => { - break Err(error); + } + 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))); } - Error::ScryBlocked(_) => { - break BAIL_FAIL; + Right(cell) => { + res = cell.tail(); + context.scry_stack = scry_stack; + context.stack.pop::(); } }, - } - } else { - // No scry handler - break BAIL_EXIT; + }, + 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) + } + }, + }; + } }) }); diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 70632f14..512445a0 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,4 +1,5 @@ use crate::hamt::Hamt; +use crate::guard::interpret_with_guard; use crate::interpreter; use crate::interpreter::{inc, interpret, Error, Mote}; use crate::jets::cold::Cold; @@ -143,7 +144,7 @@ impl Context { snapshot: Option, constant_hot_state: &[HotEntry], ) -> Self { - let mut stack = NockStack::new(2048 << 10 << 10, 0); + let mut stack = NockStack::new(128 << 10 << 10, 0); let newt = Newt::new(); let cache = Hamt::::new(&mut stack); @@ -402,7 +403,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]); - interpret(&mut context.nock_context, sub, fol) + interpret_with_guard(&mut context.nock_context, sub, fol) } fn peek(context: &mut Context, ovo: Noun) -> Noun { From 1980ca7fd698da8b8a50c195001a6b335a9aef2d Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 14 Feb 2024 17:05:33 -0500 Subject: [PATCH 39/58] wip: use only one `jmp_buf`; add `NockStackSnapshot` --- rust/ares/src/guard.rs | 9 +++-- rust/ares/src/mem.rs | 21 +++++++++++ rust/ares_guard/c-src/guard.c | 65 ++++++++--------------------------- rust/ares_guard/c-src/guard.h | 12 ++----- 4 files changed, 42 insertions(+), 65 deletions(-) diff --git a/rust/ares/src/guard.rs b/rust/ares/src/guard.rs index 5977495c..c9cc4ed9 100644 --- a/rust/ares/src/guard.rs +++ b/rust/ares/src/guard.rs @@ -65,8 +65,8 @@ impl<'closure> CCallback<'closure> { } pub fn call_with_guard Result>( - stack_pp: *const *const u64, - alloc_pp: *const *const u64, + stack_pp: *const *mut u64, + alloc_pp: *const *mut u64, closure: &mut F, ) -> Result { let cb = CCallback::new(closure); @@ -100,8 +100,7 @@ pub fn call_with_guard Result>( } pub fn interpret_with_guard(context: &mut Context, eve: Noun, lyf: Noun) -> Result { - 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 stack_pp = context.stack.get_stack_pointer_pointer() as *const *mut u64; + let alloc_pp = context.stack.get_alloc_pointer_pointer() as *const *mut u64; call_with_guard(stack_pp, alloc_pp, &mut || interpret(context, eve, lyf)) } diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 210e8976..e6b50a26 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -32,6 +32,13 @@ fn indirect_raw_size(atom: IndirectAtom) -> usize { atom.size() + 2 } +#[allow(dead_code)] +pub struct NockStackSnapshot { + frame_pointer: *mut u64, + stack_pointer: *mut u64, + alloc_pointer: *mut u64, +} + /** A stack for Nock computation, which supports stack allocation and delimited copying collection * for returned nouns */ @@ -87,6 +94,20 @@ impl NockStack { } } + pub fn save(&self) -> NockStackSnapshot { + NockStackSnapshot { + frame_pointer: self.frame_pointer, + stack_pointer: self.stack_pointer, + alloc_pointer: self.alloc_pointer, + } + } + + pub fn restore(&mut self, saved: &NockStackSnapshot) { + self.frame_pointer = saved.frame_pointer; + self.stack_pointer = saved.stack_pointer; + self.alloc_pointer = saved.alloc_pointer; + } + /** Resets the NockStack but flipping the top-frame polarity and unsetting PC. Sets the alloc * pointer to the "previous" alloc pointer stored in the top frame to keep things "preserved" * from the top frame. This allows us to do a copying GC on the top frame without erroneously diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index ac37c059..28a6bfff 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -18,7 +18,7 @@ static uintptr_t guard_p = 0; static const uintptr_t *stack_pp = NULL; static const uintptr_t *alloc_pp = NULL; -static BufListNode *buffer_list = NULL; +static jmp_buf env_buffer; static struct sigaction prev_sa; static int32_t @@ -113,7 +113,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) if (sig != SIGSEGV) { fprintf(stderr, "guard: sig_handle: invalid signal\r\n"); // XX: do we even want to jump? if this is fatal error, maybe just die now - siglongjmp(buffer_list->buffer, guard_signal); + siglongjmp(env_buffer, guard_signal); } sig_addr = (uintptr_t)si->si_addr; @@ -126,7 +126,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) err = _focus_guard(); if (err) { fprintf(stderr, "guard: sig_handle: focus error\r\n"); - siglongjmp(buffer_list->buffer, err); + siglongjmp(env_buffer, err); } } else { @@ -173,11 +173,10 @@ int32_t guard( callback f, void *closure, - const uintptr_t *const s_pp, - const uintptr_t *const a_pp, + const uintptr_t *s_pp, + const uintptr_t *a_pp, void ** ret ) { - BufListNode *new_buffer; int32_t err = 0; int32_t td_err = 0; @@ -185,8 +184,6 @@ guard( fprintf(stderr, "guard: setup: alloc pointer at %p\r\n", (void *)(*a_pp)); if (guard_p == 0) { - assert(buffer_list == NULL); - stack_pp = s_pp; alloc_pp = a_pp; @@ -201,58 +198,26 @@ guard( fprintf(stderr, "guard: setup _register_handler error\r\n"); goto clean; } - } else { - assert(buffer_list != NULL); - } - - // Setup new longjmp buffer - new_buffer = (BufListNode *)malloc(sizeof(BufListNode)); - if (new_buffer == NULL) { - fprintf(stderr, "guard: malloc error\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - err = guard_malloc | errno; - goto skip; } - new_buffer->next = buffer_list; - buffer_list = new_buffer; // Run given closure fprintf(stderr, "guard: run\r\n"); - if (!(err = sigsetjmp(buffer_list->buffer, 1))) { + if (!(err = sigsetjmp(env_buffer, 1))) { *ret = f(closure); } - // Restore previous longjmp buffer - buffer_list = buffer_list->next; - free((void *)new_buffer); - -skip: - // If no more guarded closures, then... - if (buffer_list == NULL) { - // Remove new signal handler - if (sigaction(SIGSEGV, &prev_sa, NULL)) { - fprintf(stderr, "guard: sigaction error\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - td_err = guard_sigaction | errno; - - if (!err) { - err = td_err; - } - } - clean: - // Unmark guard page - assert(guard_p != 0); - td_err = _unmark_page((void *)guard_p); - if (td_err) { - fprintf(stderr, "guard: unmark error\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - if (!err) { - err = td_err; - } + // Unmark guard page + assert(guard_p != 0); + td_err = _unmark_page((void *)guard_p); + if (td_err) { + fprintf(stderr, "guard: unmark error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + if (!err) { + err = td_err; } - guard_p = 0; } + guard_p = 0; exit: fprintf(stderr, "guard: return\r\n"); diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index a2f610ff..06d36981 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -4,14 +4,6 @@ #include #include -/** - * - */ -typedef struct _buf_list_node { - jmp_buf buffer; - struct _buf_list_node *next; -} BufListNode; - /** * Error codes and flags. * @@ -60,8 +52,8 @@ int32_t guard( callback f, void *closure, - const uintptr_t *const s_pp, - const uintptr_t *const a_pp, + const uintptr_t *s_pp, + const uintptr_t *a_pp, void **ret ); From 8bcbf6adca5266173423f29217d1395d8456ee44 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Thu, 15 Feb 2024 14:32:17 -0500 Subject: [PATCH 40/58] Revert "wip: use only one `jmp_buf`; add `NockStackSnapshot`" This reverts commit 1980ca7fd698da8b8a50c195001a6b335a9aef2d. --- rust/ares/src/guard.rs | 9 ++--- rust/ares/src/mem.rs | 21 ----------- rust/ares_guard/c-src/guard.c | 65 +++++++++++++++++++++++++++-------- rust/ares_guard/c-src/guard.h | 12 +++++-- 4 files changed, 65 insertions(+), 42 deletions(-) diff --git a/rust/ares/src/guard.rs b/rust/ares/src/guard.rs index c9cc4ed9..5977495c 100644 --- a/rust/ares/src/guard.rs +++ b/rust/ares/src/guard.rs @@ -65,8 +65,8 @@ impl<'closure> CCallback<'closure> { } pub fn call_with_guard Result>( - stack_pp: *const *mut u64, - alloc_pp: *const *mut u64, + stack_pp: *const *const u64, + alloc_pp: *const *const u64, closure: &mut F, ) -> Result { let cb = CCallback::new(closure); @@ -100,7 +100,8 @@ pub fn call_with_guard Result>( } pub fn interpret_with_guard(context: &mut Context, eve: Noun, lyf: Noun) -> Result { - let stack_pp = context.stack.get_stack_pointer_pointer() as *const *mut u64; - let alloc_pp = context.stack.get_alloc_pointer_pointer() as *const *mut u64; + 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; + call_with_guard(stack_pp, alloc_pp, &mut || interpret(context, eve, lyf)) } diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index e6b50a26..210e8976 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -32,13 +32,6 @@ fn indirect_raw_size(atom: IndirectAtom) -> usize { atom.size() + 2 } -#[allow(dead_code)] -pub struct NockStackSnapshot { - frame_pointer: *mut u64, - stack_pointer: *mut u64, - alloc_pointer: *mut u64, -} - /** A stack for Nock computation, which supports stack allocation and delimited copying collection * for returned nouns */ @@ -94,20 +87,6 @@ impl NockStack { } } - pub fn save(&self) -> NockStackSnapshot { - NockStackSnapshot { - frame_pointer: self.frame_pointer, - stack_pointer: self.stack_pointer, - alloc_pointer: self.alloc_pointer, - } - } - - pub fn restore(&mut self, saved: &NockStackSnapshot) { - self.frame_pointer = saved.frame_pointer; - self.stack_pointer = saved.stack_pointer; - self.alloc_pointer = saved.alloc_pointer; - } - /** Resets the NockStack but flipping the top-frame polarity and unsetting PC. Sets the alloc * pointer to the "previous" alloc pointer stored in the top frame to keep things "preserved" * from the top frame. This allows us to do a copying GC on the top frame without erroneously diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 28a6bfff..ac37c059 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -18,7 +18,7 @@ static uintptr_t guard_p = 0; static const uintptr_t *stack_pp = NULL; static const uintptr_t *alloc_pp = NULL; -static jmp_buf env_buffer; +static BufListNode *buffer_list = NULL; static struct sigaction prev_sa; static int32_t @@ -113,7 +113,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) if (sig != SIGSEGV) { fprintf(stderr, "guard: sig_handle: invalid signal\r\n"); // XX: do we even want to jump? if this is fatal error, maybe just die now - siglongjmp(env_buffer, guard_signal); + siglongjmp(buffer_list->buffer, guard_signal); } sig_addr = (uintptr_t)si->si_addr; @@ -126,7 +126,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) err = _focus_guard(); if (err) { fprintf(stderr, "guard: sig_handle: focus error\r\n"); - siglongjmp(env_buffer, err); + siglongjmp(buffer_list->buffer, err); } } else { @@ -173,10 +173,11 @@ int32_t guard( callback f, void *closure, - const uintptr_t *s_pp, - const uintptr_t *a_pp, + const uintptr_t *const s_pp, + const uintptr_t *const a_pp, void ** ret ) { + BufListNode *new_buffer; int32_t err = 0; int32_t td_err = 0; @@ -184,6 +185,8 @@ guard( fprintf(stderr, "guard: setup: alloc pointer at %p\r\n", (void *)(*a_pp)); if (guard_p == 0) { + assert(buffer_list == NULL); + stack_pp = s_pp; alloc_pp = a_pp; @@ -198,26 +201,58 @@ guard( fprintf(stderr, "guard: setup _register_handler error\r\n"); goto clean; } + } else { + assert(buffer_list != NULL); + } + + // Setup new longjmp buffer + new_buffer = (BufListNode *)malloc(sizeof(BufListNode)); + if (new_buffer == NULL) { + fprintf(stderr, "guard: malloc error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + err = guard_malloc | errno; + goto skip; } + new_buffer->next = buffer_list; + buffer_list = new_buffer; // Run given closure fprintf(stderr, "guard: run\r\n"); - if (!(err = sigsetjmp(env_buffer, 1))) { + if (!(err = sigsetjmp(buffer_list->buffer, 1))) { *ret = f(closure); } + // Restore previous longjmp buffer + buffer_list = buffer_list->next; + free((void *)new_buffer); + +skip: + // If no more guarded closures, then... + if (buffer_list == NULL) { + // Remove new signal handler + if (sigaction(SIGSEGV, &prev_sa, NULL)) { + fprintf(stderr, "guard: sigaction error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + td_err = guard_sigaction | errno; + + if (!err) { + err = td_err; + } + } + clean: - // Unmark guard page - assert(guard_p != 0); - td_err = _unmark_page((void *)guard_p); - if (td_err) { - fprintf(stderr, "guard: unmark error\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - if (!err) { - err = td_err; + // Unmark guard page + assert(guard_p != 0); + td_err = _unmark_page((void *)guard_p); + if (td_err) { + fprintf(stderr, "guard: unmark error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + if (!err) { + err = td_err; + } } + guard_p = 0; } - guard_p = 0; exit: fprintf(stderr, "guard: return\r\n"); diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 06d36981..a2f610ff 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -4,6 +4,14 @@ #include #include +/** + * + */ +typedef struct _buf_list_node { + jmp_buf buffer; + struct _buf_list_node *next; +} BufListNode; + /** * Error codes and flags. * @@ -52,8 +60,8 @@ int32_t guard( callback f, void *closure, - const uintptr_t *s_pp, - const uintptr_t *a_pp, + const uintptr_t *const s_pp, + const uintptr_t *const a_pp, void **ret ); From 77ec530c7c564fa5e87689bc1043509e44456ad3 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Thu, 15 Feb 2024 14:32:37 -0500 Subject: [PATCH 41/58] Revert "wip: test `interpret_with_guard`" This reverts commit fcb1ba51da48e62b0003bd7115bcac902d59de3d. --- rust/ares/src/guard.rs | 11 +- rust/ares/src/interpreter.rs | 961 ++++++++++++++++++----------------- rust/ares/src/serf.rs | 5 +- 3 files changed, 491 insertions(+), 486 deletions(-) diff --git a/rust/ares/src/guard.rs b/rust/ares/src/guard.rs index 5977495c..4ac09d43 100644 --- a/rust/ares/src/guard.rs +++ b/rust/ares/src/guard.rs @@ -1,5 +1,5 @@ -use crate::interpreter::{interpret, Context, Error, Mote, Result}; -use crate::noun::{Noun, D}; +use crate::interpreter::{Error, Mote, Result}; +use crate::noun::D; use ares_guard::*; use assert_no_alloc::permit_alloc; use std::convert::TryFrom; @@ -98,10 +98,3 @@ pub fn call_with_guard Result>( } } } - -pub fn interpret_with_guard(context: &mut Context, eve: Noun, lyf: Noun) -> Result { - 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; - - call_with_guard(stack_pp, alloc_pp, &mut || interpret(context, eve, lyf)) -} diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 30bb51bf..12bb2298 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; @@ -401,546 +402,558 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res // // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) let nock = assert_no_alloc(|| { - ensure_alloc_counters(|| unsafe { - push_formula(&mut context.stack, formula, true)?; + 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); + 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(); + 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); + 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)?; + break Ok(res); } - 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 => { + NockWork::Ret => { + write_trace(context); + 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; + 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); } - - 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)?; + 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)?; } - 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)?; - } + TodoCons::ComputeTail => { + cons.todo = TodoCons::Cons; + cons.head = res; + *context.stack.top() = NockWork::WorkCons(cons); + push_formula(&mut context.stack, cons.tail, false)?; } - Todo2::RestoreSubject => { + TodoCons::Cons => { let stack = &mut context.stack; - - subject = vale.subject; + res = T(stack, &[cons.head, res]); stack.pop::(); - - debug_assertions(stack, orig_subject); - debug_assertions(stack, subject); - debug_assertions(stack, 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(); + }, + NockWork::Work0(zero) => { + if let Ok(noun) = subject.slot_atom(zero.axis) { + res = noun; context.stack.pop::(); } else { - // Cannot increment (Nock 4) a cell - break BAIL_EXIT; - } - } - }, - 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)?; - } else { - // Test branch of Nock 6 must return 0 or 1 - break BAIL_EXIT; - } - } else { - // Test branch of Nock 6 must return a direct atom + // Axis invalid for input Noun 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; + NockWork::Work1(once) => { + res = once.noun; 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)?; + NockWork::Work2(mut vale) => { + if (*terminator).load(Ordering::Relaxed) { + break BAIL_INTR; } - 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 kale.tail { + if vale.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)?; + subject = vale.subject; + push_formula(stack, res, true)?; } else { - kale.todo = Todo9::RestoreSubject; - kale.core = subject; - *stack.top() = NockWork::Work9(kale); + 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); - 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); - }; - }; + 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); + } + } + } + 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 { - // Axis into core must be atom + // Cannot increment (Nock 4) a cell break BAIL_EXIT; } } - Todo9::RestoreSubject => { + }, + 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; - - subject = kale.core; + let saved_value_ptr = &mut five.left; + res = if unifying_equality(stack, &mut res, saved_value_ptr) { + D(0) + } else { + D(1) + }; 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)?; + }, + 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; + } + } else { + // Test branch of Nock 6 must return a direct atom + break BAIL_EXIT; + } } - Todo10::ComputePatch => { - diet.todo = Todo10::Edit; - diet.tree = res; - *context.stack.top() = NockWork::Work10(diet); - push_formula(&mut context.stack, diet.patch, false)?; + }, + 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)?; } - Todo10::Edit => { - res = edit( - &mut context.stack, - diet.axis.as_bitslice(), - res, - diet.tree, - ); + 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::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); + }, + 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); + }; + }; + } + } else { + // Axis into core must be atom + break BAIL_EXIT; } } - } else { - dint.todo = Todo11D::ComputeResult; - *context.stack.top() = NockWork::Work11D(dint); - push_formula(&mut context.stack, dint.hint, false)?; + 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); + } } } - 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::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)?; } - } else { - if dint.tail { + 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::(); - } 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; + 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)?; + } } - 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::(); + 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); + } } - 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)?; } - } else { - if sint.tail { - context.stack.pop::(); + } + 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 { - sint.todo = Todo11S::Done; - *context.stack.top() = NockWork::Work11S(sint); + 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)?; } - 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; + Todo11S::Done => { + if let Some(found) = hint::match_post_nock( + context, subject, sint.tag, None, sint.body, res, + ) { + res = found; + } + context.stack.pop::(); } - 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))); + }, + 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) => { - res = cell.tail(); - context.scry_stack = scry_stack; - context.stack.pop::(); + 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; - } - }, + 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; } - } else { - // No scry handler - break BAIL_EXIT; } - } - }, - }; - } + }, + }; + } + }; + + call_with_guard(stack_pp, alloc_pp, work_f) }) }); diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 512445a0..70632f14 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,5 +1,4 @@ use crate::hamt::Hamt; -use crate::guard::interpret_with_guard; use crate::interpreter; use crate::interpreter::{inc, interpret, Error, Mote}; use crate::jets::cold::Cold; @@ -144,7 +143,7 @@ impl Context { snapshot: Option, constant_hot_state: &[HotEntry], ) -> Self { - let mut stack = NockStack::new(128 << 10 << 10, 0); + let mut stack = NockStack::new(2048 << 10 << 10, 0); let newt = Newt::new(); let cache = Hamt::::new(&mut stack); @@ -403,7 +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]); - interpret_with_guard(&mut context.nock_context, sub, fol) + interpret(&mut context.nock_context, sub, fol) } fn peek(context: &mut Context, ovo: Noun) -> Noun { From b81ae6e406a31ccf8f09974d25f4c78997d573b0 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Fri, 16 Feb 2024 13:23:17 -0500 Subject: [PATCH 42/58] guard: documentation --- rust/ares_guard/c-src/guard.c | 31 ++++++------ rust/ares_guard/c-src/guard.h | 88 +++++++++++++++++++++-------------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index ac37c059..8423cecc 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -18,10 +18,10 @@ static uintptr_t guard_p = 0; static const uintptr_t *stack_pp = NULL; static const uintptr_t *alloc_pp = NULL; -static BufListNode *buffer_list = NULL; +static GD_buflistnode *buffer_list = NULL; static struct sigaction prev_sa; -static int32_t +static guard_result _prot_page(void *address, int prot) { if (mprotect(address, GD_PAGE_SIZE, prot)) { @@ -33,13 +33,13 @@ _prot_page(void *address, int prot) return 0; } -static int32_t +static guard_result _mark_page(void *address) { return _prot_page(address, PROT_NONE); } -static int32_t +static guard_result _unmark_page(void *address) { return _prot_page(address, PROT_READ | PROT_WRITE); @@ -48,14 +48,14 @@ _unmark_page(void *address) // 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 +static guard_result _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; + guard_result 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); @@ -104,7 +104,7 @@ static void _signal_handler(int sig, siginfo_t *si, void *unused) { uintptr_t sig_addr; - int32_t err = 0; + guard_result err = 0; assert(guard_p); @@ -112,8 +112,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) if (sig != SIGSEGV) { fprintf(stderr, "guard: sig_handle: invalid signal\r\n"); - // XX: do we even want to jump? if this is fatal error, maybe just die now - siglongjmp(buffer_list->buffer, guard_signal); + assert(0); } sig_addr = (uintptr_t)si->si_addr; @@ -143,7 +142,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) } } -static int32_t +static guard_result _register_handler() { struct sigaction sa; @@ -169,17 +168,17 @@ _register_handler() return 0; } -int32_t +guard_result guard( - callback f, + void *(*f)(void *), void *closure, const uintptr_t *const s_pp, const uintptr_t *const a_pp, void ** ret ) { - BufListNode *new_buffer; - int32_t err = 0; - int32_t td_err = 0; + GD_buflistnode *new_buffer; + guard_result err = 0; + guard_result td_err = 0; fprintf(stderr, "guard: setup: stack pointer at %p\r\n", (void *)(*s_pp)); fprintf(stderr, "guard: setup: alloc pointer at %p\r\n", (void *)(*a_pp)); @@ -206,7 +205,7 @@ guard( } // Setup new longjmp buffer - new_buffer = (BufListNode *)malloc(sizeof(BufListNode)); + new_buffer = (GD_buflistnode *)malloc(sizeof(GD_buflistnode)); if (new_buffer == NULL) { fprintf(stderr, "guard: malloc error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index a2f610ff..3cef93c6 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -5,60 +5,76 @@ #include /** - * + * Linked list stack of jump buffers. */ -typedef struct _buf_list_node { - jmp_buf buffer; - struct _buf_list_node *next; -} BufListNode; +typedef struct GD_buflistnode GD_buflistnode; +struct GD_buflistnode { + jmp_buf buffer; + struct GD_buflistnode *next; +}; /** - * Error codes and flags. + * Return codes and flags. * * The flags are bitwise added to the errno of their respective errors. */ typedef enum { + guard_success = 0, // successful return guard_null = 1, // null stack or alloc pointer - guard_signal, // invalid signal - guard_oom, // OOM + guard_signal = 2, // invalid signal + guard_oom = 3, // out of memory guard_malloc = 0x10000000, // malloc error flag guard_mprotect = 0x20000000, // mprotect error flag guard_sigaction = 0x40000000, // sigaction error flag -} guard_err; - -typedef void *(*callback)(void *); +} guard_result; /** - * Execute the given closure `f` within the memory arena between the - * `stack` and `alloc` pointers, with guard page protection. Write either - * `f`'s succesful result or a `guard_err` to the given `ret` pointer. + * @brief Executes the given callback function `f` within the memory arena + * between the stack and allocation pointers pointed to by `s_pp` and `a_pp`, + * with guard page protection. If `f`'s execution succeeds, its result is + * written to the return pointer `*ret`. If `f`'s execution triggers an + * out of memory error or any other `guard_result`, the `guard_result` is + * returned and `*ret` is left empty. In either case, cleanup is performed + * before returning. + * + * Definitions: + * - A guard page is marked `PROT_NONE`. * - * Memory - * ------ - * The free memory arena between the `stack` and `alloc` pointers is part of a - * NockStack frame, which may either face east or west. If the frame faces - * east, the `stack` pointer will be greater than the `alloc` pointer. If it - * faces west, the `stack` pointer will be less than the `alloc` pointer. + * Assumptions: + * - `NockStack` pages are marked `PROT_READ|PROT_WRITE` by default. + * - All memory access patterns are outside-in. + * - Callback functions are compatible with the C ABI. + * - `NockStack` stack and allocation pointer locations are fixed. + * - The caller is responsible for return value memory allocation. + * - The caller is responsible for managing any external state the callback + * function may mutate. + * - The callback function may be interrupted in the case of memory exhaustion + * or other `guard_result` error (failure to `mprotect`, `malloc`, etc.). + * - `SIGSEGV` signals are expected to be raised only on guard page accesses. * - * All the pages in the memory arena are marked clean (`PROT_READ | PROT_WRITE`) - * by default, with the exception of a single guard page in the middle of the - * arena, which is marked with `PROT_NONE`. + * Invariants: + * - A single guard page is installed and maintained in the approximate center + * until `crate::guard::call_with_guard` returns. + * - A return value is only written to `*ret` on successful callback execution. + * - A `guard_result` is returned, excepting panics or negative assertions. * - * Guard - * ----- - * This function protects the free memory arena between the `stack` and `alloc` - * pointers with a guard page. A guard page is simply a single page of memory - * which is marked with `PROT_NONE`. Since all other pages are marked clean by - * default, a SIGSEGV will only be raised if the `f` function attempts to write - * to the guard page. When it does, the signal handler will attempt to re-center - * the guard page in the remaining free space left in the arena. If there is no - * more free space, then memory exhaustion has occurred and the `guard_spent` - * error will be written to the `ret` pointer. The caller is then responsible - * for handling this error and aborting with a `bail:meme`. + * Enhancements: + * - Use only a single, static jump buffer variable instead of a linked list. + * We currently use a linked list of jump buffers because we don't have a + * function for preserving stack traces across `crate::interpreter::interpret` + * calls. + * + * @param f The callback function to execute. + * @param closure A pointer to the closure data for the callback function. + * @param s_pp A pointer to the stack pointer location. + * @param a_pp A pointer to the allocation pointer location. + * @param ret A pointer to a location where the callback's result can be stored. + * + * @return A `guard_result` return code. */ -int32_t +guard_result guard( - callback f, + void *(*f)(void *), void *closure, const uintptr_t *const s_pp, const uintptr_t *const a_pp, From 4cfd3ca1e7f81093d557117040d54224cf490a88 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Fri, 16 Feb 2024 13:32:11 -0500 Subject: [PATCH 43/58] guard: comment debug `fprintf` calls --- rust/ares_guard/c-src/guard.c | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 8423cecc..4ec25659 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -18,7 +18,7 @@ static uintptr_t guard_p = 0; static const uintptr_t *stack_pp = NULL; static const uintptr_t *alloc_pp = NULL; -static GD_buflistnode *buffer_list = NULL; +static GD_buflistnode *buffer_list = NULL; static struct sigaction prev_sa; static guard_result @@ -57,25 +57,25 @@ _focus_guard() uintptr_t new_guard_p; guard_result 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); + // 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"); + // fprintf(stderr, "guard: focus: stack and alloc pointers equal\r\n"); return guard_oom; } - fprintf(stderr, "guard: focus: old guard = %p\r\n", (void *)old_guard_p); + // fprintf(stderr, "guard: focus: old guard = %p\r\n", (void *)old_guard_p); // Compute new guard page // XX: Should we also check for new_guard_p < min(stack_p, alloc_p)? new_guard_p = GD_PAGE_ROUND_DOWN((stack_p + alloc_p) / 2); - fprintf(stderr, "guard: focus: new guard = %p\r\n", (void *)new_guard_p); + // 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"); + // fprintf(stderr, "guard: focus: OOM\r\n"); return guard_oom; } @@ -86,7 +86,7 @@ _focus_guard() } // Update guard page tracker - fprintf(stderr, "guard: focus: installed guard page at %p\r\n", (void *)new_guard_p); + // fprintf(stderr, "guard: focus: installed guard at %p\r\n", (void *)new_guard_p); guard_p = new_guard_p; // Unmark the old guard page (if there is one) @@ -108,28 +108,28 @@ _signal_handler(int sig, siginfo_t *si, void *unused) assert(guard_p); - fprintf(stderr, "guard: sig_handle: %d received\r\n", sig); + // fprintf(stderr, "guard: handler: %d received\r\n", sig); if (sig != SIGSEGV) { - fprintf(stderr, "guard: sig_handle: invalid signal\r\n"); + fprintf(stderr, "guard: handler: invalid signal\r\n"); assert(0); } sig_addr = (uintptr_t)si->si_addr; - fprintf(stderr, "guard: SIGSEGV address = %p\r\n", (void *)sig_addr); + // fprintf(stderr, "guard: SIGSEGV address = %p\r\n", (void *)sig_addr); if (sig_addr >= guard_p && sig_addr < guard_p + GD_PAGE_SIZE) { - fprintf(stderr, "guard: hit: %p\r\n", si->si_addr); + // fprintf(stderr, "guard: hit: %p\r\n", si->si_addr); err = _focus_guard(); if (err) { - fprintf(stderr, "guard: sig_handle: focus error\r\n"); + fprintf(stderr, "guard: handler: focus error\r\n"); siglongjmp(buffer_list->buffer, err); } } else { - fprintf(stderr, "guard: page at %p miss\r\n", (void *)guard_p); + // 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); @@ -180,8 +180,8 @@ guard( guard_result err = 0; guard_result td_err = 0; - fprintf(stderr, "guard: setup: stack pointer at %p\r\n", (void *)(*s_pp)); - fprintf(stderr, "guard: setup: alloc pointer at %p\r\n", (void *)(*a_pp)); + // fprintf(stderr, "guard: stack pointer at %p\r\n", (void *)(*s_pp)); + // fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *)(*a_pp)); if (guard_p == 0) { assert(buffer_list == NULL); @@ -191,13 +191,13 @@ guard( // Initialize the guard page if ((err = _focus_guard())) { - fprintf(stderr, "guard: setup _focus_guard error\r\n"); + fprintf(stderr, "guard: initial focus error\r\n"); goto exit; } // Register guard page signal handler if ((err = _register_handler())) { - fprintf(stderr, "guard: setup _register_handler error\r\n"); + fprintf(stderr, "guard: registration error\r\n"); goto clean; } } else { @@ -216,7 +216,7 @@ guard( buffer_list = new_buffer; // Run given closure - fprintf(stderr, "guard: run\r\n"); + // fprintf(stderr, "guard: run\r\n"); if (!(err = sigsetjmp(buffer_list->buffer, 1))) { *ret = f(closure); } @@ -254,6 +254,6 @@ guard( } exit: - fprintf(stderr, "guard: return\r\n"); + // fprintf(stderr, "guard: return\r\n"); return err; } From 446efcd8d809f98fa93d6cd814dd17838dbb79f0 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Fri, 16 Feb 2024 13:35:56 -0500 Subject: [PATCH 44/58] guard: touchups --- rust/ares/src/serf.rs | 2 +- rust/ares_guard/c-src/guard.c | 2 +- rust/ares_guard/src/lib.rs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 70632f14..1f4aecce 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -605,7 +605,7 @@ fn work_swap(context: &mut Context, job: Noun, goof: Noun) { context.work_swap(ovo, fec); } Err(goof_crud) => { - eprintln!("\r serf: bail"); + eprintln!("\rserf: bail"); let stack = &mut context.nock_context.stack; let lud = T(stack, &[goof_crud, goof, D(0)]); context.work_bail(lud); diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 4ec25659..6579e610 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -124,7 +124,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) // fprintf(stderr, "guard: hit: %p\r\n", si->si_addr); err = _focus_guard(); if (err) { - fprintf(stderr, "guard: handler: focus error\r\n"); + // fprintf(stderr, "guard: handler: focus error\r\n"); siglongjmp(buffer_list->buffer, err); } } diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index c10fb5aa..66f12cd7 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -4,9 +4,9 @@ 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; +pub const GUARD_NULL: u32 = guard_result_guard_null; +pub const GUARD_SIGNAL: u32 = guard_result_guard_signal; +pub const GUARD_OOM: u32 = guard_result_guard_oom; +pub const GUARD_MALLOC: u32 = guard_result_guard_malloc; +pub const GUARD_MPROTECT: u32 = guard_result_guard_mprotect; +pub const GUARD_SIGACTION: u32 = guard_result_guard_sigaction; From 840204391fe4bf5ccbf1bf209d69472531efb5bf Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Fri, 16 Feb 2024 13:45:03 -0500 Subject: [PATCH 45/58] cargo: clippy --- rust/ares/src/guard.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/ares/src/guard.rs b/rust/ares/src/guard.rs index 4ac09d43..794ec173 100644 --- a/rust/ares/src/guard.rs +++ b/rust/ares/src/guard.rs @@ -2,7 +2,6 @@ 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; @@ -88,7 +87,7 @@ pub fn call_with_guard Result>( *result_box }) } else { - let err = GuardError::from(u32::try_from(res).unwrap()); + let err = GuardError::from(res); match err { GuardError::OutOfMemory => Err(Error::NonDeterministic(Mote::Meme, D(0))), _ => { From f12337e6203954b88470ec0f70408b8fca11b630 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Fri, 16 Feb 2024 13:56:43 -0500 Subject: [PATCH 46/58] build: turn off `disable_release` default feature for `rust-assert-no-alloc` --- rust/rust-assert-no-alloc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/rust-assert-no-alloc/Cargo.toml b/rust/rust-assert-no-alloc/Cargo.toml index bbe18fb0..004f51c5 100644 --- a/rust/rust-assert-no-alloc/Cargo.toml +++ b/rust/rust-assert-no-alloc/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["allocator", "real-time", "debug", "audio"] categories = ["development-tools::debugging"] [features] -default = ["disable_release"] +default = [] warn_debug = [] warn_release = [] disable_release = [] From 45f47e069fb8ef2896bc60a0e01c64a5c5cc70e2 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Sat, 17 Feb 2024 17:27:13 -0800 Subject: [PATCH 47/58] wip: initial working on macOS --- rust/ares_guard/c-src/guard.c | 69 ++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 6579e610..5bf5d32d 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -19,7 +19,8 @@ static uintptr_t guard_p = 0; static const uintptr_t *stack_pp = NULL; static const uintptr_t *alloc_pp = NULL; static GD_buflistnode *buffer_list = NULL; -static struct sigaction prev_sa; +static struct sigaction prev_sigsegv_sa; +static struct sigaction prev_sigbus_sa; static guard_result _prot_page(void *address, int prot) @@ -110,8 +111,8 @@ _signal_handler(int sig, siginfo_t *si, void *unused) // fprintf(stderr, "guard: handler: %d received\r\n", sig); - if (sig != SIGSEGV) { - fprintf(stderr, "guard: handler: invalid signal\r\n"); + if (sig != SIGSEGV && sig != SIGBUS) { + fprintf(stderr, "guard: handler: invalid signal: %d\r\n", sig); assert(0); } @@ -130,14 +131,26 @@ _signal_handler(int sig, siginfo_t *si, void *unused) } 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); - } else if (prev_sa.sa_handler != NULL) { - prev_sa.sa_handler(sig); - } else { - // There should always be a default SIGSEGV handler - assert(0); + switch (sig) { + case SIGSEGV: { + if (prev_sigsegv_sa.sa_sigaction != NULL) { + prev_sigsegv_sa.sa_sigaction(sig, si, unused); + } else if (prev_sigsegv_sa.sa_handler != NULL) { + prev_sigsegv_sa.sa_handler(sig); + } else { + assert(0); + } + break; + } + case SIGBUS: { + if (prev_sigbus_sa.sa_sigaction != NULL) { + prev_sigbus_sa.sa_sigaction(sig, si, unused); + } else if (prev_sigbus_sa.sa_handler != NULL) { + prev_sigbus_sa.sa_handler(sig); + } else { + assert(0); + } + } } } } @@ -146,20 +159,17 @@ static guard_result _register_handler() { 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); - - // XX: should assert that prev_sa is uninitialized, but it's not a pointer so non-trivial - if (sigaction(SIGSEGV, &sa, &prev_sa)) { + + // XX should assert that prev_sigsegv_sa is uninitialized, but it's non-trivial + if (sigaction(SIGSEGV, &sa, &prev_sigsegv_sa)) { + fprintf(stderr, "guard: register: sigaction error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + return guard_sigaction | errno; + } + + if (sigaction(SIGBUS, &sa, &prev_sigbus_sa)) { fprintf(stderr, "guard: register: sigaction error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); return guard_sigaction | errno; @@ -228,8 +238,17 @@ guard( skip: // If no more guarded closures, then... if (buffer_list == NULL) { - // Remove new signal handler - if (sigaction(SIGSEGV, &prev_sa, NULL)) { + // Remove new signal handlers + if (sigaction(SIGSEGV, &prev_sigsegv_sa, NULL)) { + fprintf(stderr, "guard: sigaction error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + td_err = guard_sigaction | errno; + + if (!err) { + err = td_err; + } + } + if (sigaction(SIGBUS, &prev_sigbus_sa, NULL)) { fprintf(stderr, "guard: sigaction error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); td_err = guard_sigaction | errno; From b1960ad1f6be154316a90e5652040189fbe5472c Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 19 Feb 2024 12:54:43 -0500 Subject: [PATCH 48/58] guard: works on macOS --- rust/ares_guard/c-src/guard.c | 64 +++++++++++------------------------ 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 5bf5d32d..34c30bde 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -31,7 +31,7 @@ _prot_page(void *address, int prot) return guard_mprotect | errno; } - return 0; + return guard_success; } static guard_result @@ -47,8 +47,6 @@ _unmark_page(void *address) } // 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 guard_result _focus_guard() { @@ -58,39 +56,30 @@ _focus_guard() uintptr_t new_guard_p; guard_result 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; } else if (stack_p == alloc_p) { - // fprintf(stderr, "guard: focus: stack and alloc pointers equal\r\n"); + fprintf(stderr, "guard: focus: stack and alloc pointers equal\r\n"); return guard_oom; } - // fprintf(stderr, "guard: focus: old guard = %p\r\n", (void *)old_guard_p); - - // Compute new guard page - // XX: Should we also check for new_guard_p < min(stack_p, alloc_p)? + // Compute new guard page. 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 + // 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 at %p\r\n", (void *)new_guard_p); + // Update guard page tracker. guard_p = new_guard_p; - // Unmark the old guard page (if there is one) + // Unmark the old guard page if there is one. if (old_guard_p) { if ((err = _unmark_page((void *)old_guard_p))) { fprintf(stderr, "guard: focus: unmark error\r\n"); @@ -98,7 +87,7 @@ _focus_guard() } } - return 0; + return guard_success; } static void @@ -109,28 +98,22 @@ _signal_handler(int sig, siginfo_t *si, void *unused) assert(guard_p); - // fprintf(stderr, "guard: handler: %d received\r\n", sig); - if (sig != SIGSEGV && sig != SIGBUS) { fprintf(stderr, "guard: handler: invalid signal: %d\r\n", sig); assert(0); } sig_addr = (uintptr_t)si->si_addr; - // fprintf(stderr, "guard: SIGSEGV address = %p\r\n", (void *)sig_addr); if (sig_addr >= guard_p && sig_addr < guard_p + GD_PAGE_SIZE) { - // fprintf(stderr, "guard: hit: %p\r\n", si->si_addr); err = _focus_guard(); if (err) { - // fprintf(stderr, "guard: handler: focus error\r\n"); siglongjmp(buffer_list->buffer, err); } } else { - // fprintf(stderr, "guard: page at %p miss\r\n", (void *)guard_p); switch (sig) { case SIGSEGV: { if (prev_sigsegv_sa.sa_sigaction != NULL) { @@ -155,14 +138,14 @@ _signal_handler(int sig, siginfo_t *si, void *unused) } } +// Registers the same handler function for SIGSEGV and SIGBUS. static guard_result -_register_handler() +_register_handlers() { struct sigaction sa; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = _signal_handler; - // XX should assert that prev_sigsegv_sa is uninitialized, but it's non-trivial if (sigaction(SIGSEGV, &sa, &prev_sigsegv_sa)) { fprintf(stderr, "guard: register: sigaction error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); @@ -175,7 +158,7 @@ _register_handler() return guard_sigaction | errno; } - return 0; + return guard_success; } guard_result @@ -184,29 +167,26 @@ guard( void *closure, const uintptr_t *const s_pp, const uintptr_t *const a_pp, - void ** ret + void **ret ) { GD_buflistnode *new_buffer; guard_result err = 0; guard_result td_err = 0; - // fprintf(stderr, "guard: stack pointer at %p\r\n", (void *)(*s_pp)); - // fprintf(stderr, "guard: alloc pointer at %p\r\n", (void *)(*a_pp)); - if (guard_p == 0) { assert(buffer_list == NULL); stack_pp = s_pp; alloc_pp = a_pp; - // Initialize the guard page + // Initialize the guard page. if ((err = _focus_guard())) { fprintf(stderr, "guard: initial focus error\r\n"); goto exit; } - // Register guard page signal handler - if ((err = _register_handler())) { + // Register guard page signal handler. + if ((err = _register_handlers())) { fprintf(stderr, "guard: registration error\r\n"); goto clean; } @@ -214,7 +194,7 @@ guard( assert(buffer_list != NULL); } - // Setup new longjmp buffer + // Setup new longjmp buffer. new_buffer = (GD_buflistnode *)malloc(sizeof(GD_buflistnode)); if (new_buffer == NULL) { fprintf(stderr, "guard: malloc error\r\n"); @@ -225,22 +205,19 @@ guard( new_buffer->next = buffer_list; buffer_list = new_buffer; - // Run given closure - // fprintf(stderr, "guard: run\r\n"); + // Run given closure. if (!(err = sigsetjmp(buffer_list->buffer, 1))) { *ret = f(closure); } - // Restore previous longjmp buffer + // Restore previous longjmp buffer. buffer_list = buffer_list->next; free((void *)new_buffer); skip: - // If no more guarded closures, then... if (buffer_list == NULL) { - // Remove new signal handlers if (sigaction(SIGSEGV, &prev_sigsegv_sa, NULL)) { - fprintf(stderr, "guard: sigaction error\r\n"); + fprintf(stderr, "guard: error replacing sigsegv handler\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); td_err = guard_sigaction | errno; @@ -249,7 +226,7 @@ guard( } } if (sigaction(SIGBUS, &prev_sigbus_sa, NULL)) { - fprintf(stderr, "guard: sigaction error\r\n"); + fprintf(stderr, "guard: error replacing sigbus handler\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); td_err = guard_sigaction | errno; @@ -259,7 +236,7 @@ guard( } clean: - // Unmark guard page + // Unmark guard page. assert(guard_p != 0); td_err = _unmark_page((void *)guard_p); if (td_err) { @@ -273,6 +250,5 @@ guard( } exit: - // fprintf(stderr, "guard: return\r\n"); return err; } From 5dcc2402ea8c8cf7a0744264a60471148a341cfe Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 19 Feb 2024 13:26:21 -0500 Subject: [PATCH 49/58] guard: use state struct --- rust/ares_guard/c-src/guard.c | 83 +++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 7ab24cc5..9bb2a528 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -15,12 +15,17 @@ #define GD_PAGE_MASK (GD_PAGE_SIZE - 1) #define GD_PAGE_ROUND_DOWN(foo) (foo & (~GD_PAGE_MASK)) -static uintptr_t guard_p = 0; -static const uintptr_t *stack_pp = NULL; -static const uintptr_t *alloc_pp = NULL; -static GD_buflistnode *buffer_list = NULL; -static struct sigaction prev_sigsegv_sa; -static struct sigaction prev_sigbus_sa; +typedef struct GD_state GD_state; +struct GD_state { + uintptr_t guard_p; + const uintptr_t *stack_pp; + const uintptr_t *alloc_pp; + GD_buflistnode *buffer_list; + struct sigaction prev_sigsegv_sa; + struct sigaction prev_sigbus_sa; +}; + +static GD_state gd = { 0 , NULL, NULL, NULL, NULL, 0 }; static guard_result _prot_page(void *address, int prot) @@ -50,9 +55,9 @@ _unmark_page(void *address) static guard_result _focus_guard() { - uintptr_t stack_p = *stack_pp; - uintptr_t alloc_p = *alloc_pp; - uintptr_t old_guard_p = guard_p; + uintptr_t stack_p = *gd.stack_pp; + uintptr_t alloc_p = *gd.alloc_pp; + uintptr_t old_guard_p = gd.guard_p; uintptr_t new_guard_p; guard_result err = 0; @@ -76,7 +81,7 @@ _focus_guard() } // Update guard page tracker. - guard_p = new_guard_p; + gd.guard_p = new_guard_p; // Unmark the old guard page if there is one. if (old_guard_p) { @@ -95,7 +100,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) uintptr_t sig_addr; guard_result err = 0; - assert(guard_p); + assert(gd.guard_p); if (sig != SIGSEGV && sig != SIGBUS) { fprintf(stderr, "guard: handler: invalid signal: %d\r\n", sig); @@ -104,31 +109,31 @@ _signal_handler(int sig, siginfo_t *si, void *unused) sig_addr = (uintptr_t)si->si_addr; - if (sig_addr >= guard_p && - sig_addr < guard_p + GD_PAGE_SIZE) + if (sig_addr >= gd.guard_p && + sig_addr < gd.guard_p + GD_PAGE_SIZE) { err = _focus_guard(); if (err) { - siglongjmp(buffer_list->buffer, err); + siglongjmp(gd.buffer_list->buffer, err); } } else { switch (sig) { case SIGSEGV: { - if (prev_sigsegv_sa.sa_sigaction != NULL) { - prev_sigsegv_sa.sa_sigaction(sig, si, unused); - } else if (prev_sigsegv_sa.sa_handler != NULL) { - prev_sigsegv_sa.sa_handler(sig); + if (gd.prev_sigsegv_sa.sa_sigaction != NULL) { + gd.prev_sigsegv_sa.sa_sigaction(sig, si, unused); + } else if (gd.prev_sigsegv_sa.sa_handler != NULL) { + gd.prev_sigsegv_sa.sa_handler(sig); } else { assert(0); } break; } case SIGBUS: { - if (prev_sigbus_sa.sa_sigaction != NULL) { - prev_sigbus_sa.sa_sigaction(sig, si, unused); - } else if (prev_sigbus_sa.sa_handler != NULL) { - prev_sigbus_sa.sa_handler(sig); + if (gd.prev_sigbus_sa.sa_sigaction != NULL) { + gd.prev_sigbus_sa.sa_sigaction(sig, si, unused); + } else if (gd.prev_sigbus_sa.sa_handler != NULL) { + gd.prev_sigbus_sa.sa_handler(sig); } else { assert(0); } @@ -145,13 +150,13 @@ _register_handlers() sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = _signal_handler; - if (sigaction(SIGSEGV, &sa, &prev_sigsegv_sa)) { + if (sigaction(SIGSEGV, &sa, &gd.prev_sigsegv_sa)) { fprintf(stderr, "guard: register: sigaction error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); return guard_sigaction | errno; } - if (sigaction(SIGBUS, &sa, &prev_sigbus_sa)) { + if (sigaction(SIGBUS, &sa, &gd.prev_sigbus_sa)) { fprintf(stderr, "guard: register: sigaction error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); return guard_sigaction | errno; @@ -172,11 +177,11 @@ guard( guard_result err = 0; guard_result td_err = 0; - if (guard_p == 0) { - assert(buffer_list == NULL); + if (gd.guard_p == 0) { + assert(gd.buffer_list == NULL); - stack_pp = s_pp; - alloc_pp = a_pp; + gd.stack_pp = s_pp; + gd.alloc_pp = a_pp; // Initialize the guard page. if ((err = _focus_guard())) { @@ -190,7 +195,7 @@ guard( goto clean; } } else { - assert(buffer_list != NULL); + assert(gd.buffer_list != NULL); } // Setup new longjmp buffer. @@ -201,21 +206,21 @@ guard( err = guard_malloc | errno; goto skip; } - new_buffer->next = buffer_list; - buffer_list = new_buffer; + new_buffer->next = gd.buffer_list; + gd.buffer_list = new_buffer; // Run given closure. - if (!(err = sigsetjmp(buffer_list->buffer, 1))) { + if (!(err = sigsetjmp(gd.buffer_list->buffer, 1))) { *ret = f(closure); } // Restore previous longjmp buffer. - buffer_list = buffer_list->next; + gd.buffer_list = gd.buffer_list->next; free((void *)new_buffer); skip: - if (buffer_list == NULL) { - if (sigaction(SIGSEGV, &prev_sigsegv_sa, NULL)) { + if (gd.buffer_list == NULL) { + if (sigaction(SIGSEGV, &gd.prev_sigsegv_sa, NULL)) { fprintf(stderr, "guard: error replacing sigsegv handler\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); td_err = guard_sigaction | errno; @@ -224,7 +229,7 @@ guard( err = td_err; } } - if (sigaction(SIGBUS, &prev_sigbus_sa, NULL)) { + if (sigaction(SIGBUS, &gd.prev_sigbus_sa, NULL)) { fprintf(stderr, "guard: error replacing sigbus handler\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); td_err = guard_sigaction | errno; @@ -236,8 +241,8 @@ guard( clean: // Unmark guard page. - assert(guard_p != 0); - td_err = _unmark_page((void *)guard_p); + assert(gd.guard_p != 0); + td_err = _unmark_page((void *)gd.guard_p); if (td_err) { fprintf(stderr, "guard: unmark error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); @@ -245,7 +250,7 @@ guard( err = td_err; } } - guard_p = 0; + gd.guard_p = 0; } exit: From 96e0777b6b4ac0d6d66cf8d3c12949a8e8d8cc4a Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 19 Feb 2024 13:47:39 -0500 Subject: [PATCH 50/58] guard: placate clang warnings --- rust/ares_guard/c-src/guard.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 9bb2a528..d39e78ce 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -25,7 +25,14 @@ struct GD_state { struct sigaction prev_sigbus_sa; }; -static GD_state gd = { 0 , NULL, NULL, NULL, NULL, 0 }; +static GD_state gd = { + .guard_p = 0, + .stack_pp = NULL, + .alloc_pp = NULL, + .buffer_list = NULL, + .prev_sigsegv_sa = { .sa_sigaction = NULL, .sa_flags = 0 }, + .prev_sigbus_sa = { .sa_sigaction = NULL, .sa_flags = 0 }, +}; static guard_result _prot_page(void *address, int prot) From 85fef33fcb9db73478d991c6a9d82ca2657a1e3f Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 19 Feb 2024 16:02:47 -0500 Subject: [PATCH 51/58] guard: use `guard_success` instead of `0` --- rust/ares_guard/c-src/guard.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index d39e78ce..428943bb 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -66,7 +66,7 @@ _focus_guard() uintptr_t alloc_p = *gd.alloc_pp; uintptr_t old_guard_p = gd.guard_p; uintptr_t new_guard_p; - guard_result err = 0; + guard_result err = guard_success; if (stack_p == 0 || alloc_p == 0) { fprintf(stderr, "guard: focus: stack or alloc pointer is null\r\n"); @@ -105,7 +105,7 @@ static void _signal_handler(int sig, siginfo_t *si, void *unused) { uintptr_t sig_addr; - guard_result err = 0; + guard_result err = guard_success; assert(gd.guard_p); @@ -181,8 +181,8 @@ guard( void **ret ) { GD_buflistnode *new_buffer; - guard_result err = 0; - guard_result td_err = 0; + guard_result err = guard_success; + guard_result td_err = guard_success; if (gd.guard_p == 0) { assert(gd.buffer_list == NULL); From b1e3b788b19f029697c058d76ddd79fff8f7429e Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Mon, 19 Feb 2024 18:47:00 -0500 Subject: [PATCH 52/58] guard: remove unhelpful functions --- rust/ares_guard/c-src/guard.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 428943bb..c14a5f1d 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -35,7 +35,7 @@ static GD_state gd = { }; static guard_result -_prot_page(void *address, int prot) +_protect_page(void *address, int prot) { if (mprotect(address, GD_PAGE_SIZE, prot)) { fprintf(stderr, "guard: prot: mprotect error %d\r\n", errno); @@ -46,18 +46,6 @@ _prot_page(void *address, int prot) return guard_success; } -static guard_result -_mark_page(void *address) -{ - return _prot_page(address, PROT_NONE); -} - -static guard_result -_unmark_page(void *address) -{ - return _prot_page(address, PROT_READ | PROT_WRITE); -} - // Center the guard page. static guard_result _focus_guard() @@ -82,7 +70,7 @@ _focus_guard() } // Mark new guard page. - if ((err = _mark_page((void *)new_guard_p))) { + if ((err = _protect_page((void *)new_guard_p, PROT_NONE))) { fprintf(stderr, "guard: focus: mark error\r\n"); return err; } @@ -92,7 +80,7 @@ _focus_guard() // Unmark the old guard page if there is one. if (old_guard_p) { - if ((err = _unmark_page((void *)old_guard_p))) { + if ((err = _protect_page((void *)old_guard_p, PROT_READ | PROT_WRITE))) { fprintf(stderr, "guard: focus: unmark error\r\n"); return err; } @@ -249,7 +237,7 @@ guard( clean: // Unmark guard page. assert(gd.guard_p != 0); - td_err = _unmark_page((void *)gd.guard_p); + td_err = _protect_page((void *)gd.guard_p, PROT_READ | PROT_WRITE); if (td_err) { fprintf(stderr, "guard: unmark error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); From d13fe50c72c6d952564aa36c72dc1bc86f6852a2 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 20 Feb 2024 02:53:14 -0500 Subject: [PATCH 53/58] guard: partially address @ashelkovnykov's comments --- rust/ares_guard/c-src/guard.c | 22 +++++++++++----------- rust/ares_guard/c-src/guard.h | 15 +++++++-------- rust/ares_guard/src/lib.rs | 1 - 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index c14a5f1d..3eda947a 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -34,7 +34,7 @@ static GD_state gd = { .prev_sigbus_sa = { .sa_sigaction = NULL, .sa_flags = 0 }, }; -static guard_result +static guard_err _protect_page(void *address, int prot) { if (mprotect(address, GD_PAGE_SIZE, prot)) { @@ -43,18 +43,18 @@ _protect_page(void *address, int prot) return guard_mprotect | errno; } - return guard_success; + return 0; } // Center the guard page. -static guard_result +static guard_err _focus_guard() { uintptr_t stack_p = *gd.stack_pp; uintptr_t alloc_p = *gd.alloc_pp; uintptr_t old_guard_p = gd.guard_p; uintptr_t new_guard_p; - guard_result err = guard_success; + guard_err err = 0; if (stack_p == 0 || alloc_p == 0) { fprintf(stderr, "guard: focus: stack or alloc pointer is null\r\n"); @@ -86,14 +86,14 @@ _focus_guard() } } - return guard_success; + return 0; } static void _signal_handler(int sig, siginfo_t *si, void *unused) { uintptr_t sig_addr; - guard_result err = guard_success; + guard_err err = 0; assert(gd.guard_p); @@ -138,7 +138,7 @@ _signal_handler(int sig, siginfo_t *si, void *unused) } // Registers the same handler function for SIGSEGV and SIGBUS. -static guard_result +static guard_err _register_handlers() { struct sigaction sa; @@ -157,10 +157,10 @@ _register_handlers() return guard_sigaction | errno; } - return guard_success; + return 0; } -guard_result +guard_err guard( void *(*f)(void *), void *closure, @@ -169,8 +169,8 @@ guard( void **ret ) { GD_buflistnode *new_buffer; - guard_result err = guard_success; - guard_result td_err = guard_success; + guard_err err = 0; + guard_err td_err = 0; if (gd.guard_p == 0) { assert(gd.buffer_list == NULL); diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 3cef93c6..d5dad891 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -10,7 +10,7 @@ typedef struct GD_buflistnode GD_buflistnode; struct GD_buflistnode { jmp_buf buffer; - struct GD_buflistnode *next; + GD_buflistnode *next; }; /** @@ -19,21 +19,20 @@ struct GD_buflistnode { * The flags are bitwise added to the errno of their respective errors. */ typedef enum { - guard_success = 0, // successful return guard_null = 1, // null stack or alloc pointer guard_signal = 2, // invalid signal guard_oom = 3, // out of memory guard_malloc = 0x10000000, // malloc error flag guard_mprotect = 0x20000000, // mprotect error flag guard_sigaction = 0x40000000, // sigaction error flag -} guard_result; +} guard_err; /** * @brief Executes the given callback function `f` within the memory arena * between the stack and allocation pointers pointed to by `s_pp` and `a_pp`, * with guard page protection. If `f`'s execution succeeds, its result is * written to the return pointer `*ret`. If `f`'s execution triggers an - * out of memory error or any other `guard_result`, the `guard_result` is + * out of memory error or any other `guard_err`, the `guard_err` is * returned and `*ret` is left empty. In either case, cleanup is performed * before returning. * @@ -49,14 +48,14 @@ typedef enum { * - The caller is responsible for managing any external state the callback * function may mutate. * - The callback function may be interrupted in the case of memory exhaustion - * or other `guard_result` error (failure to `mprotect`, `malloc`, etc.). + * or other `guard_err` error (failure to `mprotect`, `malloc`, etc.). * - `SIGSEGV` signals are expected to be raised only on guard page accesses. * * Invariants: * - A single guard page is installed and maintained in the approximate center * until `crate::guard::call_with_guard` returns. * - A return value is only written to `*ret` on successful callback execution. - * - A `guard_result` is returned, excepting panics or negative assertions. + * - A `guard_err` is returned, excepting panics or negative assertions. * * Enhancements: * - Use only a single, static jump buffer variable instead of a linked list. @@ -70,9 +69,9 @@ typedef enum { * @param a_pp A pointer to the allocation pointer location. * @param ret A pointer to a location where the callback's result can be stored. * - * @return A `guard_result` return code. + * @return A `guard_err` error code. */ -guard_result +guard_err guard( void *(*f)(void *), void *closure, diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index ff1a93ee..7fa53815 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -4,7 +4,6 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -pub const GUARD_SUCCESS: u32 = guard_result_guard_success; pub const GUARD_NULL: u32 = guard_result_guard_null; pub const GUARD_SIGNAL: u32 = guard_result_guard_signal; pub const GUARD_OOM: u32 = guard_result_guard_oom; From 66166264d2082743af338cc87577b0622421c1d9 Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 21 Feb 2024 10:06:18 -0500 Subject: [PATCH 54/58] Revert "Merge branch 'as/serf-guard' into msl/guard" This reverts commit dd2bbc5ae2ba3828d8ed75cf7a46a2a327c42a3e, reversing changes made to d13fe50c72c6d952564aa36c72dc1bc86f6852a2. --- rust/ares_guard/c-src/guard.c | 154 +++++++++++++++------------ rust/ares_guard/c-src/guard.h | 8 +- rust/rust-assert-no-alloc/Cargo.toml | 1 + 3 files changed, 90 insertions(+), 73 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 834adfe8..3eda947a 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -15,23 +15,24 @@ #define GD_PAGE_MASK (GD_PAGE_SIZE - 1) #define GD_PAGE_ROUND_DOWN(foo) (foo & (~GD_PAGE_MASK)) -#ifdef __APPLE__ - #define GD_SIGNAL SIGBUS -#else - #define GD_SIGNAL SIGSEGV -#endif - -typedef struct _GD_state GD_state; -struct _GD_state { +typedef struct GD_state GD_state; +struct GD_state { uintptr_t guard_p; const uintptr_t *stack_pp; const uintptr_t *alloc_pp; GD_buflistnode *buffer_list; - struct sigaction prev_sa; + struct sigaction prev_sigsegv_sa; + struct sigaction prev_sigbus_sa; }; -static GD_state *_guard_state = NULL; - +static GD_state gd = { + .guard_p = 0, + .stack_pp = NULL, + .alloc_pp = NULL, + .buffer_list = NULL, + .prev_sigsegv_sa = { .sa_sigaction = NULL, .sa_flags = 0 }, + .prev_sigbus_sa = { .sa_sigaction = NULL, .sa_flags = 0 }, +}; static guard_err _protect_page(void *address, int prot) @@ -39,7 +40,7 @@ _protect_page(void *address, int prot) if (mprotect(address, GD_PAGE_SIZE, prot)) { fprintf(stderr, "guard: prot: mprotect error %d\r\n", errno); fprintf(stderr, "%s\r\n", strerror(errno)); - return guard_mprotect; + return guard_mprotect | errno; } return 0; @@ -47,14 +48,14 @@ _protect_page(void *address, int prot) // Center the guard page. static guard_err -_focus_guard(GD_state *gs) { - uintptr_t stack_p = *(gs->stack_pp); - uintptr_t alloc_p = *(gs->alloc_pp); - uintptr_t old_guard_p = _guard_state->guard_p; +_focus_guard() +{ + uintptr_t stack_p = *gd.stack_pp; + uintptr_t alloc_p = *gd.alloc_pp; + uintptr_t old_guard_p = gd.guard_p; uintptr_t new_guard_p; - guard_err err = 0; + guard_err err = 0; - // Check anomalous arguments. if (stack_p == 0 || alloc_p == 0) { fprintf(stderr, "guard: focus: stack or alloc pointer is null\r\n"); return guard_null; @@ -75,7 +76,7 @@ _focus_guard(GD_state *gs) { } // Update guard page tracker. - gs->guard_p = new_guard_p; + gd.guard_p = new_guard_p; // Unmark the old guard page if there is one. if (old_guard_p) { @@ -94,48 +95,66 @@ _signal_handler(int sig, siginfo_t *si, void *unused) uintptr_t sig_addr; guard_err err = 0; - assert(_guard_state->guard_p); + assert(gd.guard_p); - if (sig != GD_SIGNAL) { + if (sig != SIGSEGV && sig != SIGBUS) { fprintf(stderr, "guard: handler: invalid signal: %d\r\n", sig); assert(0); } - assert(sig == GD_SIGNAL); sig_addr = (uintptr_t)si->si_addr; - if (sig_addr >= _guard_state->guard_p && - sig_addr < _guard_state->guard_p + GD_PAGE_SIZE) + if (sig_addr >= gd.guard_p && + sig_addr < gd.guard_p + GD_PAGE_SIZE) { - err = _focus_guard(_guard_state); + err = _focus_guard(); if (err) { - siglongjmp(_guard_state->buffer_list->buffer, err); + siglongjmp(gd.buffer_list->buffer, err); } } else { - if (_guard_state->prev_sa.sa_sigaction != NULL) { - _guard_state->prev_sa.sa_sigaction(sig, si, unused); - } else if (_guard_state->prev_sa.sa_handler != NULL) { - _guard_state->prev_sa.sa_handler(sig); - } else { - assert(0); + switch (sig) { + case SIGSEGV: { + if (gd.prev_sigsegv_sa.sa_sigaction != NULL) { + gd.prev_sigsegv_sa.sa_sigaction(sig, si, unused); + } else if (gd.prev_sigsegv_sa.sa_handler != NULL) { + gd.prev_sigsegv_sa.sa_handler(sig); + } else { + assert(0); + } + break; + } + case SIGBUS: { + if (gd.prev_sigbus_sa.sa_sigaction != NULL) { + gd.prev_sigbus_sa.sa_sigaction(sig, si, unused); + } else if (gd.prev_sigbus_sa.sa_handler != NULL) { + gd.prev_sigbus_sa.sa_handler(sig); + } else { + assert(0); + } + } } } } -// Registers the handler function for GD_SIGNAL. +// Registers the same handler function for SIGSEGV and SIGBUS. static guard_err -_register_handler(GD_state *gs) +_register_handlers() { struct sigaction sa; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = _signal_handler; - // Set the new handler and save the old if it exists. - if (sigaction(GD_SIGNAL, &sa, &(gs->prev_sa))) { + if (sigaction(SIGSEGV, &sa, &gd.prev_sigsegv_sa)) { + fprintf(stderr, "guard: register: sigaction error\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + return guard_sigaction | errno; + } + + if (sigaction(SIGBUS, &sa, &gd.prev_sigbus_sa)) { fprintf(stderr, "guard: register: sigaction error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); - return guard_sigaction; + return guard_sigaction | errno; } return 0; @@ -153,36 +172,25 @@ guard( guard_err err = 0; guard_err td_err = 0; - // Setup guard state. - if (_guard_state == NULL) { - _guard_state = (GD_state *)malloc(sizeof(GD_state)); - if (_guard_state == NULL) { - fprintf(stderr, "guard: malloc error\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - err = guard_malloc; - goto skip; - } - _guard_state->guard_p = 0; - _guard_state->stack_pp = s_pp; - _guard_state->alloc_pp = a_pp; - } - - if (_guard_state->guard_p == 0) { - assert(_guard_state->buffer_list == NULL); + if (gd.guard_p == 0) { + assert(gd.buffer_list == NULL); + + gd.stack_pp = s_pp; + gd.alloc_pp = a_pp; // Initialize the guard page. - if ((err = _focus_guard(_guard_state))) { + if ((err = _focus_guard())) { fprintf(stderr, "guard: initial focus error\r\n"); goto exit; } // Register guard page signal handler. - if ((err = _register_handler(_guard_state))) { + if ((err = _register_handlers())) { fprintf(stderr, "guard: registration error\r\n"); goto clean; } } else { - assert(_guard_state->buffer_list != NULL); + assert(gd.buffer_list != NULL); } // Setup new longjmp buffer. @@ -190,27 +198,36 @@ guard( if (new_buffer == NULL) { fprintf(stderr, "guard: malloc error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); - err = guard_malloc; + err = guard_malloc | errno; goto skip; } - new_buffer->next = _guard_state->buffer_list; - _guard_state->buffer_list = new_buffer; + new_buffer->next = gd.buffer_list; + gd.buffer_list = new_buffer; // Run given closure. - if (!(err = sigsetjmp(_guard_state->buffer_list->buffer, 1))) { + if (!(err = sigsetjmp(gd.buffer_list->buffer, 1))) { *ret = f(closure); } // Restore previous longjmp buffer. - _guard_state->buffer_list = _guard_state->buffer_list->next; + gd.buffer_list = gd.buffer_list->next; free((void *)new_buffer); skip: - if (_guard_state->buffer_list == NULL) { - if (sigaction(GD_SIGNAL, &_guard_state->prev_sa, NULL)) { - fprintf(stderr, "guard: error replacing previous handler\r\n"); + if (gd.buffer_list == NULL) { + if (sigaction(SIGSEGV, &gd.prev_sigsegv_sa, NULL)) { + fprintf(stderr, "guard: error replacing sigsegv handler\r\n"); + fprintf(stderr, "%s\r\n", strerror(errno)); + td_err = guard_sigaction | errno; + + if (!err) { + err = td_err; + } + } + if (sigaction(SIGBUS, &gd.prev_sigbus_sa, NULL)) { + fprintf(stderr, "guard: error replacing sigbus handler\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); - td_err = guard_sigaction; + td_err = guard_sigaction | errno; if (!err) { err = td_err; @@ -219,8 +236,8 @@ guard( clean: // Unmark guard page. - assert(_guard_state->guard_p != 0); - td_err = _protect_page((void *)_guard_state->guard_p, PROT_READ | PROT_WRITE); + assert(gd.guard_p != 0); + td_err = _protect_page((void *)gd.guard_p, PROT_READ | PROT_WRITE); if (td_err) { fprintf(stderr, "guard: unmark error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); @@ -228,8 +245,7 @@ guard( err = td_err; } } - _guard_state->guard_p = 0; - // free(_guard_state); + gd.guard_p = 0; } exit: diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index 53ab8a60..d5dad891 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -9,7 +9,7 @@ */ typedef struct GD_buflistnode GD_buflistnode; struct GD_buflistnode { - jmp_buf buffer; + jmp_buf buffer; GD_buflistnode *next; }; @@ -22,9 +22,9 @@ typedef enum { guard_null = 1, // null stack or alloc pointer guard_signal = 2, // invalid signal guard_oom = 3, // out of memory - guard_malloc = 4, // malloc error flag - guard_mprotect = 5, // mprotect error flag - guard_sigaction = 6, // sigaction error flag + guard_malloc = 0x10000000, // malloc error flag + guard_mprotect = 0x20000000, // mprotect error flag + guard_sigaction = 0x40000000, // sigaction error flag } guard_err; /** diff --git a/rust/rust-assert-no-alloc/Cargo.toml b/rust/rust-assert-no-alloc/Cargo.toml index 95a935e7..004f51c5 100644 --- a/rust/rust-assert-no-alloc/Cargo.toml +++ b/rust/rust-assert-no-alloc/Cargo.toml @@ -12,6 +12,7 @@ keywords = ["allocator", "real-time", "debug", "audio"] categories = ["development-tools::debugging"] [features] +default = [] warn_debug = [] warn_release = [] disable_release = [] From e03d340adde2cea863a2df6397183cbceb2729ae Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 21 Feb 2024 10:09:04 -0500 Subject: [PATCH 55/58] guard: use `guard_err` in rust constants --- rust/ares_guard/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/ares_guard/src/lib.rs b/rust/ares_guard/src/lib.rs index 7fa53815..f7b1089a 100644 --- a/rust/ares_guard/src/lib.rs +++ b/rust/ares_guard/src/lib.rs @@ -4,9 +4,9 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -pub const GUARD_NULL: u32 = guard_result_guard_null; -pub const GUARD_SIGNAL: u32 = guard_result_guard_signal; -pub const GUARD_OOM: u32 = guard_result_guard_oom; -pub const GUARD_MALLOC: u32 = guard_result_guard_malloc; -pub const GUARD_MPROTECT: u32 = guard_result_guard_mprotect; -pub const GUARD_SIGACTION: u32 = guard_result_guard_sigaction; +pub const GUARD_NULL: u32 = guard_err_guard_null; +pub const GUARD_SIGNAL: u32 = guard_err_guard_signal; +pub const GUARD_OOM: u32 = guard_err_guard_oom; +pub const GUARD_MALLOC: u32 = guard_err_guard_malloc; +pub const GUARD_MPROTECT: u32 = guard_err_guard_mprotect; +pub const GUARD_SIGACTION: u32 = guard_err_guard_sigaction; From 3573e90b91ca270d225024b4389f10f18a266f4a Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 21 Feb 2024 10:12:27 -0500 Subject: [PATCH 56/58] pma: restore `btree.c` --- rust/ares_pma/c-src/btree.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/ares_pma/c-src/btree.c b/rust/ares_pma/c-src/btree.c index 9ecf982c..24dff2fa 100644 --- a/rust/ares_pma/c-src/btree.c +++ b/rust/ares_pma/c-src/btree.c @@ -76,6 +76,7 @@ STATIC_ASSERT(0, "debugger break instruction unimplemented"); /* the opposite of P2BYTES */ #define B2PAGES(x) ((size_t)(x) >> BT_PAGEBITS) + #define __packed __attribute__((__packed__)) #define UNUSED(x) ((void)(x)) @@ -94,6 +95,7 @@ STATIC_ASSERT(0, "debugger break instruction unimplemented"); /* given a pointer p returns the low page-aligned addr */ #define LO_ALIGN_PAGE(p) ((BT_page *)(((uintptr_t)p) & ~(BT_PAGESIZE - 1))) + #define BT_MAPADDR ((BYTE *) S(0x1000,0000,0000)) static inline vaof_t @@ -339,6 +341,7 @@ struct BT_state { /* + //// =========================================================================== //// btree internal routines @@ -1567,7 +1570,6 @@ _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); From 209835cdf8fec59ca38ed3fb9df231e8c91c5d70 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 21 Feb 2024 11:05:31 -0500 Subject: [PATCH 57/58] guard: C style (pair w/ Matt) --- rust/ares_guard/c-src/guard.c | 165 +++++++++++++++++----------------- rust/ares_guard/c-src/guard.h | 34 +++---- 2 files changed, 93 insertions(+), 106 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 3eda947a..4eec53cb 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -15,46 +15,62 @@ #define GD_PAGE_MASK (GD_PAGE_SIZE - 1) #define GD_PAGE_ROUND_DOWN(foo) (foo & (~GD_PAGE_MASK)) +#ifdef __APPLE__ + #define GD_SIGNAL SIGBUS +#else + #define GD_SIGNAL SIGSEGV +#endif + +/** + * Linked list stack of jump buffers. + */ +typedef struct GD_buflistnode GD_buflistnode; +struct GD_buflistnode { + jmp_buf buffer; + GD_buflistnode *next; +}; + +/** + * Global guard page state. + */ typedef struct GD_state GD_state; struct GD_state { uintptr_t guard_p; const uintptr_t *stack_pp; const uintptr_t *alloc_pp; GD_buflistnode *buffer_list; - struct sigaction prev_sigsegv_sa; - struct sigaction prev_sigbus_sa; + struct sigaction prev_sa; }; -static GD_state gd = { +static GD_state _gd_state = { .guard_p = 0, .stack_pp = NULL, .alloc_pp = NULL, .buffer_list = NULL, - .prev_sigsegv_sa = { .sa_sigaction = NULL, .sa_flags = 0 }, - .prev_sigbus_sa = { .sa_sigaction = NULL, .sa_flags = 0 }, + .prev_sa = { .sa_sigaction = NULL, .sa_flags = 0 }, }; -static guard_err +static uint32_t _protect_page(void *address, int prot) { if (mprotect(address, GD_PAGE_SIZE, prot)) { fprintf(stderr, "guard: prot: mprotect error %d\r\n", errno); fprintf(stderr, "%s\r\n", strerror(errno)); - return guard_mprotect | errno; + return guard_mprotect ; } return 0; } // Center the guard page. -static guard_err -_focus_guard() +static uint32_t +_focus_guard(GD_state *gd) { - uintptr_t stack_p = *gd.stack_pp; - uintptr_t alloc_p = *gd.alloc_pp; - uintptr_t old_guard_p = gd.guard_p; + uintptr_t stack_p = *(gd->stack_pp); + uintptr_t alloc_p = *(gd->alloc_pp); + uintptr_t old_guard_p = (gd->guard_p); uintptr_t new_guard_p; - guard_err err = 0; + uint32_t err = 0; if (stack_p == 0 || alloc_p == 0) { fprintf(stderr, "guard: focus: stack or alloc pointer is null\r\n"); @@ -76,7 +92,7 @@ _focus_guard() } // Update guard page tracker. - gd.guard_p = new_guard_p; + gd->guard_p = new_guard_p; // Unmark the old guard page if there is one. if (old_guard_p) { @@ -93,74 +109,64 @@ static void _signal_handler(int sig, siginfo_t *si, void *unused) { uintptr_t sig_addr; - guard_err err = 0; + uint32_t err = 0; - assert(gd.guard_p); - - if (sig != SIGSEGV && sig != SIGBUS) { + assert(_gd_state.guard_p); + if (sig != GD_SIGNAL) { fprintf(stderr, "guard: handler: invalid signal: %d\r\n", sig); assert(0); } sig_addr = (uintptr_t)si->si_addr; - if (sig_addr >= gd.guard_p && - sig_addr < gd.guard_p + GD_PAGE_SIZE) + if (sig_addr >= _gd_state.guard_p && + sig_addr < _gd_state.guard_p + GD_PAGE_SIZE) { - err = _focus_guard(); + err = _focus_guard(&_gd_state); if (err) { - siglongjmp(gd.buffer_list->buffer, err); + siglongjmp(_gd_state.buffer_list->buffer, err); } } else { - switch (sig) { - case SIGSEGV: { - if (gd.prev_sigsegv_sa.sa_sigaction != NULL) { - gd.prev_sigsegv_sa.sa_sigaction(sig, si, unused); - } else if (gd.prev_sigsegv_sa.sa_handler != NULL) { - gd.prev_sigsegv_sa.sa_handler(sig); - } else { - assert(0); - } - break; - } - case SIGBUS: { - if (gd.prev_sigbus_sa.sa_sigaction != NULL) { - gd.prev_sigbus_sa.sa_sigaction(sig, si, unused); - } else if (gd.prev_sigbus_sa.sa_handler != NULL) { - gd.prev_sigbus_sa.sa_handler(sig); - } else { - assert(0); - } - } + struct sigaction prev_sa = _gd_state.prev_sa; + + if (prev_sa.sa_sigaction != NULL) { + prev_sa.sa_sigaction(sig, si, unused); + } else if (prev_sa.sa_handler != NULL) { + prev_sa.sa_handler(sig); + } else { + // There should always be a default handler + assert(0); } } } -// Registers the same handler function for SIGSEGV and SIGBUS. -static guard_err -_register_handlers() +// Registers the handler function. +static uint32_t +_register_handler(GD_state *gd) { struct sigaction sa; + + // Flag to use sa_sigaction instead of sa_handler sa.sa_flags = SA_SIGINFO; +// Must use sa_sigaction; sa-handler takes signal handler as its only argument sa.sa_sigaction = _signal_handler; + // XX: By default the signal that triggered the signal handler is automatically added to the + // mask while it's being handled, so unless we plan to add more signals to this then I don't + // think it's necessary. + // sigemptyset(&sa.sa_mask); + // sigaddset(&(sa.sa_mask), GD_SIGNAL); - if (sigaction(SIGSEGV, &sa, &gd.prev_sigsegv_sa)) { - fprintf(stderr, "guard: register: sigaction error\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - return guard_sigaction | errno; - } - - if (sigaction(SIGBUS, &sa, &gd.prev_sigbus_sa)) { + if (sigaction(GD_SIGNAL, &sa, &(gd->prev_sa))) { fprintf(stderr, "guard: register: sigaction error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); - return guard_sigaction | errno; + return guard_sigaction; } return 0; } -guard_err +uint32_t guard( void *(*f)(void *), void *closure, @@ -168,29 +174,29 @@ guard( const uintptr_t *const a_pp, void **ret ) { - GD_buflistnode *new_buffer; - guard_err err = 0; - guard_err td_err = 0; + GD_buflistnode *new_buffer; + uint32_t err = 0; + uint32_t td_err = 0; - if (gd.guard_p == 0) { - assert(gd.buffer_list == NULL); + if (_gd_state.guard_p == 0) { + assert(_gd_state.buffer_list == NULL); - gd.stack_pp = s_pp; - gd.alloc_pp = a_pp; + _gd_state.stack_pp = s_pp; + _gd_state.alloc_pp = a_pp; // Initialize the guard page. - if ((err = _focus_guard())) { + if ((err = _focus_guard(&_gd_state))) { fprintf(stderr, "guard: initial focus error\r\n"); goto exit; } // Register guard page signal handler. - if ((err = _register_handlers())) { + if ((err = _register_handler(&_gd_state))) { fprintf(stderr, "guard: registration error\r\n"); goto clean; } } else { - assert(gd.buffer_list != NULL); + assert(_gd_state.buffer_list != NULL); } // Setup new longjmp buffer. @@ -198,36 +204,27 @@ guard( if (new_buffer == NULL) { fprintf(stderr, "guard: malloc error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); - err = guard_malloc | errno; + err = guard_malloc; goto skip; } - new_buffer->next = gd.buffer_list; - gd.buffer_list = new_buffer; + new_buffer->next = _gd_state.buffer_list; + _gd_state.buffer_list = new_buffer; // Run given closure. - if (!(err = sigsetjmp(gd.buffer_list->buffer, 1))) { + if (!(err = sigsetjmp(_gd_state.buffer_list->buffer, 1))) { *ret = f(closure); } // Restore previous longjmp buffer. - gd.buffer_list = gd.buffer_list->next; + _gd_state.buffer_list = _gd_state.buffer_list->next; free((void *)new_buffer); skip: - if (gd.buffer_list == NULL) { - if (sigaction(SIGSEGV, &gd.prev_sigsegv_sa, NULL)) { + if (_gd_state.buffer_list == NULL) { + if (sigaction(GD_SIGNAL, &_gd_state.prev_sa, NULL)) { fprintf(stderr, "guard: error replacing sigsegv handler\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); - td_err = guard_sigaction | errno; - - if (!err) { - err = td_err; - } - } - if (sigaction(SIGBUS, &gd.prev_sigbus_sa, NULL)) { - fprintf(stderr, "guard: error replacing sigbus handler\r\n"); - fprintf(stderr, "%s\r\n", strerror(errno)); - td_err = guard_sigaction | errno; + td_err = guard_sigaction; if (!err) { err = td_err; @@ -236,8 +233,8 @@ guard( clean: // Unmark guard page. - assert(gd.guard_p != 0); - td_err = _protect_page((void *)gd.guard_p, PROT_READ | PROT_WRITE); + assert(_gd_state.guard_p != 0); + td_err = _protect_page((void *)_gd_state.guard_p, PROT_READ | PROT_WRITE); if (td_err) { fprintf(stderr, "guard: unmark error\r\n"); fprintf(stderr, "%s\r\n", strerror(errno)); @@ -245,7 +242,7 @@ guard( err = td_err; } } - gd.guard_p = 0; + _gd_state.guard_p = 0; } exit: diff --git a/rust/ares_guard/c-src/guard.h b/rust/ares_guard/c-src/guard.h index d5dad891..bcbffb26 100644 --- a/rust/ares_guard/c-src/guard.h +++ b/rust/ares_guard/c-src/guard.h @@ -5,26 +5,15 @@ #include /** - * Linked list stack of jump buffers. - */ -typedef struct GD_buflistnode GD_buflistnode; -struct GD_buflistnode { - jmp_buf buffer; - GD_buflistnode *next; -}; - -/** - * Return codes and flags. - * - * The flags are bitwise added to the errno of their respective errors. + * Error codes. */ typedef enum { - guard_null = 1, // null stack or alloc pointer - guard_signal = 2, // invalid signal - guard_oom = 3, // out of memory - guard_malloc = 0x10000000, // malloc error flag - guard_mprotect = 0x20000000, // mprotect error flag - guard_sigaction = 0x40000000, // sigaction error flag + guard_null, // null stack or alloc pointer + guard_signal, // invalid signal + guard_oom, // out of memory + guard_malloc, // malloc error + guard_mprotect, // mprotect error + guard_sigaction, // sigaction error } guard_err; /** @@ -49,13 +38,14 @@ typedef enum { * function may mutate. * - The callback function may be interrupted in the case of memory exhaustion * or other `guard_err` error (failure to `mprotect`, `malloc`, etc.). - * - `SIGSEGV` signals are expected to be raised only on guard page accesses. + * - `SIGSEGV` (`SIGBUS` on macOS) signals are expected to be raised only on + * guard page accesses. * * Invariants: * - A single guard page is installed and maintained in the approximate center * until `crate::guard::call_with_guard` returns. * - A return value is only written to `*ret` on successful callback execution. - * - A `guard_err` is returned, excepting panics or negative assertions. + * - A `guard_err` is returned. * * Enhancements: * - Use only a single, static jump buffer variable instead of a linked list. @@ -69,9 +59,9 @@ typedef enum { * @param a_pp A pointer to the allocation pointer location. * @param ret A pointer to a location where the callback's result can be stored. * - * @return A `guard_err` error code. + * @return 0 on callback success; otherwise `guard_err` error code. */ -guard_err +uint32_t guard( void *(*f)(void *), void *closure, From dca588693b3551cccc200ed37f3fc5827b606c2e Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Wed, 21 Feb 2024 11:12:28 -0500 Subject: [PATCH 58/58] guard: minors --- rust/ares_guard/c-src/guard.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/rust/ares_guard/c-src/guard.c b/rust/ares_guard/c-src/guard.c index 4eec53cb..e296201a 100644 --- a/rust/ares_guard/c-src/guard.c +++ b/rust/ares_guard/c-src/guard.c @@ -147,15 +147,8 @@ _register_handler(GD_state *gd) { struct sigaction sa; - // Flag to use sa_sigaction instead of sa_handler sa.sa_flags = SA_SIGINFO; -// Must use sa_sigaction; sa-handler takes signal handler as its only argument sa.sa_sigaction = _signal_handler; - // XX: By default the signal that triggered the signal handler is automatically added to the - // mask while it's being handled, so unless we plan to add more signals to this then I don't - // think it's necessary. - // sigemptyset(&sa.sa_mask); - // sigaddset(&(sa.sa_mask), GD_SIGNAL); if (sigaction(GD_SIGNAL, &sa, &(gd->prev_sa))) { fprintf(stderr, "guard: register: sigaction error\r\n"); @@ -193,7 +186,7 @@ guard( // Register guard page signal handler. if ((err = _register_handler(&_gd_state))) { fprintf(stderr, "guard: registration error\r\n"); - goto clean; + goto tidy; } } else { assert(_gd_state.buffer_list != NULL); @@ -231,7 +224,7 @@ guard( } } -clean: +tidy: // Unmark guard page. assert(_gd_state.guard_p != 0); td_err = _protect_page((void *)_gd_state.guard_p, PROT_READ | PROT_WRITE);