From 1f19e418dd03c6b69ff1d48feb0ba685c39aa586 Mon Sep 17 00:00:00 2001 From: Sam Thomas Date: Wed, 13 Mar 2024 02:46:16 +0000 Subject: [PATCH] add basic state impl --- fugue-high/Cargo.toml | 2 + fugue-high/src/eval/fixed_state.rs | 207 +++++++++++++++++++++++++++++ fugue-high/src/eval/mod.rs | 84 +++++++++++- 3 files changed, 286 insertions(+), 7 deletions(-) create mode 100644 fugue-high/src/eval/fixed_state.rs diff --git a/fugue-high/Cargo.toml b/fugue-high/Cargo.toml index 4a9069c..bdba3f6 100644 --- a/fugue-high/Cargo.toml +++ b/fugue-high/Cargo.toml @@ -4,9 +4,11 @@ version = "0.3.0" edition = "2021" [dependencies] +anyhow = "1" bitflags = "2" fugue-arch = { version = "0.3", path = "../fugue-arch" } fugue-bv = { version = "0.3", path = "../fugue-bv" } +fugue-bytes = { version = "0.3", path = "../fugue-bytes" } fugue-ir = { version = "0.3", path = "../fugue-ir" } nom = "7" ouroboros = "0.18" diff --git a/fugue-high/src/eval/fixed_state.rs b/fugue-high/src/eval/fixed_state.rs new file mode 100644 index 0000000..b8bcc9e --- /dev/null +++ b/fugue-high/src/eval/fixed_state.rs @@ -0,0 +1,207 @@ +use fugue_bv::BitVec; +use fugue_bytes::{Endian, Order}; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum FixedStateError { + #[error("out-of-bounds read access; {size} bytes at {offset:#x}")] + OOBRead { offset: usize, size: usize }, + #[error("out-of-bounds write access; {size} bytes at {offset:#x}")] + OOBWrite { offset: usize, size: usize }, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[repr(transparent)] +pub struct FixedState { + pub(crate) backing: Box<[u8]>, +} + +impl AsRef for FixedState { + #[inline(always)] + fn as_ref(&self) -> &Self { + self + } +} + +impl AsMut for FixedState { + #[inline(always)] + fn as_mut(&mut self) -> &mut Self { + self + } +} + +impl From> for FixedState { + fn from(backing: Vec) -> Self { + Self { + backing: backing.into_boxed_slice(), + } + } +} + +impl FixedState { + pub fn new(size: usize) -> Self { + Self { + backing: vec![0u8; size].into_boxed_slice(), + } + } + + #[inline(always)] + pub fn len(&self) -> usize { + self.backing.len() + } + + #[inline(always)] + pub fn read_val( + &self, + offset: impl Into, + size: usize, + ) -> Result { + let view = self.view_bytes(offset, size)?; + + Ok(if O::ENDIAN.is_big() { + BitVec::from_le_bytes(view) + } else { + BitVec::from_le_bytes(view) + }) + } + + #[inline(always)] + pub fn read_val_with( + &self, + offset: impl Into, + size: usize, + endian: Endian, + ) -> Result { + let view = self.view_bytes(offset, size)?; + + Ok(if endian.is_big() { + BitVec::from_le_bytes(view) + } else { + BitVec::from_le_bytes(view) + }) + } + + #[inline(always)] + pub fn write_val( + &mut self, + offset: impl Into, + value: &BitVec, + ) -> Result<(), FixedStateError> { + debug_assert_eq!(value.bits() % 8, 0); + + let size = value.bits() / 8; + let view = self.view_bytes_mut(offset, size)?; + + if O::ENDIAN.is_big() { + value.to_be_bytes(view) + } else { + value.to_le_bytes(view) + } + + Ok(()) + } + + #[inline(always)] + pub fn write_val_with( + &mut self, + offset: impl Into, + value: &BitVec, + endian: Endian, + ) -> Result<(), FixedStateError> { + debug_assert_eq!(value.bits() % 8, 0); + + let size = value.bits() / 8; + let view = self.view_bytes_mut(offset, size)?; + + if endian.is_big() { + value.to_be_bytes(view) + } else { + value.to_le_bytes(view) + } + + Ok(()) + } + + #[inline(always)] + pub fn read_bytes( + &self, + offset: impl Into, + values: &mut [u8], + ) -> Result<(), FixedStateError> { + let offset = offset.into(); + let size = values.len(); + + let end = offset + .checked_add(size) + .ok_or(FixedStateError::OOBRead { offset, size })?; + + if end > self.backing.len() { + return Err(FixedStateError::OOBRead { offset, size }); + } + + values[..].copy_from_slice(&self.backing[offset..end]); + + Ok(()) + } + + #[inline(always)] + pub fn view_bytes( + &self, + offset: impl Into, + size: usize, + ) -> Result<&[u8], FixedStateError> { + let offset = offset.into(); + + let end = offset + .checked_add(size) + .ok_or(FixedStateError::OOBRead { offset, size })?; + + if end > self.backing.len() { + return Err(FixedStateError::OOBRead { offset, size }); + } + + Ok(&self.backing[offset..end]) + } + + #[inline(always)] + pub fn view_bytes_mut( + &mut self, + offset: impl Into, + size: usize, + ) -> Result<&mut [u8], FixedStateError> { + let offset = offset.into(); + + let end = offset + .checked_add(size) + .ok_or(FixedStateError::OOBWrite { offset, size })?; + + if end > self.backing.len() { + return Err(FixedStateError::OOBWrite { offset, size }); + } + + Ok(&mut self.backing[offset..end]) + } + + #[inline(always)] + pub fn write_bytes( + &mut self, + offset: impl Into, + values: &[u8], + ) -> Result<(), FixedStateError> { + let offset = offset.into(); + let size = values.len(); + + let end = offset + .checked_add(size) + .ok_or(FixedStateError::OOBWrite { offset, size })?; + + if end > self.backing.len() { + return Err(FixedStateError::OOBWrite { offset, size }); + } + + self.backing[offset..end].copy_from_slice(values); + + Ok(()) + } +} diff --git a/fugue-high/src/eval/mod.rs b/fugue-high/src/eval/mod.rs index a770bf3..92ac1f1 100644 --- a/fugue-high/src/eval/mod.rs +++ b/fugue-high/src/eval/mod.rs @@ -1,5 +1,6 @@ use fugue_bv::BitVec; +use fugue_bytes::Endian; use fugue_ir::disassembly::{Opcode, PCodeData}; use fugue_ir::{Address, AddressSpace, Translator, VarnodeData}; @@ -8,6 +9,9 @@ use thiserror::Error; use crate::ir::Location; use crate::lifter::Lifter; +pub mod fixed_state; +use self::fixed_state::FixedState; + #[derive(Debug, Error)] pub enum EvaluatorError { #[error("invalid address: {0:x}")] @@ -16,16 +20,68 @@ pub enum EvaluatorError { DivideByZero, #[error("{0}")] Lift(fugue_ir::error::Error), + #[error("{0}")] + State(anyhow::Error), #[error("unsupported opcode: {0:?}")] Unsupported(Opcode), } +impl EvaluatorError { + pub fn state(e: E) -> Self + where + E: std::error::Error + Send + Sync + 'static, + { + Self::State(anyhow::Error::new(e)) + } + + pub fn state_with(msg: M) -> Self + where + M: std::fmt::Display + std::fmt::Debug + Send + Sync + 'static, + { + Self::State(anyhow::Error::msg(msg)) + } +} + pub trait EvaluatorContext { fn read_vnd(&mut self, var: &VarnodeData) -> Result; fn write_vnd(&mut self, var: &VarnodeData, val: &BitVec) -> Result<(), EvaluatorError>; } -pub struct DummyContext; +pub struct DummyContext { + base: Address, + endian: Endian, + memory: fixed_state::FixedState, + registers: fixed_state::FixedState, + temporaries: fixed_state::FixedState, +} + +impl DummyContext { + pub fn new(lifter: &Lifter, base: impl Into
, size: usize) -> Self { + let t = lifter.translator(); + + Self { + base: base.into(), + endian: if t.is_big_endian() { + Endian::Big + } else { + Endian::Little + }, + memory: FixedState::new(size), + registers: FixedState::new(t.register_space_size()), + temporaries: FixedState::new(t.unique_space_size()), + } + } + + fn translate(&self, addr: u64) -> Result { + let addr = addr + .checked_sub(self.base.into()) + .ok_or(EvaluatorError::state_with( + "address translation out-of-bounds", + ))?; + + Ok(addr as usize) + } +} impl EvaluatorContext for DummyContext { fn read_vnd(&mut self, var: &VarnodeData) -> Result { @@ -33,22 +89,36 @@ impl EvaluatorContext for DummyContext { if spc.is_constant() { Ok(BitVec::from_u64(var.offset(), var.size() * 8)) } else if spc.is_register() { - todo!("read a register") + self.registers + .read_val_with(var.offset() as usize, var.size(), self.endian) + .map_err(EvaluatorError::state) } else if spc.is_unique() { - todo!("read a temporary") + self.temporaries + .read_val_with(var.offset() as usize, var.size(), self.endian) + .map_err(EvaluatorError::state) } else { - todo!("read memory") + let addr = self.translate(var.offset())?; + self.memory + .read_val_with(addr, var.size(), self.endian) + .map_err(EvaluatorError::state) } } fn write_vnd(&mut self, var: &VarnodeData, val: &BitVec) -> Result<(), EvaluatorError> { let spc = var.space(); if spc.is_register() { - todo!("write a register: {val}") + self.registers + .write_val_with(var.offset() as usize, val, self.endian) + .map_err(EvaluatorError::state) } else if spc.is_unique() { - todo!("write a temporary: {val}") + self.temporaries + .write_val_with(var.offset() as usize, val, self.endian) + .map_err(EvaluatorError::state) } else if spc.is_default() { - todo!("write memory: {val}") + let addr = self.translate(var.offset())?; + self.memory + .write_val_with(addr, val, self.endian) + .map_err(EvaluatorError::state) } else { panic!("cannot write to constant Varnode") }