From b0007c83f4e3b497320c8526b31197bb6e4dc987 Mon Sep 17 00:00:00 2001 From: nattthebear Date: Sun, 21 Jun 2020 14:08:27 -0400 Subject: [PATCH] more musings --- waterbox/waterboxhost/Cargo.lock | 158 +++++++++++ waterbox/waterboxhost/Cargo.toml | 2 + waterbox/waterboxhost/src/lib.rs | 7 + waterbox/waterboxhost/src/memory_block/mod.rs | 253 +++++++++++++++++- .../src/memory_block/pageblock.rs | 1 + waterbox/waterboxhost/src/memory_block/pal.rs | 19 +- .../src/memory_block/tripguard.rs | 6 +- 7 files changed, 427 insertions(+), 19 deletions(-) diff --git a/waterbox/waterboxhost/Cargo.lock b/waterbox/waterboxhost/Cargo.lock index 5792e80d99..353875a48b 100644 --- a/waterbox/waterboxhost/Cargo.lock +++ b/waterbox/waterboxhost/Cargo.lock @@ -6,6 +6,33 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "getset" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -18,6 +45,15 @@ version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + [[package]] name = "page_size" version = "0.4.2" @@ -28,14 +64,136 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "syn-mid", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "smallvec" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" + +[[package]] +name = "syn" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a994520748611c17d163e81b6c4a4b13d11b7f63884362ab2efac3aa9cf16d00" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + [[package]] name = "waterboxhost" version = "0.1.0" dependencies = [ "bitflags", + "getset", "lazy_static", "libc", "page_size", + "parking_lot", "winapi", ] diff --git a/waterbox/waterboxhost/Cargo.toml b/waterbox/waterboxhost/Cargo.toml index a4cbc09a0d..44731392e0 100644 --- a/waterbox/waterboxhost/Cargo.toml +++ b/waterbox/waterboxhost/Cargo.toml @@ -11,6 +11,8 @@ publish = false bitflags = "1.2.1" page_size = "0.4.2" lazy_static = "1.4.0" +getset = "0.1.1" +parking_lot = "0.10.2" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.8", features = ["memoryapi", "handleapi", "errhandlingapi", "winnt"] } diff --git a/waterbox/waterboxhost/src/lib.rs b/waterbox/waterboxhost/src/lib.rs index 733fd30984..daa84d514c 100644 --- a/waterbox/waterboxhost/src/lib.rs +++ b/waterbox/waterboxhost/src/lib.rs @@ -1,11 +1,18 @@ #![crate_type = "cdylib"] +use std::io::{Read, Write}; + const PAGESIZE: usize = 0x1000; const PAGEMASK: usize = 0xfff; const PAGESHIFT: i32 = 12; mod memory_block; +pub trait IStateable { + fn save_sate(&mut self, stream: Box); + fn load_state(&mut self, stream: Box); +} + #[cfg(test)] mod tests { #[test] diff --git a/waterbox/waterboxhost/src/memory_block/mod.rs b/waterbox/waterboxhost/src/memory_block/mod.rs index 5f5dfd4984..415df7925a 100644 --- a/waterbox/waterboxhost/src/memory_block/mod.rs +++ b/waterbox/waterboxhost/src/memory_block/mod.rs @@ -2,8 +2,26 @@ mod pageblock; mod pal; mod tripguard; +use std::ops::{DerefMut, Deref}; +use parking_lot::ReentrantMutex; +use std::collections::HashMap; +use std::sync::Mutex; use pageblock::PageBlock; use bitflags::bitflags; +use crate::*; +use getset::Getters; +use lazy_static::lazy_static; + +lazy_static! { + static ref LOCK_LIST: Mutex>>> = Mutex::new(HashMap::new()); +} + +fn alignDown(p: usize) -> usize { + p & !PAGEMASK +} +fn alignUp(p: usize) -> usize { + ((p - 1) | PAGEMASK) + 1 +} bitflags! { struct PageFlags: u32 { @@ -18,7 +36,16 @@ bitflags! { const STACK = 32; } } +pub enum Protection { + None, + R, + RW, + RX, + RWX, + RWStack +} +#[derive(Debug)] enum Snapshot { None, ZeroFilled, @@ -26,15 +53,231 @@ enum Snapshot { } /// Information about a single page of memory +#[derive(Debug)] struct Page { pub flags: PageFlags, pub snapshot: Snapshot, } +#[derive(Getters)] +#[derive(Debug)] struct MemoryBlock { - pub pages: Vec, - pub start: usize, - pub length: usize, - pub end: usize, - pub sealed: bool, + #[get] + pages: Vec, + #[get] + start: usize, + #[get] + length: usize, + #[get] + end: usize, + #[get] + sealed: bool, + + lock_index: u32, + handle: pal::Handle, + lock_count: u32, +} + +struct MemoryBlockGuard<'a> { + block: &'a mut MemoryBlock, +} +impl<'a> Drop for MemoryBlockGuard<'a> { + fn drop(&mut self) { + self.block.deactivate(); + } +} +impl<'a> Deref for MemoryBlockGuard<'a> { + type Target = MemoryBlock; + fn deref(&self) -> &MemoryBlock { + self.block + } +} +impl<'a> DerefMut for MemoryBlockGuard<'a> { + fn deref_mut(&mut self) -> &mut MemoryBlock { + self.block + } +} + +impl MemoryBlock { + pub fn new(start: usize, length: usize) -> MemoryBlock { + if start != alignDown(start) || length != alignDown(length) { + panic!("Addresses and sizes must be aligned!"); + } + let end = start + length; + if start >> 32 != (end - 1) >> 32 { + panic!("MemoryBlock must fit into a single 4G region!"); + } + let npage = length >> PAGESHIFT; + let mut pages = Vec::new(); + pages.reserve_exact(npage); + for _ in 0..npage { + pages.push(Page { + flags: PageFlags::empty(), + snapshot: Snapshot::None, + }); + } + let handle = pal::open(length).unwrap(); + let lock_index = (start >> 32) as u32; + // add the lock_index stuff now, so we won't have to check for it later on activate / drop + { + let map = &mut LOCK_LIST.lock().unwrap(); + map.entry(lock_index).or_insert(ReentrantMutex::new(None)); + } + + MemoryBlock { + pages, + start, + length, + end, + sealed: false, + + lock_index, + handle, + lock_count: 0, + } + } + + pub fn enter(&mut self) -> MemoryBlockGuard { + self.activate(); + MemoryBlockGuard { + block: self, + } + } + + /// lock self, and potentially swap this block into memory + pub fn activate(&mut self) { + unsafe { + let map = &mut LOCK_LIST.lock().unwrap(); + let lock = map.get_mut(&self.lock_index).unwrap(); + std::mem::forget(lock.lock()); + let other_opt = lock.get_mut(); + match *other_opt { + Some(MemoryBlockRef(other)) => { + if other != self { + assert!(!(*other).active()); + (*other).swapout(); + self.swapin(); + *other_opt = Some(MemoryBlockRef(self)); + } + }, + None => { + self.swapin(); + *other_opt = Some(MemoryBlockRef(self)); + } + } + self.lock_count += 1; + } + } + /// unlock self, and potentially swap this block out of memory + pub fn deactivate(&mut self) { + unsafe { + assert!(self.active()); + let map = &mut LOCK_LIST.lock().unwrap(); + let lock = map.get_mut(&self.lock_index).unwrap(); + self.lock_count -= 1; + #[cfg(debug_assertions)] + { + let other_opt = lock.get_mut(); + let other = other_opt.as_ref().unwrap().0; + assert_eq!(&*other, self); + // in debug mode, forcibly evict to catch dangling pointers + if !self.active() { + self.swapout(); + *other_opt = None; + } + } + lock.force_unlock(); + } + } + + unsafe fn swapin(&mut self) { + assert!(pal::map(&self.handle, self.start, self.length)); + } + unsafe fn swapout(&mut self) { + assert!(pal::unmap(self.start, self.length)); + } + + pub fn active (&self) -> bool { + self.lock_count > 0 + } + + pub fn protect(&mut self, start: usize, length: usize, prot: Protection) { + + } + pub fn seal(&mut self) { + + } +} + +impl IStateable for MemoryBlock { + fn save_sate(&mut self, stream: Box) { + + } + fn load_state(&mut self, stream: Box) { + + } +} + +impl Drop for MemoryBlock { + fn drop(&mut self) { + assert!(!self.active()); + let map = &mut LOCK_LIST.lock().unwrap(); + let lock = map.get_mut(&self.lock_index).unwrap(); + let other_opt = lock.get_mut(); + match *other_opt { + Some(MemoryBlockRef(other)) => { + if other == self { + unsafe { + self.swapout(); + } + *other_opt = None; + } + }, + None => () + } + let mut h = pal::bad(); + std::mem::swap(&mut h, &mut self.handle); + unsafe { + pal::close(h); + } + } +} + +impl PartialEq for MemoryBlock { + fn eq(&self, other: &MemoryBlock) -> bool { + self as *const MemoryBlock == other as *const MemoryBlock + } +} +impl Eq for MemoryBlock {} + +struct MemoryBlockRef(*mut MemoryBlock); +unsafe impl Send for MemoryBlockRef {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_basic() { + drop(MemoryBlock::new(0x36300000000, 0x50000)); + drop(MemoryBlock::new(0x36b00000000, 0x2000)); + { + let mut b = MemoryBlock::new(0x36100000000, 0x65000); + b.activate(); + b.deactivate(); + b.enter(); + } + { + let mut b = MemoryBlock::new(0x36e00000000, 0x5000); + b.activate(); + b.activate(); + let mut guard = b.enter(); + guard.activate(); + guard.deactivate(); + drop(guard); + b.deactivate(); + b.deactivate(); + b.enter(); + } + } } diff --git a/waterbox/waterboxhost/src/memory_block/pageblock.rs b/waterbox/waterboxhost/src/memory_block/pageblock.rs index b9ded822da..52b42b0753 100644 --- a/waterbox/waterboxhost/src/memory_block/pageblock.rs +++ b/waterbox/waterboxhost/src/memory_block/pageblock.rs @@ -3,6 +3,7 @@ use core::ffi::c_void; use crate::*; /// wraps the allocation of a single PAGESIZE bytes of ram, and is safe-ish to call within a signal handler +#[derive(Debug)] pub struct PageBlock { ptr: NonNull, } diff --git a/waterbox/waterboxhost/src/memory_block/pal.rs b/waterbox/waterboxhost/src/memory_block/pal.rs index 82b091a179..962df67995 100644 --- a/waterbox/waterboxhost/src/memory_block/pal.rs +++ b/waterbox/waterboxhost/src/memory_block/pal.rs @@ -1,16 +1,9 @@ // Platform abstraction layer over mmap/etc. Doesn't do much checking, not meant for general consumption +use super::Protection; +#[derive(Debug)] pub struct Handle(usize); -pub enum Protection { - None, - R, - RW, - RX, - RWX, - RWStack -} - #[cfg(windows)] pub use win::*; #[cfg(windows)] @@ -52,6 +45,10 @@ mod win { CloseHandle(handle.0 as *mut c_void) != 0 } + pub fn bad() -> Handle { + return Handle(INVALID_HANDLE_VALUE as usize); + } + pub fn map(handle: &Handle, start: usize, size: usize) -> bool { unsafe { let res = MapViewOfFileEx( @@ -124,6 +121,10 @@ mod nix { libc::close(handle.0 as i32) == 0 } + pub fn bad() -> Handle { + return Handle(-1i32 as usize); + } + pub fn map(handle: &Handle, start: usize, size: usize) -> bool { unsafe { let res = mmap(start as *mut c_void, diff --git a/waterbox/waterboxhost/src/memory_block/tripguard.rs b/waterbox/waterboxhost/src/memory_block/tripguard.rs index 2bbf35247d..d6c7fe6f5e 100644 --- a/waterbox/waterboxhost/src/memory_block/tripguard.rs +++ b/waterbox/waterboxhost/src/memory_block/tripguard.rs @@ -17,10 +17,6 @@ struct GlobalData { initialized: bool, active_blocks: Vec, } -struct MemoryBlockRef(*mut MemoryBlock); -unsafe impl Send for MemoryBlockRef { - -} unsafe fn register(block: *mut MemoryBlock) { let mut data = global_data.lock().unwrap(); @@ -64,7 +60,7 @@ unsafe fn trip(addr: usize) -> TripResult { 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 }; + let new_prot = if page.flags.contains(PageFlags::X) { Protection::RWX } else { Protection::RW }; assert!(pal::protect(page_start_addr, PAGESIZE, new_prot)); TripResult::Handled }