use std::ffi::NulError;
use std::io::Error as IOError;
use std::path::PathBuf;
use thiserror::Error;
use crate::breakpoint::BreakpointId;
use crate::mapping::{PermissionMatcher, Permissions};
use crate::process::Pid;
use crate::run::{PtraceOption, Reason};
#[derive(Debug, Error)]
pub enum CouldNotAllocate {
#[error(transparent)]
CouldNotPerformSyscall(#[from] CouldNotResume),
#[error("remote mmap in process (PID: {pid}) failed with error: {source}")]
MmapFailed {
pid: Pid,
#[source]
source: IOError,
},
}
#[derive(Debug, Error)]
pub enum CouldNotCreateAsyncReaderWriter {
#[error("the address range {start:#x}-{end:#x} is not mapped in process (PID: {pid}) memory")]
InvalidAddressRange { pid: Pid, start: usize, end: usize },
#[error("expected a {expected} memory mapping, but it was {got}")]
PermissionsMismatch {
expected: PermissionMatcher,
got: Permissions,
},
#[error(transparent)]
CouldNotReadMemoryMapping(#[from] MappingError),
}
#[derive(Error, Debug)]
pub enum CouldNotAttachToPid {
#[error("executable file `{0}` not found")]
ExecutableNotFound(PathBuf),
#[error("command-line file `{0}` not found")]
CmdlineFileNotFound(PathBuf),
#[error("could not attach process (PID: {pid}), error: {source}")]
CouldNotAttach {
pid: Pid,
#[source]
source: IOError,
},
#[error("could not stop process (PID: {pid}), error: {source}")]
CouldNotStop {
pid: Pid,
#[source]
source: IOError,
},
#[error("could not restart process (PID: {pid}), error: {source}")]
CouldNotRestart {
pid: Pid,
#[source]
source: IOError,
},
#[error("could not read `/proc/{pid}/cmdline`, error: {source}")]
CouldNotReadCmdLineFile {
pid: Pid,
#[source]
source: IOError,
},
#[error("process (PID: {pid}) not found")]
ProcessNotFound { pid: Pid },
}
#[derive(Debug, Error)]
pub enum CouldNotCreateBreakpoint {
#[error(transparent)]
CouldNotReadMemory(#[from] CouldNotRead),
#[error(transparent)]
CouldNotWriteTrap(#[from] CouldNotWrite),
#[error("a breakpoint already exist in process (PID: {pid} at address {address:#x}")]
BreakpointAlreadyExists { pid: Pid, address: usize },
#[error("process (PID: {pid}) has already been killed: {source}")]
ProcessKilled {
pid: Pid,
#[source]
source: IOError,
},
}
impl From<ReadWriteError> for CouldNotCreateBreakpoint {
fn from(value: ReadWriteError) -> Self {
match value {
ReadWriteError::CouldNotRead(e) => Self::from(e),
ReadWriteError::CouldNotWrite(e) => Self::from(e),
ReadWriteError::ProcessKilled { pid, source } => Self::ProcessKilled { pid, source },
}
}
}
#[derive(Debug, Error)]
#[error("could not get the signal info about process (PID: {pid}), errno: {source}")]
pub struct CouldNotGetSignalInfo {
pub pid: Pid,
#[source]
pub source: IOError,
}
#[derive(Debug, Error)]
pub enum CouldNotDetermineState {
#[error(transparent)]
CouldNotReadStatFile(#[from] IOError),
#[error(transparent)]
CouldNotReadRegisters(#[from] CouldNotReadRegisters),
#[error(transparent)]
CouldNotGetSignalInfo(#[from] CouldNotGetSignalInfo),
}
#[derive(Debug, Error)]
#[error("could not read memory at address {address} in process (PID: {pid}), errno: {source}")]
pub struct CouldNotRead {
pub address: usize,
pub pid: Pid,
#[source]
pub source: IOError,
}
#[derive(Debug, Error)]
#[error("could not write memory at address {address} in process (PID: {pid}), errno: {source}")]
pub struct CouldNotWrite {
pub address: usize,
pub pid: Pid,
#[source]
pub source: IOError,
}
#[derive(Debug, Error)]
#[error("could not read registers from process (PID: {pid}), errno: {source}")]
pub struct CouldNotReadRegisters {
pub pid: Pid,
#[source]
pub source: IOError,
}
#[derive(Debug, Error)]
#[error("could not write registers into process (PID: {pid}), errno: {source}")]
pub struct CouldNotWriteRegisters {
pub pid: Pid,
#[source]
pub source: IOError,
}
#[derive(Debug, Error)]
pub enum CouldNotDeallocate {
#[error(transparent)]
CouldNotPerformSyscall(#[from] CouldNotResume),
#[error("remote munmap in process (PID: {pid}) failed with error: {source}")]
MunmapFailed {
pid: Pid,
#[source]
source: IOError,
},
}
#[derive(Debug, Error)]
pub enum CouldNotExecute {
#[error("could not fork process: {source}")]
CouldNotFork {
#[source]
source: IOError,
},
#[error("argument `{string:?}` contained a null byte at `{}`", source.nul_position())]
NullInString {
string: String,
#[source]
source: NulError,
},
#[error("executable file `{0}` not found")]
ExecutableNotFound(PathBuf),
#[error("could not trace process (PID: {pid}), errno: {source}")]
CouldNotTrace {
pid: Pid,
#[source]
source: IOError,
},
#[error("invalid UTF-8 path `{0}`")]
InvalidUTF8Path(PathBuf),
}
#[derive(Debug, Error, PartialEq, Eq)]
#[error("the process has exited unexpectedly: {reason}")]
pub struct UnexpectedExit {
pub reason: Reason,
}
#[derive(Debug, Error)]
#[allow(clippy::module_name_repetitions)]
pub enum MappingError {
#[error(transparent)]
IO(#[from] IOError),
#[error(transparent)]
Parse(#[from] CouldNotParseMappingFile),
}
#[derive(Clone, Copy, Debug, Error, PartialEq, Eq)]
pub enum CouldNotParseMappingFile {
#[error("could not parse mapping' address range")]
AddressRange,
#[error("could not parse mapping' device")]
Device,
#[error("could not parse mapping' end address")]
EndAddress,
#[error("could not parse mapping' inode")]
Inode,
#[error("could not parse mapping' offset")]
Offset,
#[error("could not parse mapping' permissions")]
Permissions,
#[error("could not parse mapping' stack thread ID")]
StackTID,
#[error("could not parse mapping' start address")]
StartAddress,
#[error("unexpected end of line")]
UnexpectedEndOfLine,
}
impl CouldNotParseMappingFile {
#[allow(clippy::missing_errors_doc)]
pub fn from_option<T>(value: Option<T>) -> Result<T, Self> {
value.map_or_else(|| Err(Self::UnexpectedEndOfLine), |v| Ok(v))
}
}
#[derive(Debug, Error)]
#[allow(clippy::module_name_repetitions)]
pub enum MprotectError {
#[error("{address:#x} is not a valid pointer, or not a multiple of the system page size")]
InvalidPointer { pid: Pid, address: usize },
#[error("protections specified as both grows-up and grows-down")]
GrowsUpAndDown { perm: Permissions },
#[error("maximum amount allowed of mappings reached")]
TooManyMappings,
}
#[derive(Debug, Error)]
pub enum CouldNotRestorePermissions {
#[error("the address range {start:#x}-{end:#x} is not mapped in process (PID: {pid}) memory")]
InvalidAddressRange { pid: Pid, start: usize, end: usize },
#[error("expected a {expected} memory mapping, but it was {got}")]
PermissionsMismatch {
expected: PermissionMatcher,
got: Permissions,
},
#[error(transparent)]
CouldNotReadMemoryMapping(#[from] MappingError),
#[error("mprotect in process (PID: {pid}) failed with error: {source}")]
MprotectFailed {
pid: Pid,
#[source]
source: MprotectError,
},
#[error(transparent)]
CouldNotRestart(#[from] CouldNotResume),
}
#[derive(Debug, Error)]
#[error("could not set ptrace options to process (PID: {pid}), error: {source}")]
pub struct CouldNotSetOptions {
pub pid: Pid,
pub options: Vec<PtraceOption>,
#[source]
pub source: IOError,
}
#[derive(Debug, Error)]
#[allow(clippy::module_name_repetitions)]
pub enum ReadWriteError {
#[error(transparent)]
CouldNotRead(#[from] CouldNotRead),
#[error(transparent)]
CouldNotWrite(#[from] CouldNotWrite),
#[error("process (PID: {pid}) has been killed before the operation finished")]
ProcessKilled {
pid: Pid,
#[source]
source: IOError,
},
}
#[derive(Debug, Error)]
pub enum CouldNotReadWriteRegister {
#[error(transparent)]
CouldNotRead(#[from] CouldNotReadRegisters),
#[error(transparent)]
CouldNotWrite(#[from] CouldNotWriteRegisters),
}
#[derive(Debug, Error)]
pub enum CouldNotCreateRemoteView {
#[error("the address range {start:#x}-{end:#x} is not mapped in process (PID: {pid}) memory")]
InvalidAddressRange { pid: Pid, start: usize, end: usize },
#[error(transparent)]
CouldNotReadMemoryMapping(#[from] MappingError),
}
#[derive(Debug, Error)]
pub enum CouldNotRemoveBreakpoint {
#[error("breakpoint {id:?} does not exist in process (PID: {pid})")]
BreakpointNotFound { id: BreakpointId, pid: Pid },
#[error(transparent)]
WriteError {
#[from]
source: ReadWriteError,
},
}
#[derive(Debug, Error)]
pub enum CouldNotResume {
#[error(transparent)]
CouldNotRead(#[from] CouldNotRead),
#[error(transparent)]
CouldNotWrite(#[from] CouldNotWrite),
#[error("could not restart process (PID: {pid}), errno: {source}")]
CouldNotRestart {
pid: Pid,
#[source]
source: IOError,
},
#[error(transparent)]
CouldNotRemoveBreakpoint(#[from] CouldNotRemoveBreakpoint),
#[error(transparent)]
CouldNotReadRegisters(#[from] CouldNotReadRegisters),
#[error(transparent)]
CouldNotWriteRegisters(#[from] CouldNotWriteRegisters),
#[error(transparent)]
CouldNotStop(#[from] CouldNotWait),
}
impl From<CouldNotReadWriteRegister> for CouldNotResume {
fn from(value: CouldNotReadWriteRegister) -> Self {
match value {
CouldNotReadWriteRegister::CouldNotRead(e) => Self::CouldNotReadRegisters(e),
CouldNotReadWriteRegister::CouldNotWrite(e) => Self::CouldNotWriteRegisters(e),
}
}
}
impl From<ReadWriteError> for CouldNotResume {
fn from(value: ReadWriteError) -> Self {
match value {
ReadWriteError::CouldNotRead(e) => Self::CouldNotRead(e),
ReadWriteError::CouldNotWrite(e) => Self::CouldNotWrite(e),
ReadWriteError::ProcessKilled { pid, source } => Self::CouldNotRestart { pid, source },
}
}
}
#[derive(Debug, Error)]
pub enum CouldNotCreateSyncReaderWriter {
#[error("the address range {start:#x}-{end:#x} is not mapped in process (PID: {pid}) memory")]
InvalidAddressRange { pid: Pid, start: usize, end: usize },
#[error(transparent)]
CouldNotReadMemoryMapping(#[from] MappingError),
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum CouldNotWait {
#[error("thread (TID: {pid}) does not exists, errno: {source}")]
ProcessNotFound {
pid: Pid,
#[source]
source: IOError,
},
}
#[derive(Debug, Error)]
pub enum CouldNotWaitForProcess {
#[error(transparent)]
CouldNotWaitForThread(#[from] CouldNotWait),
#[error(transparent)]
CouldNotDetermineState(#[from] CouldNotDetermineState),
}
#[derive(Debug, Error)]
#[error("could not find thread (TID: {tid}) in process (PID: {pid})")]
pub struct ThreadNotFound {
pub pid: Pid,
pub tid: Pid,
}