Merge branch 'as/serf-guard' into msl/guard

- manual
- compiles
- tests fail
- doesn't run
This commit is contained in:
Alex Shelkovnykov 2024-02-14 12:50:37 +09:00
parent 42ffde2208
commit 1df75a2cab
15 changed files with 635 additions and 675 deletions

10
rust/ares/Cargo.lock generated
View File

@ -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"

View File

@ -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"

File diff suppressed because it is too large Load Diff

View File

@ -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<Noun, Error> {
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 {

View File

@ -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

View File

@ -15,19 +15,11 @@
#define GD_PAGE_MASK (GD_PAGE_SIZE - 1)
#define GD_PAGE_ROUND_DOWN(foo) (foo & (~GD_PAGE_MASK))
/**
* XX: documentation
*/
typedef struct _gs {
uintptr_t guard_p;
const uintptr_t *stack_pp;
const uintptr_t *alloc_pp;
jmp_buf env_buffer;
struct sigaction prev_sa;
} GuardState;
static GuardState *_guard_state = NULL;
static uintptr_t guard_p;
static const uintptr_t *stack_pp;
static const uintptr_t *alloc_pp;
static jmp_buf env_buffer;
static struct sigaction prev_sa;
static int32_t
_prot_page(void *address, int prot)
@ -53,18 +45,21 @@ _unmark_page(void *address)
return _prot_page(address, PROT_READ | PROT_WRITE);
}
/**
* Center the guard page.
*/
// Center the guard page.
// XX: could be a false positive if the new frame results in exact same guard page
// solution: we only re-center from the signal handler
static int32_t
_focus_guard(
uintptr_t *guard_pp,
const uintptr_t stack_p,
const uintptr_t alloc_p
) {
// Check for strange situations.
_focus_guard()
{
uintptr_t stack_p = *stack_pp;
uintptr_t alloc_p = *alloc_pp;
uintptr_t old_guard_p = guard_p;
uintptr_t new_guard_p;
int32_t err = 0;
fprintf(stderr, "guard: focus: stack pointer at %p\r\n", (void *)stack_p);
fprintf(stderr, "guard: focus: alloc pointer at %p\r\n", (void *)alloc_p);
if (stack_p == 0 || alloc_p == 0) {
fprintf(stderr, "guard: focus: stack or alloc pointer is null\r\n");
return guard_null;
@ -73,20 +68,8 @@ _focus_guard(
return guard_oom;
}
uintptr_t old_guard_p = *guard_pp;
uintptr_t new_guard_p;
int32_t err = 0;
fprintf(stderr, "guard: focus: old guard = %p\r\n", (void *)old_guard_p);
// Unmark the old guard page (if there is one)
if (old_guard_p) {
if ((err = _unmark_page((void *)old_guard_p))) {
fprintf(stderr, "guard: focus: unmark error\r\n");
return err;
}
}
// Compute new guard page
// XX: Should we also check for new_guard_p < min(stack_p, alloc_p)?
new_guard_p = GD_PAGE_ROUND_DOWN((stack_p + alloc_p) / 2);
@ -104,45 +87,50 @@ _focus_guard(
// Update guard page tracker
fprintf(stderr, "guard: focus: installed guard page at %p\r\n", (void *)new_guard_p);
*guard_pp = new_guard_p;
guard_p = new_guard_p;
// Unmark the old guard page (if there is one)
if (old_guard_p) {
if ((err = _unmark_page((void *)old_guard_p))) {
fprintf(stderr, "guard: focus: unmark error\r\n");
return err;
}
}
return 0;
}
void
static void
_signal_handler(int sig, siginfo_t *si, void *unused)
{
uintptr_t sig_addr;
int32_t err = 0;
assert(guard_p);
fprintf(stderr, "guard: sig_handle: %d received\r\n", sig);
if (sig != SIGSEGV) {
fprintf(stderr, "guard: sig_handle: invalid signal\r\n");
siglongjmp(_guard_state->env_buffer, guard_signal);
// XX: do we even want to jump? if this is fatal error, maybe just die now
siglongjmp(env_buffer, guard_signal);
}
sig_addr = (uintptr_t)si->si_addr;
fprintf(stderr, "guard: SIGSEGV address = %p\r\n", (void *)sig_addr);
fprintf(stderr, "guard: sig_handle: %p \r\n", _guard_state);
if (
sig_addr >= _guard_state->guard_p &&
sig_addr < (_guard_state->guard_p + GD_PAGE_SIZE))
if (sig_addr >= guard_p &&
sig_addr < guard_p + GD_PAGE_SIZE)
{
fprintf(stderr, "guard: page at %p hit\r\n", (void *)_guard_state->guard_p);
err = _focus_guard(
&(_guard_state->guard_p),
*(_guard_state->stack_pp),
*(_guard_state->alloc_pp));
fprintf(stderr, "guard: hit: %p\r\n", si->si_addr);
err = _focus_guard();
if (err) {
fprintf(stderr, "guard: sig_handle: focus error\r\n");
siglongjmp(_guard_state->env_buffer, err);
siglongjmp(env_buffer, err);
}
} else {
struct sigaction prev_sa = _guard_state->prev_sa;
fprintf(stderr, "guard: page at %p miss\r\n", (void *)_guard_state->guard_p);
}
else {
fprintf(stderr, "guard: page at %p miss\r\n", (void *)guard_p);
if (prev_sa.sa_sigaction != NULL) {
prev_sa.sa_sigaction(sig, si, unused);
@ -155,8 +143,8 @@ _signal_handler(int sig, siginfo_t *si, void *unused)
}
}
int32_t
_register_handler(struct sigaction *prev_sa)
static int32_t
_register_handler()
{
struct sigaction sa;
@ -171,8 +159,8 @@ _register_handler(struct sigaction *prev_sa)
// sigemptyset(&sa.sa_mask);
// sigaddset(&(sa.sa_mask), SIGSEGV);
// Set the new SIGSEGV handler, and save the old SIGSEGV handler (if any)
if (sigaction(SIGSEGV, &sa, prev_sa)) {
// XX: should assert that prev_sa doesn't have a handler in it, but it's not a pointer so non-trivial
if (sigaction(SIGSEGV, &sa, &prev_sa)) {
fprintf(stderr, "guard: register: sigaction error\r\n");
fprintf(stderr, "%s\r\n", strerror(errno));
return guard_sigaction | errno;
@ -181,107 +169,59 @@ _register_handler(struct sigaction *prev_sa)
return 0;
}
int32_t
_setup(
GuardState **gs_p,
const uintptr_t *const stack_pp,
const uintptr_t *const alloc_pp
) {
GuardState *gs;
int32_t err = 0;
assert(*gs_p == NULL);
fprintf(stderr, "guard: setup: stack pointer at %p\r\n", (void *)(*stack_pp));
fprintf(stderr, "guard: setup: alloc pointer at %p\r\n", (void *)(*alloc_pp));
// Setup guard page state
*gs_p = (GuardState *)malloc(sizeof(GuardState));
gs = *gs_p;
if (gs == NULL) {
fprintf(stderr, "guard: malloc error\r\n");
fprintf(stderr, "%s\r\n", strerror(errno));
return guard_malloc | errno;
}
fprintf(stderr, "guard: state allocated to %p \r\n", gs);
gs->guard_p = 0;
gs->stack_pp = stack_pp;
gs->alloc_pp = alloc_pp;
// Initialize the guard page
if ((err = _focus_guard(&(gs->guard_p), *stack_pp, *alloc_pp))) {
fprintf(stderr, "guard: setup: _focus_guard error\r\n");
return err;
}
// Register guard page signal handler
if ((err = _register_handler(&(gs->prev_sa)))) {
fprintf(stderr, "guard: setup: _register_handler error\r\n");
return err;
}
return 0;
}
int32_t
_teardown(GuardState** gs_p)
{
int32_t err = 0;
if (*gs_p != NULL) {
GuardState *gs = *gs_p;
if (gs->guard_p != 0) {
err = _unmark_page((void *)gs->guard_p);
}
if (sigaction(SIGSEGV, &(gs->prev_sa), NULL)) {
fprintf(stderr, "guard: teardown: sigaction error\r\n");
fprintf(stderr, "%s\r\n", strerror(errno));
err = guard_sigaction | errno;
}
free(gs);
*gs_p = NULL;
}
return err;
}
int32_t
guard(
callback f,
void *closure,
const uintptr_t *const stack_pp,
const uintptr_t *const alloc_pp,
const uintptr_t *const s_pp,
const uintptr_t *const a_pp,
void ** ret
) {
int32_t err = 0;
int32_t td_err;
// Setup the guard page
fprintf(stderr, "guard: setup\r\n");
if ((err = _setup(&_guard_state, stack_pp, alloc_pp))) {
assert(guard_p == 0);
fprintf(stderr, "guard: setup: stack pointer at %p\r\n", (void *)(*stack_pp));
fprintf(stderr, "guard: setup: alloc pointer at %p\r\n", (void *)(*alloc_pp));
guard_p = 0;
stack_pp = s_pp;
alloc_pp = a_pp;
// Initialize the guard page
if ((err = _focus_guard())) {
fprintf(stderr, "guard: setup: _focus_guard error\r\n");
goto done;
}
// Register guard page signal handler
if ((err = _register_handler())) {
fprintf(stderr, "guard: setup: _register_handler error\r\n");
goto done;
}
// Run given closure
fprintf(stderr, "guard: run\r\n");
if (!(err = sigsetjmp(_guard_state->env_buffer, 1))) {
if (!(err = sigsetjmp(env_buffer, 1))) {
*ret = f(closure);
// Clean up
fprintf(stderr, "guard: teardown\r\n");
err = _teardown(&_guard_state);
fprintf(stderr, "guard: return\r\n");
return err;
} else {
done:
// Clean up
fprintf(stderr, "guard: teardown\r\n");
_teardown(&_guard_state);
fprintf(stderr, "guard: return\r\n");
return err;
}
done:
// Clean up
if (guard_p != 0) {
td_err = _unmark_page((void *)guard_p);
}
if (sigaction(SIGSEGV, &prev_sa, NULL)) {
fprintf(stderr, "guard: teardown: sigaction error\r\n");
fprintf(stderr, "%s\r\n", strerror(errno));
td_err = guard_sigaction | errno;
}
if (!err) {
err = td_err;
}
fprintf(stderr, "guard: return\r\n");
return err;
}

View File

@ -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__

View File

@ -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;

View File

@ -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);

View File

@ -91,6 +91,23 @@ pub fn assert_no_alloc<T, F: FnOnce() -> 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, F: FnOnce() -> 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.
///