use std::error::Error;
use std::fmt::Debug;
use rand::Rng;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::arch::Arch;
use crate::state::{Addr, AsSystemState, Page, SystemState};
use crate::utils::bitmask_u64;
mod careful;
mod counter;
mod iter;
mod verifier;
pub use careful::CarefulOracle;
pub use counter::InvocationCountingOracle;
pub(crate) use iter::FallbackBatchObserveIter;
pub use verifier::VerifyOracle;
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
pub enum OracleError {
#[error("Encountered a memory access at {:X}", .0)]
MemoryAccess(Addr),
#[error("Encountered an instruction fetch at {:X}", .0)]
InstructionFetchMemoryAccess(Addr),
#[error("Invalid instruction")]
InvalidInstruction,
#[error("General fault")]
GeneralFault,
#[error("Instruction execution is unreliable: executing multiple times with the same state gives different results")]
Unreliable,
#[error("A computation error occurred")]
ComputationError,
#[error("The observation timed out")]
Timeout,
#[error("Multiple instructions were executed")]
MultipleInstructionsExecuted,
#[error("Oracle failed with {}", .0)]
ApiError(String),
}
impl OracleError {
pub fn api<E: Error + Send + Sync + 'static>(e: E) -> Self {
OracleError::ApiError(e.to_string())
}
}
pub type ObservationResult<A> = Result<SystemState<A>, OracleError>;
pub type Observation<S, A> = (S, ObservationResult<A>);
pub trait MappableArea: Clone + Debug {
fn can_map(&self, addr: Addr) -> bool;
}
pub trait OracleSource {
type Oracle;
fn start(&self) -> Vec<Self::Oracle>;
}
pub trait Oracle<A: Arch> {
type MappableArea: MappableArea;
const UNRELIABLE_INSTRUCTION_FETCH_ERRORS: bool;
fn mappable_area(&self) -> Self::MappableArea;
fn random_mappable_page(&self, rng: &mut impl Rng) -> Page<A> {
let mappable = self.mappable_area();
loop {
let addr = Addr::new(rng.gen::<u64>() & !bitmask_u64(A::PAGE_BITS as u32));
if mappable.can_map(addr) {
return addr.page::<A>()
}
}
}
fn page_size(&mut self) -> u64;
fn observe(&mut self, before: &SystemState<A>) -> Result<SystemState<A>, OracleError>;
fn batch_observe_iter<'a, S: AsSystemState<A> + 'a, I: IntoIterator<Item = S> + 'a>(
&'a mut self, states: I,
) -> impl Iterator<Item = Observation<S, A>>;
fn batch_observe_gpreg_only_iter<'a, S: AsSystemState<A> + 'a, I: IntoIterator<Item = S> + 'a>(
&'a mut self, states: I,
) -> impl Iterator<Item = Observation<S, A>>;
fn observe_carefully(&mut self, before: &SystemState<A>) -> Result<SystemState<A>, OracleError> {
let result = self.observe(before)?;
let mut accesses = self.scan_memory_accesses(before)?;
accesses.retain(|&found_addr| {
before
.memory()
.iter()
.all(|(mapped_addr, _, data)| !mapped_addr.into_area(data.len() as u64).contains(found_addr))
});
if !accesses.is_empty() {
Err(OracleError::MemoryAccess(accesses[0]))
} else {
Ok(result)
}
}
fn batch_observe<'a, const N: usize, S: AsSystemState<A> + 'a>(&mut self, states: [S; N]) -> [Observation<S, A>; N] {
let mut iter = self.batch_observe_iter(<[S; N] as IntoIterator>::into_iter(states));
[(); N].map(|_| iter.next().unwrap())
}
fn scan_memory_accesses(&mut self, before: &SystemState<A>) -> Result<Vec<Addr>, OracleError>;
fn debug_dump(&mut self);
fn restart(&mut self);
fn kill(self);
}