This commit is contained in:
nattthebear 2020-06-20 18:07:17 -04:00
parent c6694a5e29
commit 3f6ffeb573
5 changed files with 128 additions and 25 deletions

View File

@ -6,6 +6,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.71"
@ -27,6 +33,7 @@ name = "waterboxhost"
version = "0.1.0"
dependencies = [
"bitflags",
"lazy_static",
"libc",
"page_size",
"winapi",

View File

@ -10,9 +10,10 @@ publish = false
[dependencies]
bitflags = "1.2.1"
page_size = "0.4.2"
lazy_static = "1.4.0"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.8", features = ["memoryapi", "handleapi", "errhandlingapi"] }
winapi = { version = "0.3.8", features = ["memoryapi", "handleapi", "errhandlingapi", "winnt"] }
[target.'cfg(unix)'.dependencies]
libc = "0.2.71"

View File

@ -1,5 +1,6 @@
mod pageblock;
mod pal;
mod tripguard;
use pageblock::PageBlock;
use bitflags::bitflags;
@ -18,7 +19,6 @@ bitflags! {
}
}
enum Snapshot {
None,
ZeroFilled,

View File

@ -15,7 +15,7 @@ impl PageBlock {
panic!("PageBlock could not allocate memory!");
} else {
PageBlock {
ptr: NonNull::new_unchecked(ptr as *mut u8)
ptr: NonNull::new_unchecked(ptr as *mut u8),
}
}
}
@ -84,7 +84,6 @@ unsafe fn free(ptr: *mut c_void) -> bool {
fn basic_test() {
let mut s = PageBlock::new();
for x in s.slice().iter() {
assert!(*x == 0);
}

View File

@ -1,27 +1,40 @@
static global_data = Mutex::new(GlobalData {
initialized: false,
activeBlocks: Vec::new(),
});
use std::ptr::null_mut;
use super::MemoryBlock;
use std::sync::Mutex;
use crate::*;
use super::*;
use lazy_static::lazy_static;
lazy_static! {
static ref global_data: Mutex<GlobalData> = Mutex::new(GlobalData {
initialized: false,
active_blocks: Vec::new(),
});
}
struct GlobalData {
initialized: bool,
activeBlocks: Vec<*mut MemoryBlock>,
active_blocks: Vec<MemoryBlockRef>,
}
struct MemoryBlockRef(*mut MemoryBlock);
unsafe impl Send for MemoryBlockRef {
}
pub unsafe fn register(block: *mut MemoryBlock) {
unsafe fn register(block: *mut MemoryBlock) {
let mut data = global_data.lock().unwrap();
if !data.initialized {
initialize();
data.initialized = true;
}
data.activeBlocks.push(block);
data.active_blocks.push(MemoryBlockRef(block));
}
pub unsafe fn unregister(block: *mut MemoryBlock) {
unsafe fn unregister(block: *mut MemoryBlock) {
let mut data = global_data.lock().unwrap();
let pos = data.activeBlocks.into_iter().position(|x| x == block).unwrap();
data.activeBlocks.remove(pos);
let pos = data.active_blocks.iter().position(|x| x.0 == block).unwrap();
data.active_blocks.remove(pos);
}
enum TripResult {
@ -30,19 +43,102 @@ enum TripResult {
}
unsafe fn trip(addr: usize) -> TripResult {
let mut data = global_data.lock().unwrap();
let mut memoryBlock = match data.activeBlocks
.into_iter()
.find(|x| addr >= x.start && addr < x.end) {
Some(x) => x,
None => return NotHandled,
}
let pageStartAddr = addr & ~PAGEMASK;
let mut page = &mut memoryBlock.pages[(addr - memoryBlock.start) >> PAGESHIFT];
let data = global_data.lock().unwrap();
let memory_block = match data.active_blocks
.iter()
.find(|x| addr >= (*x.0).start && addr < (*x.0).end) {
Some(x) => &mut *x.0,
None => return TripResult::NotHandled,
};
let page_start_addr = addr & !PAGEMASK;
let page = &mut memory_block.pages[(addr - memory_block.start) >> PAGESHIFT];
if !page.flags.contains(PageFlags::W) {
NotHandled
return TripResult::NotHandled
}
if memoryBlock.sealed && page.snapshot == Snapshot::None {
if memory_block.sealed && match page.snapshot { Snapshot::None => true, _ => false } {
// take snapshot now
let mut snapshot = PageBlock::new();
let src = std::slice::from_raw_parts(page_start_addr as *const u8, PAGESIZE);
let dst = snapshot.slice_mut();
dst.copy_from_slice(src);
page.snapshot = Snapshot::Data(snapshot);
}
page.flags.insert(PageFlags::DIRTY);
let new_prot = if page.flags.contains(PageFlags::X) { pal::Protection::RWX } else { pal::Protection::RW };
assert!(pal::protect(page_start_addr, PAGESIZE, new_prot));
TripResult::Handled
}
#[cfg(windows)]
fn initialize() {
use winapi::um::errhandlingapi::*;
use winapi::um::winnt::*;
use winapi::vc::excpt::*;
unsafe extern "system" fn handler(p_info: *mut EXCEPTION_POINTERS) -> i32 {
let p_record = &mut *(*p_info).ExceptionRecord;
let flags = p_record.ExceptionInformation[0];
if p_record.ExceptionCode != STATUS_ACCESS_VIOLATION // only trigger on access violations...
|| (flags & 1) != 0 { // ...due to a write attempts
return EXCEPTION_CONTINUE_SEARCH
}
let fault_address = p_record.ExceptionInformation[1] as usize;
match trip(fault_address) {
TripResult::Handled => EXCEPTION_CONTINUE_EXECUTION,
TripResult::NotHandled => EXCEPTION_CONTINUE_SEARCH,
}
}
unsafe {
let res = AddVectoredExceptionHandler(1 /* CALL_FIRST */, Some(handler));
assert!(res != null_mut(), "AddVectoredExceptionHandler failed");
}
}
#[cfg(unix)]
type sa_handler = unsafe extern fn(i32) -> ();
#[cfg(unix)]
type sa_sigaction = unsafe extern fn(i32, *const siginfo_t, *const ucontext_t) -> ();
#[cfg(unix)]
use libc::*;
#[cfg(unix)]
static mut altstack: [u8; SIGSTKSZ] = [0; SIGSTKSZ];
#[cfg(unix)]
static mut sa_old: Option<Box<sigaction>> = None;
#[cfg(unix)]
fn initialize() {
use std::mem::{transmute, zeroed};
unsafe extern fn handler(sig: i32, info: *const siginfo_t, ucontext: *const ucontext_t) {
let faultAddress = (*info).si_addr() as usize;
let write = (*ucontext).uc_mcontext.gregs[REG_ERR as usize] & 2 != 0;
let rethrow = !write || match trip(faultAddress) {
TripResult::NotHandled => true,
_ => false
};
if rethrow {
if sa_old.as_ref().unwrap().sa_flags & SA_SIGINFO != 0 {
transmute::<usize, sa_sigaction>(sa_old.as_ref().unwrap().sa_sigaction)(sig, info, ucontext);
} else {
transmute::<usize, sa_handler>(sa_old.as_ref().unwrap().sa_sigaction)(sig);
}
abort();
}
}
unsafe {
sa_old = Some(Box::new(zeroed::<sigaction>()));
let ss = stack_t {
ss_flags: 0,
ss_sp: &mut altstack[0] as *mut u8 as *mut c_void,
ss_size: SIGSTKSZ
};
assert!(sigaltstack(&ss, null_mut()) == 0, "sigaltstack failed");
let mut sa = sigaction {
sa_mask: zeroed::<sigset_t>(),
sa_sigaction: transmute::<sa_sigaction, usize>(handler),
sa_flags: SA_ONSTACK | SA_SIGINFO,
sa_restorer: None,
};
sigfillset(&mut sa.sa_mask);
assert!(sigaction(SIGSEGV, &sa, &mut **sa_old.as_mut().unwrap() as *mut sigaction) == 0, "sigaction failed");
}
}