rune: explorer error handling

Replaces explicit panics with error handling in the `explorer` module.
This commit is contained in:
0xllx0 authored and pancake committed 2026-01-21 05:46:00 +01:00
1 parent ce435e3ad4
commit c30eb12a11
9 files changed
+108 -49

No files matched your search

+1 -1
View File
@@ -706,7 +706,7 @@ impl Tokenize for Tokenizer {
}
}
}
} else if let Some(h) = t.strip_prefix("0x") {
} else if let Some(h) = t.strip_prefix("0x") {
match u64::from_str_radix(h, 16) {
Ok(v) => vec![Token::EConstant(v)],
Err(_) => vec![Token::EInvalid],
+16 -19
View File
@@ -4,7 +4,7 @@ use rune::context::rune_ctx::RuneContext;
use rune::context::{Context, Evaluate, MemoryRead, RegisterRead};
use rune::engine::rune::RuneControl;
use rune::explorer::interactive::Command;
use rune::explorer::PathExplorer;
use rune::explorer::{ExplorerError, ExplorerResult, PathExplorer};
use rune::memory::seg_mem::SegMem;
use rune::regstore::regfile::RuneRegFile;
@@ -89,8 +89,7 @@ impl InteractiveExplorer {
let op_2 = {
if let Some(h) = tokens[2].strip_prefix("0x") {
let const_v =
u64::from_str_radix(h, 16).expect("Invalid base16 Integer");
let const_v = u64::from_str_radix(h, 16).expect("Invalid base16 Integer");
ctx.define_const(const_v, 64)
} else {
ctx.reg_read(tokens[2])
@@ -163,29 +162,27 @@ impl PathExplorer for InteractiveExplorer {
&mut self,
ctx: &mut Self::Ctx,
condition: <Self::Ctx as RegisterRead>::VarRef,
) -> RuneControl {
) -> ExplorerResult<RuneControl> {
if self.cmd_q.is_empty() {
self.console
.print_info(&format!("Encountered Branch At {:#x}", ctx.ip()));
self.cmd_q = self.console.read_command();
}
if let Some(cmd) = self.cmd_q.pop() {
match cmd {
Command::FollowTrue => {
let one = ctx.define_const(1, 64);
ctx.eval(core::OpCodes::Cmp, [condition, one]);
RuneControl::ExploreTrue
}
Command::FollowFalse => {
let zero = ctx.define_const(0, 64);
ctx.eval(core::OpCodes::Cmp, [condition, zero]);
RuneControl::ExploreFalse
}
_ => panic!("Incompatible command"),
let cmd = self.cmd_q.pop().ok_or(ExplorerError::EmptyCommandQueue)?;
match cmd {
Command::FollowTrue => {
let one = ctx.define_const(1, 64);
ctx.eval(core::OpCodes::Cmp, [condition, one]);
Ok(RuneControl::ExploreTrue)
}
} else {
unreachable!()
Command::FollowFalse => {
let zero = ctx.define_const(0, 64);
ctx.eval(core::OpCodes::Cmp, [condition, zero]);
Ok(RuneControl::ExploreFalse)
}
_ => Err(ExplorerError::IncompatibleCommand),
}
}
}
+27 -1
View File
@@ -1,5 +1,7 @@
//! Defines traits and structs that perform the actual symbolic emulation.
use crate::explorer::ExplorerError;
pub mod breakpt;
pub mod hook;
pub mod rune;
@@ -7,7 +9,31 @@ pub mod rune;
#[derive(Clone, Copy, Debug)]
pub enum EngineError {
Undefined,
InCorrectOperand,
IncorrectOperand,
Explorer(ExplorerError),
}
impl EngineError {
/// Gets the string representation of the error.
pub const fn as_str(&self) -> &'static str {
match self {
Self::Undefined => "engine: undefined",
Self::IncorrectOperand => "engine: incorrect operand",
Self::Explorer(err) => err.as_str(),
}
}
}
impl From<ExplorerError> for EngineError {
fn from(err: ExplorerError) -> Self {
Self::Explorer(err)
}
}
impl std::fmt::Display for EngineError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
pub type EngineResult<T> = Result<T, EngineError>;
+4 -2
View File
@@ -136,12 +136,14 @@ where
}
Ok(None)
} else {
Err(EngineError::InCorrectOperand)
Err(EngineError::IncorrectOperand)
};
return res;
}
Token::EIf => {
*control = self.explorer.register_branch(&mut self.ctx, l_op.unwrap());
*control = self
.explorer
.register_branch(&mut self.ctx, l_op.unwrap())?;
return Ok(None);
}
Token::EPoke(size) => {
+5 -1
View File
@@ -9,6 +9,10 @@ pub mod dfs;
pub mod directed;
pub mod interactive;
mod error;
pub use error::{ExplorerError, ExplorerResult};
/// Represents a collection of methods for exploring an instruction path.
pub trait PathExplorer {
type C: Clone + Debug;
type Ctx: Context;
@@ -21,5 +25,5 @@ pub trait PathExplorer {
&mut self,
_: &mut Self::Ctx,
_: <Self::Ctx as RegisterRead>::VarRef,
) -> Self::C;
) -> ExplorerResult<Self::C>;
}
+3 -3
View File
@@ -1,7 +1,7 @@
//! `PathExplorer` that works by exploring the CFG in Breadth First Order.
use std::collections::VecDeque;
use super::PathExplorer;
use super::{ExplorerResult, PathExplorer};
use crate::context::{Context, RegisterRead};
use crate::engine::rune::RuneControl;
@@ -74,7 +74,7 @@ where
&mut self,
ctx: &mut Self::Ctx,
_condition: <Self::Ctx as RegisterRead>::VarRef,
) -> RuneControl {
) -> ExplorerResult<RuneControl> {
// When rune encounters a conditional branch instruction, it needs to decide which state to
// explore next. To resolve this, it makes a call to the path explorer which decides on the
// path to be explored next. Path explorer saves the current context information in order
@@ -88,6 +88,6 @@ where
// Switch to a new path in the BFS Queue and pre-empt the current instruction, forcing run
// to load a new job from the queue.
RuneControl::TerminatePath
Ok(RuneControl::TerminatePath)
}
}
+3 -3
View File
@@ -4,7 +4,7 @@ use std::collections::VecDeque;
use libsmt::theories::core;
use super::PathExplorer;
use super::{ExplorerResult, PathExplorer};
use crate::context::rune_ctx::RuneContext;
use crate::context::{Context, Evaluate, RegisterRead};
use crate::engine::rune::RuneControl;
@@ -73,7 +73,7 @@ impl PathExplorer for DFSExplorer<RuneContext<QWordMemory, RuneRegFile>> {
&mut self,
ctx: &mut Self::Ctx,
condition: <Self::Ctx as RegisterRead>::VarRef,
) -> RuneControl {
) -> ExplorerResult<RuneControl> {
// When a new branch is encountered, push the false branch into the queue and
// explore the
// true branch. Note that this choice is arbitrary and we could have as well
@@ -90,6 +90,6 @@ impl PathExplorer for DFSExplorer<RuneContext<QWordMemory, RuneRegFile>> {
let one = ctx.define_const(1, 1);
ctx.eval(core::OpCodes::Cmp, [condition, one]);
}
RuneControl::ExploreTrue
Ok(RuneControl::ExploreTrue)
}
}
+18 -19
View File
@@ -1,7 +1,7 @@
//! `DirectedExplorer`, an implementation of a `PathExplorer` which allows directed symbolic
//! execution
use super::PathExplorer;
use super::{ExplorerError, ExplorerResult, PathExplorer};
use crate::engine::rune::RuneControl;
use crate::context::rune_ctx::RuneContext;
@@ -75,25 +75,24 @@ impl PathExplorer for DirectedExplorer {
&mut self,
ctx: &mut Self::Ctx,
condition: <Self::Ctx as RegisterRead>::VarRef,
) -> RuneControl {
// println!("{:#x}", ctx.ip());
if self.d_map.contains_key(&ctx.ip()) {
let direction = self.d_map.get(&ctx.ip()).unwrap();
match *direction {
BranchType::True => {
let one = ctx.define_const(1, 64);
ctx.eval(core::OpCodes::Cmp, [condition, one]);
RuneControl::ExploreTrue
}
BranchType::False => {
let zero = ctx.define_const(0, 64);
ctx.eval(core::OpCodes::Cmp, [condition, zero]);
RuneControl::ExploreFalse
}
_ => panic!("Invalid branch type found!"),
) -> ExplorerResult<RuneControl> {
let direction = self
.d_map
.get(&ctx.ip())
.ok_or(ExplorerError::UnknownBranch)?;
match *direction {
BranchType::True => {
let one = ctx.define_const(1, 64);
ctx.eval(core::OpCodes::Cmp, [condition, one]);
Ok(RuneControl::ExploreTrue)
}
} else {
panic!("Do not know which branch to take. Set a default!");
BranchType::False => {
let zero = ctx.define_const(0, 64);
ctx.eval(core::OpCodes::Cmp, [condition, zero]);
Ok(RuneControl::ExploreFalse)
}
_ => Err(ExplorerError::InvalidBranch),
}
}
}
+31
View File
@@ -0,0 +1,31 @@
/// Represents a convenience alias for the Rune Explorer [Result](std::result::Result) type.
pub type ExplorerResult<T> = std::result::Result<T, ExplorerError>;
/// Represents error variants for the Rune Explorer.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ExplorerError {
EmptyCommandQueue,
IncompatibleCommand,
InvalidBranch,
UnknownBranch,
}
impl ExplorerError {
/// Gets the string representation of the error.
pub const fn as_str(&self) -> &'static str {
match self {
Self::EmptyCommandQueue => "explorer: Command queue is empty",
Self::IncompatibleCommand => "explorer: Incompatible command",
Self::InvalidBranch => "explorer: Invalid branch type found",
Self::UnknownBranch => "explorer: Do not know which branch to take. Set a default",
}
}
}
impl std::fmt::Display for ExplorerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl std::error::Error for ExplorerError {}