diff --git a/crates/linux/process-backend/src/lib.rs b/crates/linux/process-backend/src/lib.rs index 29636379..32d9e639 100644 --- a/crates/linux/process-backend/src/lib.rs +++ b/crates/linux/process-backend/src/lib.rs @@ -96,6 +96,8 @@ pub enum ProcessReaderKind { pub enum Error { #[error("there was a failure making a direct syscall")] Local(#[source] local::Error), + #[error("the remote executor returned an error")] + Executor(#[source] remote::executor::Error), #[error("buffer was too small to contain the result")] BufferTooSmall, } diff --git a/crates/linux/process-backend/src/remote/executor/handler.rs b/crates/linux/process-backend/src/remote/executor/handler.rs new file mode 100644 index 00000000..1c1168f7 --- /dev/null +++ b/crates/linux/process-backend/src/remote/executor/handler.rs @@ -0,0 +1,269 @@ +use super as executor; +use super::remote; +use crate::{ProcessReaderKind, regs::*, wrapper::Stat}; +use executor::{Error, Executor, Result}; +use remote::wire; +use wire::{CStrWithNull, HandlesRequests}; + +impl HandlesRequests for &mut Executor<'_> { + fn GetPid<'args, 'output>(self, _args: ()) -> Result + where + Self: 'output, + { + Ok(self.local_backend.pid()) + } + + fn ProcessReaderReadAt<'args, 'output>( + self, + args: wire::ProcessReaderReadAtArgs, + ) -> Result<&'output [u8]> + where + Self: 'output, + { + let scratch_buf = borrow_with_limit(self.scratch_buf, args.requested_len); + + let bytes_read = self + .local_backend + .process_reader() + .read_at(args.address, scratch_buf) + .map_err(Error::Local)?; + + Ok(&scratch_buf[0..bytes_read]) + } + + fn StopProcess<'args, 'output>(self, _args: ()) -> Result<()> + where + Self: 'output, + { + self.local_backend.stop_process().map_err(Error::Local) + } + + fn ContinueProcess<'args, 'output>(self, _args: ()) -> Result<()> + where + Self: 'output, + { + self.local_backend.continue_process().map_err(Error::Local) + } + + fn SuspendThread<'args, 'output>(self, tid: libc::pid_t) -> Result<()> + where + Self: 'output, + { + self.local_backend.suspend_thread(tid).map_err(Error::Local) + } + + fn ResumeThread<'args, 'output>(self, tid: libc::pid_t) -> Result<()> + where + Self: 'output, + { + self.local_backend.resume_thread(tid).map_err(Error::Local) + } + + fn MappedModuleMemoryReaderOpen<'args, 'output>( + self, + args: wire::MappedModuleMemoryReaderOpenArgs, + ) -> Result + where + Self: 'output, + { + let reader = self + .local_backend + .map_module_into_memory(&args.path, args.offset) + .map_err(Error::Local)?; + self.mapped_module_memory_readers + .assign_slot(reader) + .ok_or(Error::MappedModuleMemoryReaderSlotsExhausted) + } + + fn MappedModuleMemoryReaderReadAt<'args, 'output>( + self, + args: wire::MappedModuleMemoryReaderReadAtArgs, + ) -> Result<&'output [u8]> + where + Self: 'output, + { + let reader = self + .mapped_module_memory_readers + .get_slot_mut(args.handle) + .ok_or(Error::EmptyMappedModuleMemoryReaderSlotRequested)?; + reader + .read_at(args.offset, args.requested_len) + .map_err(Error::Local) + } + + fn MappedModuleMemoryReaderLen<'args, 'output>(self, handle: usize) -> Result + where + Self: 'output, + { + let reader = self + .mapped_module_memory_readers + .get_slot_mut(handle) + .ok_or(Error::EmptyMappedModuleMemoryReaderSlotRequested)?; + Ok(reader.len()) + } + + fn MappedModuleMemoryReaderClose<'args, 'output>(self, handle: usize) -> Result<()> + where + Self: 'output, + { + let _reader = self + .mapped_module_memory_readers + .free_slot(handle) + .ok_or(Error::EmptyMappedModuleMemoryReaderSlotRequested)?; + Ok(()) + } + + fn StatFile<'args, 'output>(self, path: CStrWithNull<'_>) -> Result + where + Self: 'output, + { + self.local_backend.stat_file(&path).map_err(Error::Local) + } + + fn FileReaderOpen<'args, 'output>(self, path: CStrWithNull<'_>) -> Result + where + Self: 'output, + { + let reader = self.local_backend.read_file(&path).map_err(Error::Local)?; + self.file_readers + .assign_slot(reader) + .ok_or(Error::FileReaderSlotsExhausted) + } + + fn FileReaderRead<'args, 'output>(self, args: wire::FileReaderArgs) -> Result<&'output [u8]> + where + Self: 'output, + { + let scratch_buf = borrow_with_limit(self.scratch_buf, args.requested_len); + + let reader = self + .file_readers + .get_slot_mut(args.handle) + .ok_or(Error::EmptyFileSlotRequested)?; + + let bytes_read = reader.read(scratch_buf).map_err(Error::Local)?; + + Ok(&scratch_buf[0..bytes_read]) + } + + fn FileReaderClose<'args, 'output>(self, handle: usize) -> Result<()> + where + Self: 'output, + { + let _reader = self + .file_readers + .free_slot(handle) + .ok_or(Error::EmptyFileSlotRequested)?; + Ok(()) + } + + fn DirReaderOpen<'args, 'output>(self, path: CStrWithNull<'_>) -> Result + where + Self: 'output, + { + let reader = self.local_backend.read_dir(&path).map_err(Error::Local)?; + self.dir_readers + .assign_slot(reader) + .ok_or(Error::DirReaderSlotsExhausted) + } + + fn DirReaderRead<'args, 'output>(self, handle: usize) -> Result> + where + Self: 'output, + { + let reader = self + .dir_readers + .get_slot_mut(handle) + .ok_or(Error::EmptyDirSlotRequested)?; + + reader.read_next_name().map_err(Error::Local) + } + + fn DirReaderClose<'args, 'output>(self, handle: usize) -> Result<()> + where + Self: 'output, + { + let _reader = self + .dir_readers + .free_slot(handle) + .ok_or(Error::EmptyDirSlotRequested)?; + Ok(()) + } + + fn ReadLink<'args, 'output>(self, args: wire::ReadLinkArgs<'_>) -> Result<&'output [u8]> + where + Self: 'output, + { + let scratch_buf = borrow_with_limit(self.scratch_buf, args.requested_len); + + let bytes_read = self + .local_backend + .read_link(&args.path, scratch_buf) + .map_err(Error::Local)?; + + Ok(&scratch_buf[0..bytes_read]) + } + + fn GetGenRegs<'args, 'output>(self, tid: libc::pid_t) -> Result + where + Self: 'output, + { + self.local_backend.get_gen_regs(tid).map_err(Error::Local) + } + + fn GetFpRegs<'args, 'output>(self, tid: libc::pid_t) -> Result + where + Self: 'output, + { + self.local_backend.get_fp_regs(tid).map_err(Error::Local) + } + + #[cfg(target_arch = "x86")] + fn GetFpxRegs<'args, 'output>(self, tid: libc::pid_t) -> Result + where + Self: 'output, + { + self.local_backend.get_fpx_regs(tid).map_err(Error::Local) + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn PtracePeekUser<'args, 'output>(self, addr: usize) -> Result<[u8; crate::PTRACE_DATA_LEN]> + where + Self: 'output, + { + self.local_backend + .ptrace_peekuser(addr) + .map_err(Error::Local) + } + + fn ForceProcessReaderKind<'args, 'output>(self, kind: ProcessReaderKind) -> Result<()> + where + Self: 'output, + { + self.local_backend + .force_process_reader_kind(kind) + .map_err(Error::Local) + } + + #[cfg(feature = "testing")] + fn FailOneSyscallWith<'args, 'output>(self, errno: core::ffi::c_int) -> Result<()> + where + Self: 'output, + { + self.local_backend.fail_one_syscall_with(errno); + Ok(()) + } + + fn Quit<'args, 'output>(self, _args: ()) -> Result<()> + where + Self: 'output, + { + self.quit = true; + Ok(()) + } +} + +fn borrow_with_limit(buf: &mut [u8], limit: usize) -> &mut [u8] { + let len = usize::min(buf.len(), limit); + &mut buf[0..len] +} diff --git a/crates/linux/process-backend/src/remote/executor/mod.rs b/crates/linux/process-backend/src/remote/executor/mod.rs new file mode 100644 index 00000000..13ef0678 --- /dev/null +++ b/crates/linux/process-backend/src/remote/executor/mod.rs @@ -0,0 +1,60 @@ +use super as remote; +use crate::local; +use remote::{transport, wire}; +use resources::Pool; + +pub use resources::Resources; + +pub mod resources; + +mod handler; + +pub type Result = core::result::Result; + +pub fn run( + pid: libc::pid_t, + mut transport: T, + resources: Resources<'_>, +) -> core::result::Result<(), transport::ExecutorError> { + let mut executor = Executor { + local_backend: local::Backend::new(pid), + quit: false, + file_readers: Pool::new(resources.file_readers), + dir_readers: Pool::new(resources.dir_readers), + mapped_module_memory_readers: Pool::new(resources.mapped_module_memory_readers), + scratch_buf: resources.scratch_buf, + }; + + while !executor.quit { + wire::serve_request(&mut transport, &mut executor)?; + } + + Ok(()) +} + +struct Executor<'a> { + local_backend: local::Backend, + quit: bool, + file_readers: Pool<'a, resources::FileReader>, + dir_readers: Pool<'a, resources::DirReader>, + mapped_module_memory_readers: Pool<'a, resources::MappedModuleMemoryReader>, + scratch_buf: &'a mut [u8], +} + +#[derive(Debug, thiserror::Error, serde::Deserialize, serde::Serialize)] +pub enum Error { + #[error("an error was returned by a local syscall")] + Local(#[source] local::Error), + #[error("ran out of slots for a new MappedModuleMemoryReader")] + MappedModuleMemoryReaderSlotsExhausted, + #[error("requested an empty MappedModuleMemoryReader slot")] + EmptyMappedModuleMemoryReaderSlotRequested, + #[error("ran out of slots for a new FileReader")] + FileReaderSlotsExhausted, + #[error("requested an empty file slot")] + EmptyFileSlotRequested, + #[error("ran out of slots for a new DirReader")] + DirReaderSlotsExhausted, + #[error("requested an empty directory slot")] + EmptyDirSlotRequested, +} diff --git a/crates/linux/process-backend/src/remote/executor/resources.rs b/crates/linux/process-backend/src/remote/executor/resources.rs new file mode 100644 index 00000000..0fa37ab7 --- /dev/null +++ b/crates/linux/process-backend/src/remote/executor/resources.rs @@ -0,0 +1,93 @@ +use crate::local; + +/// Resources required by the executor. +/// +/// The resources here place hard limits on how many simultaneous file, directory, and module +/// readers can be open, as well as a hard limit on the size of the buffer used by certain +/// operations. +/// +/// See remote/wire.rs `operations!()` macro for notes on buffer sizing +#[derive(Debug)] +pub struct Resources<'a> { + pub file_readers: &'a mut [FileReader], + pub dir_readers: &'a mut [DirReader], + pub mapped_module_memory_readers: &'a mut [MappedModuleMemoryReader], + pub scratch_buf: &'a mut [u8], +} + +#[derive(Debug)] +pub struct FileReader(Option); + +impl FileReader { + pub const fn new_array() -> [Self; N] { + [const { Self(None) }; N] + } +} + +#[derive(Debug)] +pub struct DirReader(Option); + +impl DirReader { + pub const fn new_array() -> [Self; N] { + [const { Self(None) }; N] + } +} + +#[derive(Debug)] +pub struct MappedModuleMemoryReader(Option); + +impl MappedModuleMemoryReader { + pub const fn new_array() -> [Self; N] { + [const { Self(None) }; N] + } +} + +#[derive(Debug)] +pub(super) struct Pool<'a, S>(&'a mut [S]); + +impl<'a, S: Slot> Pool<'a, S> { + pub(super) fn new(s: &'a mut [S]) -> Self { + Self(s) + } + pub(super) fn assign_slot(&mut self, resource: S::Resource) -> Option { + for (idx, slot) in self.0.iter_mut().map(Slot::inner).enumerate() { + if slot.is_none() { + *slot = Some(resource); + return Some(idx); + } + } + None + } + pub(super) fn get_slot_mut(&mut self, idx: usize) -> Option<&mut S::Resource> { + self.0.get_mut(idx)?.inner().as_mut() + } + pub(super) fn free_slot(&mut self, idx: usize) -> Option { + self.0.get_mut(idx)?.inner().take() + } +} + +pub(super) trait Slot { + type Resource; + fn inner(&mut self) -> &mut Option; +} + +impl Slot for FileReader { + type Resource = local::FileReader; + fn inner(&mut self) -> &mut Option { + &mut self.0 + } +} + +impl Slot for DirReader { + type Resource = local::DirReader; + fn inner(&mut self) -> &mut Option { + &mut self.0 + } +} + +impl Slot for MappedModuleMemoryReader { + type Resource = local::MappedModuleMemoryReader; + fn inner(&mut self) -> &mut Option { + &mut self.0 + } +} diff --git a/crates/linux/process-backend/src/remote/mod.rs b/crates/linux/process-backend/src/remote/mod.rs index e3c47574..221b710d 100644 --- a/crates/linux/process-backend/src/remote/mod.rs +++ b/crates/linux/process-backend/src/remote/mod.rs @@ -1,3 +1,4 @@ +pub mod executor; pub mod io; pub mod transport; diff --git a/crates/linux/process-backend/src/remote/wire/mod.rs b/crates/linux/process-backend/src/remote/wire/mod.rs index b039a60f..f9acb1ae 100644 --- a/crates/linux/process-backend/src/remote/wire/mod.rs +++ b/crates/linux/process-backend/src/remote/wire/mod.rs @@ -18,8 +18,7 @@ pub(crate) trait Operation { T: transport::Backend; } -// TEMPORARY - Will be the executor::Error type when the executor is implemented -pub(crate) type RemoteError = (); +pub(crate) type RemoteError = remote::executor::Error; macro_rules! operations({ $(