#![recursion_limit = "1024"]
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate failure;
extern crate errno;
extern crate libc;
pub use error::{Error, Result};
pub use lock::*;
pub use protection::Protection;
pub use view::*;
mod error;
mod lock;
mod os;
pub mod page;
mod protection;
mod view;
#[derive(Debug, Clone, Copy)]
pub struct Region {
pub base: *const u8,
pub guarded: bool,
pub protection: Protection,
pub shared: bool,
pub size: usize,
}
impl Region {
pub fn lower(&self) -> usize {
self.base as usize
}
pub fn upper(&self) -> usize {
self.lower() + self.size
}
}
pub fn query(address: *const u8) -> error::Result<Region> {
if address.is_null() {
Err(error::Error::Null)?;
}
os::get_region(page::floor(address as usize) as *const u8)
}
pub fn query_range(address: *const u8, size: usize) -> error::Result<Vec<Region>> {
let mut result = Vec::new();
let mut base = page::floor(address as usize);
let limit = address as usize + size;
loop {
let region = query(base as *const u8)?;
result.push(region);
base = region.upper();
if limit <= region.upper() {
break;
}
}
Ok(result)
}
pub unsafe fn protect(
address: *const u8,
size: usize,
protection: Protection,
) -> error::Result<()> {
if address.is_null() {
Err(error::Error::Null)?;
}
os::set_protection(
page::floor(address as usize) as *const u8,
page::size_from_range(address, size),
protection,
)
}
#[cfg(test)]
mod tests {
extern crate memmap;
use self::memmap::MmapMut;
use super::*;
pub fn alloc_pages(prots: &[Protection]) -> MmapMut {
let pz = page::size();
let map = MmapMut::map_anon(pz * prots.len()).unwrap();
let mut base = map.as_ptr();
for protection in prots {
unsafe {
protect(base, pz, *protection).unwrap();
base = base.offset(pz as isize);
}
}
map
}
#[test]
fn query_null() {
assert!(query(::std::ptr::null()).is_err());
}
#[test]
#[cfg(unix)]
fn query_code() {
let region = query(&query_code as *const _ as *const u8).unwrap();
assert_eq!(region.guarded, false);
assert_eq!(region.protection, Protection::ReadExecute);
assert_eq!(region.shared, false);
}
#[test]
fn query_alloc() {
let size = page::size() * 2;
let mut map = alloc_pages(&[Protection::ReadExecute, Protection::ReadExecute]);
let region = query(map.as_ptr()).unwrap();
assert_eq!(region.guarded, false);
assert_eq!(region.protection, Protection::ReadExecute);
assert!(!region.base.is_null() && region.base <= map.as_mut_ptr());
assert!(region.size >= size);
}
#[test]
fn query_area_zero() {
let region = query_range(&query_area_zero as *const _ as *const u8, 0).unwrap();
assert_eq!(region.len(), 1);
}
#[test]
fn query_area_overlap() {
let pz = page::size();
let prots = [Protection::ReadExecute, Protection::ReadWrite];
let map = alloc_pages(&prots);
let address = unsafe { map.as_ptr().offset(pz as isize - 1) };
let result = query_range(address, 2).unwrap();
assert_eq!(result.len(), prots.len());
for i in 0..prots.len() {
assert_eq!(result[i].protection, prots[i]);
}
}
#[test]
fn query_area_alloc() {
let pz = page::size();
let prots = [
Protection::Read,
Protection::ReadWrite,
Protection::ReadExecute,
];
let map = alloc_pages(&prots);
let result = query_range(map.as_ptr(), pz).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].protection, prots[0]);
let result = query_range(map.as_ptr(), pz * prots.len()).unwrap();
assert_eq!(result.len(), prots.len());
assert_eq!(result[1].size, pz);
for i in 0..prots.len() {
assert_eq!(result[i].protection, prots[i]);
}
}
#[test]
fn protect_null() {
assert!(unsafe { protect(::std::ptr::null(), 0, Protection::None) }.is_err());
}
#[test]
fn protect_code() {
let address = &mut protect_code as *mut _ as *mut u8;
unsafe {
protect(address, 0x10, Protection::ReadWriteExecute).unwrap();
*address = 0x90;
}
}
#[test]
fn protect_alloc() {
let mut map = alloc_pages(&[Protection::Read]);
unsafe {
protect(map.as_ptr(), page::size(), Protection::ReadWrite).unwrap();
*map.as_mut_ptr() = 0x1;
}
}
#[test]
fn protect_overlap() {
let pz = page::size();
let prots = [
Protection::Read,
Protection::ReadExecute,
Protection::ReadWrite,
Protection::Read,
];
let map = alloc_pages(&prots);
let base_exec = unsafe { map.as_ptr().offset(pz as isize) };
let straddle = unsafe { base_exec.offset(pz as isize - 1) };
unsafe { protect(straddle, 2, Protection::ReadWriteExecute).unwrap() };
let result = query_range(base_exec, pz * 2).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].protection, Protection::ReadWriteExecute);
assert_eq!(result[0].size, pz * 2);
}
}