8000 Isolated Processes 18: Remote backend to forward syscalls to another process by marti4d · Pull Request #218 · rust-minidump/minidump-writer · GitHub
[go: up one dir, main page]

Skip to content
8000
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/linux/process-backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ postcard = { version = "1.1.3", default-features = false, optional = true }
process-reader = { version = "0.1.0", path = "../../process-reader", features = ["serde"] }
serde.workspace = true
thiserror.workspace = true

[dev-dependencies]
process-backend = { path = ".", features = ["postcard", "testing", "unix_stream"] }
3 changes: 2 additions & 1 deletion crates/linux/process-backend/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![no_std]
#![allow(dead_code)]
#![cfg(any(target_os = "linux", target_os = "android"))]

use core::ffi::CStr;
Expand Down Expand Up @@ -98,6 +97,8 @@ pub enum Error {
Local(#[source] local::Error),
#[error("the remote executor returned an error")]
Executor(#[source] remote::executor::Error),
#[error("there was an error transporting the request to/from the remote executor")]
Transport(#[source] remote::transport::BackendError),
#[error("buffer was too small to contain the result")]
BufferTooSmall,
}
347 changes: 347 additions & 0 deletions crates/linux/process-backend/src/remote/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
use super as remote;
use crate::wrapper::Stat;
use crate::{Error, ProcessReaderKind, Result};
use core::cell::{Cell, RefCell};
use remote::{transport, wire};
use wire::{Operation, ops};

#[derive(Debug)]
pub struct Backend<T: transport::Backend> {
transport_state: RefCell<TransportState<T>>,
pid: Cell<Option<libc::pid_t>>,
max_response_output_len: usize,
}

#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
enum TransportState<T> {
Ready(T),
Poisoned,
}

impl<T> TransportState<T> {
fn expect_ready(&mut self) -> &mut T {
match self {
TransportState::Ready(transport) => transport,
TransportState::Poisoned => panic!("attempted to use poisoned transport"),
}
}
}

impl<T: transport::Backend> Backend<T> {
pub fn new(transport: T) -> Self {
let max_response_output_len = transport.max_response_output_len();
Self {
transport_state: RefCell::new(TransportState::Ready(transport)),
pid: Cell::new(None),
max_response_output_len,
}
}

fn empty_request<Op, Out>(&self) -> Result<Out>
where
Op: for<'args, 'output> Operation<Args<'args> = (), Output<'output> = Out>,
Ou 10BC8 t: 'static,
{
self.simple_request::<Op, Out>(())
}

fn simple_request<Op, Out>(&self, args: Op::Args<'_>) -> Result<Out>
where
Op: for<'output> Operation<Output<'output> = Out>,
Out: 'static,
{
self.mapped_request::<Op, _, _>(args, Ok)
}

fn mapped_request<Op, F, U>(&self, args: Op::Args<'_>, map_fn: F) -> Result<U>
where
Op: Operation,
F: for<'output> FnOnce(Op::Output<'output>) -> Result<U>,
{
let mut transport_state = self.transport_state.borrow_mut();
let transport = transport_state.expect_ready();

let e = match Op::request(transport, args) {
Ok(response) => {
let output = response.map_err(Error::Executor)?;
return map_fn(output);
}
Err(e) => e,
};

*transport_state = TransportState::Poisoned;
Err(Error::Transport(e))
}
Comment thread
marti4d marked this conversation as resolved.
Resolved
}

impl<T: transport::Backend> crate::Backend for Backend<T> {
type ProcessReader<'a>
= ProcessReader<'a, T>
where
Self: 'a;

type FileReader<'a>
= FileReader<'a, T>
where
Self: 'a;

type DirReader<'a>
= DirReader<'a, T>
where
Self: 'a;

type MappedModuleMemoryReader<'a>
= MappedModuleMemoryReader<'a, T>
where
Self: 'a;

fn pid(&self) -> Result<libc::pid_t> {
match self.pid.get() {
Some(pid) => Ok(pid),
None => {
let pid = self.empty_request::<ops::GetPid, _>()?;
self.pid.set(Some(pid));
Ok(pid)
}
}
}

fn process_reader<'a>(&'a self) -> Self::ProcessReader<'a> {
ProcessReader { backend: self }
}

fn stop_process(&self) -> Result<()> {
self.empty_request::<ops::StopProcess, _>()
}

fn continue_process(&self) -> Result<()> {
self.empty_request::<ops::ContinueProcess, _>()
}

fn suspend_thread(&self, tid: libc::pid_t) -> Result<()> {
self.simple_request::<ops::SuspendThread, _>(tid)
}

fn resume_thread(&self, tid: libc::pid_t) -> Result<()> {
self.simple_request::<ops::ResumeThread, _>(tid)
}

fn map_module_into_memory<'a>(
&'a self,
path: &core::ffi::CStr,
offset: u64,
) -> Result<Self::MappedModuleMemoryReader<'a>> {
self.mapped_request::<ops::MappedModuleMemoryReaderOpen, _, _>(
wire::MappedModuleMemoryReaderOpenArgs {
path: path.into(),
offset,
},
|handle| {
Ok(MappedModuleMemoryReader {
backend: self,
handle,
})
},
)
}

fn stat_file(&self, path: &core::ffi::CStr) -> Result<Stat> {
self.simple_request::<ops::StatFile, _>(path.into())
}

fn read_file<'a>(&'a self, path: &core::ffi::CStr) -> Result<Self::FileReader<'a>> {
self.mapped_request::<ops::FileReaderOpen, _, _>(path.into(), |handle| {
Ok(FileReader {
backend: self,
handle,
})
})
}

fn read_dir<'a>(&'a self, path: &core::ffi::CStr) -> Result<Self::DirReader<'a>> {
self.mapped_request::<ops::DirReaderOpen, _, _>(path.into(), |handle| {
Ok(DirReader {
backend: self,
handle,
})
})
}

fn read_link(&self, path: &core::ffi::CStr, buf: &mut [u8]) -> Result<usize> {
self.mapped_request::<ops::ReadLink, _, _>(
wire::ReadLinkArgs {
path: path.into(),
requested_len: usize::min(buf.len(), self.max_response_output_len),
},
|bytes| {
buf[0..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
},
)
}

fn get_gen_regs(&self, tid: libc::pid_t) -> Result<crate::regs::GenRegs> {
self.simple_request::<ops::GetGenRegs, _>(tid)
}

fn get_fp_regs(&self, tid: libc::pid_t) -> Result<crate::regs::FpRegs> {
self.simple_request::<ops::GetFpRegs, _>(tid)
}

#[cfg(target_arch = "x86")]
fn get_fpx_regs(&self, tid: libc::pid_t) -> Result<crate::regs::FpxRegs> {
self.simple_request::<ops::GetFpxRegs, _>(tid)
}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn ptrace_peekuser(&self, addr: usize) -> Result<[u8; crate::PTRACE_DATA_LEN]> {
self.simple_request::<ops::PtracePeekUser, _>(addr)
}

fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<()> {
self.simple_request::<ops::ForceProcessReaderKind, _>(kind)
}

#[cfg(feature = "testing")]
fn fail_one_syscall_with(&self, errno: core::ffi::c_int) {
let _result = self.simple_request::<ops::FailOneSyscallWith, _>(errno);
}
}

impl<T: transport::Backend> Drop for Backend<T> {
fn drop(&mut self) {
if let Err(e) = self.empty_request::<ops::Quit, _>() {
report_drop_failed!("failed to drop backend: {e:#?}");
}
}
}

#[derive(Debug)]
pub struct ProcessReader<'a, T: transport::Backend> {
backend: &'a Backend<T>,
}

impl<'a, T: transport::Backend> crate::ProcessReader for ProcessReader<'a, T> {
fn read_at(&self, address: usize, buf: &mut [u8]) -> Result<usize> {
self.backend
.mapped_request::<ops::ProcessReaderReadAt, _, _>(
wire::ProcessReaderReadAtArgs {
address,
requested_len: usize::min(buf.len(), self.backend.max_response_output_len),
},
|bytes| {
buf[0..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
},
)
}
}

#[derive(Debug)]
pub struct FileReader<'a, T: transport::Backend> {
backend: &'a Backend<T>,
handle: usize,
}

impl<'a, T: transport::Backend> crate::FileReader for FileReader<'a, T> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.backend.mapped_request::<ops::FileReaderRead, _, _>(
wire::FileReaderArgs {
handle: self.handle,
requested_len: usize::min(buf.len(), self.backend.max_response_output_len),
},
|bytes| {
buf[0..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
},
)
}
}

impl<'a, T: transport::Backend> Drop for FileReader<'a, T> {
fn drop(&mut self) {
if let Err(e) = self
.backend
.simple_request::<ops::FileReaderClose, _>(self.handle)
{
report_drop_failed!("failed to close FileReader: {e:#?}");
}
}
}

#[derive(Debug)]
pub struct DirReader<'a, T: transport::Backend> {
backend: &'a Backend<T>,
handle: usize,
}

impl<'a, T: transport::Backend> crate::DirReader for DirReader<'a, T> {
fn read_next_name(&mut self, buf: &mut [u8]) -> Result<usize> {
self.backend
.mapped_request::<ops::DirReaderRead, _, _>(self.handle, |maybe_bytes| {
if let Some(bytes) = maybe_bytes {
8 buf[0 10BC8 ..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
} else {
Ok(0)
}
})
}
}

impl<'a, T: transport::Backend> Drop for DirReader<'a, T> {
fn drop(&mut self) {
if let Err(e) = self
.backend
.simple_request::<ops::DirReaderClose, _>(self.handle)
{
report_drop_failed!("failed to close DirReader: {e:#?}");
}
}
}

#[derive(Debug)]
pub struct MappedModuleMemoryReader<'a, T: transport::Backend> {
backend: &'a Backend<T>,
handle: usize,
}

impl<'a, T: transport::Backend> crate::MappedModuleMemoryReader
for MappedModuleMemoryReader<'a, T>
{
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
self.backend
.mapped_request::<ops::MappedModuleMemoryReaderReadAt, _, _>(
wire::MappedModuleMemoryReaderReadAtArgs {
handle: self.handle,
offset,
requested_len: usize::min(buf.len(), self.backend.max_response_output_len),
},
|bytes| {
buf[0..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
},
)
}

fn len(&self) -> Result<usize> {
self.backend
.simple_request::<ops::MappedModuleMemoryReaderLen, _>(self.handle)
}

fn is_empty(&self) -> Result<bool> {
self.len().map(|len| len == 0)
}
}

impl<'a, T: transport::Backend> Drop for MappedModuleMemoryReader<'a, T> {
fn drop(&mut self) {
if let Err(e) = self
.backend
.simple_request::<ops::MappedModuleMemoryReaderClose, _>(self.handle)
{
report_drop_failed!("failed to close MappedModuleMemoryReader: {e:#?}");
}
}
}
1 change: 1 addition & 0 deletions crates/linux/process-backend/src/remote/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod backend;
pub mod executor;
pub mod io;
pub mod transport;
Expand Down
Loading
Loading
0