Skip to main content

Region

Struct Region 

Source
pub struct Region { /* private fields */ }
Expand description

An owned region of page-aligned virtual memory, freed when it is dropped.

A region is the unit a JIT or runtime works in: a block of memory whose access permissions can be changed independently of the rest of the address space. It is created ReadWrite so code or data can be written in, then moved to another protection — usually ReadExecute — with protect. Dropping the region returns its pages to the operating system.

The requested length is rounded up to a whole number of page_size bytes, so len is the real, page-aligned size and is at least what was asked for. Optionally a region can be flanked by inaccessible guard pages with with_guard_pages, so a run off either end faults at once instead of silently corrupting a neighbour.

Region is Send and Sync: it owns its mapping the way Box<[u8]> owns its allocation, and every method that touches the bytes takes &self or &mut self, so the borrow checker rules out data races on the safe surface.

§Executing code

Writing machine code into a region and running it is inherently unsafe — the bytes must be valid code for the target and match the calling convention you transmute to. The safe surface here gets the memory into the right state; the final transmute-and-call is the caller’s unsafe responsibility. On architectures with separate instruction and data caches (for example AArch64), freshly written code also needs the instruction cache synchronized before it is run; on x86 and x86-64 this is automatic.

§Examples

Write a byte pattern, read it back, then flip the region to executable (the W^X pattern, minus the architecture-specific call):

use pager_lang::{Protection, Region};

let mut region = Region::new(64)?;
assert!(region.len() >= 64);
assert_eq!(region.protection(), Protection::ReadWrite);

// Fill the first bytes and read them straight back.
region.write(0, &[0x90, 0x90, 0xC3])?;
assert_eq!(&region.as_slice().unwrap()[..3], &[0x90, 0x90, 0xC3]);

// Make it read+execute: it is no longer writable.
region.protect(Protection::ReadExecute)?;
assert!(region.as_mut_slice().is_none());

Implementations§

Source§

impl Region

Source

pub fn new(len: usize) -> Result<Self, PagerError>

Maps a new read/write region of at least len bytes.

The length is rounded up to a whole number of page_size bytes; the region starts out ReadWrite. The bytes are zero-initialized by the operating system.

§Errors
§Examples
use pager_lang::Region;

let region = Region::new(1)?;
// One byte was asked for; a whole page is what you get.
assert_eq!(region.len(), pager_lang::page_size());
Source

pub fn with_guard_pages(len: usize) -> Result<Self, PagerError>

Maps a new read/write region of at least len bytes, flanked on both sides by an inaccessible guard page.

The usable region behaves exactly like one from new: rounded up to whole pages and starting ReadWrite. The difference is the guard page immediately before and after it — both None — so a read or write that runs off either end faults immediately rather than corrupting whatever happened to be mapped next. has_guard_pages reports true. len, as_ptr, and the slice accessors all describe the usable region only; the guards are never exposed.

§Errors
§Examples
use pager_lang::Region;

let region = Region::with_guard_pages(128)?;
assert!(region.has_guard_pages());
assert!(region.len() >= 128);
Source

pub fn len(&self) -> usize

The length of the usable region in bytes — a whole number of pages, at least the size requested.

Source

pub fn is_empty(&self) -> bool

Whether the region is empty. Always false: a region spans at least one page. Present so the type reads naturally alongside len.

Source

pub fn protection(&self) -> Protection

The region’s current protection.

Source

pub fn has_guard_pages(&self) -> bool

Whether the region is flanked by guard pages (created with with_guard_pages).

Source

pub fn as_ptr(&self) -> *const u8

A raw const pointer to the start of the usable region.

Always valid to obtain; dereferencing or transmuting it (for example to a function pointer to run code in the region) is unsafe and the caller’s responsibility.

Source

pub fn as_mut_ptr(&mut self) -> *mut u8

A raw mutable pointer to the start of the usable region.

Always valid to obtain; dereferencing it is unsafe and only sound while the region’s protection permits the access.

Source

pub fn as_slice(&self) -> Option<&[u8]>

Borrows the region as a byte slice, or None if it is not currently readable.

Returns Some exactly when protection is readable (everything but Protection::None). The None case is what keeps this safe: a region flipped to a no-access protection cannot be read through a slice.

§Examples
use pager_lang::{Protection, Region};

let mut region = Region::new(32)?;
assert!(region.as_slice().is_some());

region.protect(Protection::None)?;
assert!(region.as_slice().is_none());
Source

pub fn as_mut_slice(&mut self) -> Option<&mut [u8]>

Borrows the region as a mutable byte slice, or None if it is not currently writable.

Returns Some exactly when protection is writable (ReadWrite or ReadWriteExecute). Use this to fill a region in bulk; for a single placed write, write does the bounds checking for you.

§Examples
use pager_lang::{Protection, Region};

let mut region = Region::new(8)?;
region.as_mut_slice().unwrap().fill(0xAB);
assert!(region.as_slice().unwrap().iter().all(|&b| b == 0xAB));

region.protect(Protection::ReadExecute)?;
assert!(region.as_mut_slice().is_none());
Source

pub fn write(&mut self, offset: usize, bytes: &[u8]) -> Result<(), PagerError>

Copies bytes into the region starting at offset.

The common way to load machine code or data: it checks that the region is writable and that the write fits, then copies. Writing an empty slice is a no-op.

§Errors
§Examples
use pager_lang::{PagerError, Region};

let mut region = Region::new(16)?;
region.write(4, &[1, 2, 3, 4])?;
assert_eq!(&region.as_slice().unwrap()[4..8], &[1, 2, 3, 4]);

// A write past the end is rejected, not truncated.
let len = region.len();
assert!(matches!(
    region.write(len, &[0]),
    Err(PagerError::OutOfBounds { .. })
));
Source

pub fn protect(&mut self, protection: Protection) -> Result<(), PagerError>

Changes the region’s protection.

Applies to the usable region only; any guard pages are left untouched. Setting the protection it already has is a no-op that returns Ok. This is the W^X flip: write code under ReadWrite, then protect to ReadExecute before running it.

§Errors

PagerError::Protect if the operating system refuses the change — most often a platform that forbids writable-and-executable pages rejecting Protection::ReadWriteExecute.

§Examples
use pager_lang::{Protection, Region};

let mut region = Region::new(64)?;
region.protect(Protection::ReadExecute)?;
assert_eq!(region.protection(), Protection::ReadExecute);

Trait Implementations§

Source§

impl Debug for Region

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for Region

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl Send for Region

Source§

impl Sync for Region

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.