From 5efbc074e205cb7a768ea4519a5a6f2a3136b5c9 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 14 Feb 2024 19:06:04 +0900 Subject: [PATCH] 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 843a4d8..4ac09d4 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 d7859ca..12bb229 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 4557041..ac37c05 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) { + // 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; + } + } + guard_p = 0; } - 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; - } - +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 ae23815..a2f610f 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 0e2b0aa..8192d56 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)); -} -