use super::fd::{FdError, FdNumber, FdOperation, KboxlikeFdSystem, ModelThreadId, ProcessId};
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use super::tracee::install_requested_operation_shadow_in_stopped_tracee;
use super::tracee::{
install_requested_operation_shadow_if_needed, TraceeMemoryWriter,
TraceeOperationInstallRequest, TraceeSyscallExecutor,
};
use std::collections::BTreeMap;
const LINUX_X86_64_SYS_READ: i64 = 0;
const LINUX_X86_64_SYS_WRITE: i64 = 1;
const LINUX_X86_64_SYS_PREAD64: i64 = 17;
const LINUX_X86_64_SYS_PWRITE64: i64 = 18;
const LINUX_X86_64_SYS_READV: i64 = 19;
const LINUX_X86_64_SYS_WRITEV: i64 = 20;
const LINUX_X86_64_SYS_MMAP: i64 = 9;
const LINUX_X86_64_SYS_FCNTL: i64 = 72;
const LINUX_X86_64_SYS_PREADV: i64 = 295;
const LINUX_X86_64_SYS_PWRITEV: i64 = 296;
const LINUX_X86_64_SYS_PREADV2: i64 = 327;
const LINUX_X86_64_SYS_PWRITEV2: i64 = 328;
const LINUX_F_SETLK: u64 = 6;
const LINUX_F_SETLKW: u64 = 7;
const LINUX_F_OFD_SETLK: u64 = 37;
const LINUX_F_OFD_SETLKW: u64 = 38;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct InterceptedSyscall {
pub(crate) nr: i64,
pub(crate) args: [u64; 6],
}
impl InterceptedSyscall {
pub(crate) fn new(nr: i64, args: [u64; 6]) -> Self {
Self { nr, args }
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn from_linux_x86_64_regs(regs: &libc::user_regs_struct) -> Self {
Self {
nr: regs.orig_rax as i64,
args: [regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9],
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct ClassifiedFdOperation {
pub(crate) fd: FdNumber,
pub(crate) operation: FdOperation,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum SyscallStopOutcome {
Unclassified,
NoShadowNeeded,
InstalledShadow,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeTraceeIdentity {
pub(crate) host_pid: i32,
pub(crate) model_pid: ProcessId,
pub(crate) model_thread_id: ModelThreadId,
}
#[derive(Debug, Default)]
pub(crate) struct KboxlikeTraceeRegistry {
host_to_identity: BTreeMap<i32, KboxlikeTraceeIdentity>,
next_model_thread_id: ModelThreadId,
}
impl KboxlikeTraceeRegistry {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn register_tracee(
&mut self,
host_pid: i32,
model_pid: ProcessId,
) -> Result<(), FdError> {
if host_pid <= 0 {
return Err(FdError::TraceeInstallFailed("invalid tracee pid"));
}
if self.host_to_identity.contains_key(&host_pid) {
return Err(FdError::TraceeInstallFailed(
"tracee pid already registered",
));
}
self.next_model_thread_id += 1;
self.host_to_identity.insert(
host_pid,
KboxlikeTraceeIdentity {
host_pid,
model_pid,
model_thread_id: self.next_model_thread_id,
},
);
Ok(())
}
pub(crate) fn unregister_tracee(&mut self, host_pid: i32) -> Option<ProcessId> {
self.host_to_identity
.remove(&host_pid)
.map(|identity| identity.model_pid)
}
pub(crate) fn contains_model_pid(&self, model_pid: ProcessId) -> bool {
self.host_to_identity
.values()
.any(|identity| identity.model_pid == model_pid)
}
pub(crate) fn contains_tracee(&self, host_pid: i32) -> bool {
self.host_to_identity.contains_key(&host_pid)
}
pub(crate) fn tracees(&self) -> Vec<(i32, ProcessId)> {
self.host_to_identity
.values()
.map(|identity| (identity.host_pid, identity.model_pid))
.collect()
}
pub(crate) fn tracee_identities(&self) -> Vec<KboxlikeTraceeIdentity> {
self.host_to_identity.values().copied().collect()
}
pub(crate) fn tracee_identity(&self, host_pid: i32) -> Result<KboxlikeTraceeIdentity, FdError> {
if host_pid <= 0 {
return Err(FdError::TraceeInstallFailed("invalid tracee pid"));
}
self.host_to_identity
.get(&host_pid)
.copied()
.ok_or(FdError::TraceeInstallFailed("unknown tracee pid"))
}
pub(crate) fn model_pid(&self, host_pid: i32) -> Result<ProcessId, FdError> {
if host_pid <= 0 {
return Err(FdError::TraceeInstallFailed("invalid tracee pid"));
}
self.host_to_identity
.get(&host_pid)
.map(|identity| identity.model_pid)
.ok_or(FdError::TraceeInstallFailed("unknown tracee pid"))
}
pub(crate) fn model_thread_id(&self, host_pid: i32) -> Result<ModelThreadId, FdError> {
if host_pid <= 0 {
return Err(FdError::TraceeInstallFailed("invalid tracee pid"));
}
self.host_to_identity
.get(&host_pid)
.map(|identity| identity.model_thread_id)
.ok_or(FdError::TraceeInstallFailed("unknown tracee pid"))
}
pub(crate) fn install_request_for_syscall(
&self,
host_pid: i32,
syscall: InterceptedSyscall,
) -> Result<Option<TraceeOperationInstallRequest>, FdError> {
let Some(classified) = classify_fd_operation(syscall)? else {
return Ok(None);
};
Ok(Some(TraceeOperationInstallRequest::new(
self.model_pid(host_pid)?,
host_pid,
classified.fd,
classified.operation,
)?))
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn install_request_for_linux_x86_64_regs(
&self,
host_pid: i32,
regs: &libc::user_regs_struct,
) -> Result<Option<TraceeOperationInstallRequest>, FdError> {
self.install_request_for_syscall(host_pid, InterceptedSyscall::from_linux_x86_64_regs(regs))
}
}
pub(crate) fn classify_fd_operation(
syscall: InterceptedSyscall,
) -> Result<Option<ClassifiedFdOperation>, FdError> {
let classified = match syscall.nr {
LINUX_X86_64_SYS_READ => ClassifiedFdOperation {
fd: syscall_fd_arg(syscall.args[0])?,
operation: FdOperation::Read,
},
LINUX_X86_64_SYS_WRITE | LINUX_X86_64_SYS_PWRITE64 => ClassifiedFdOperation {
fd: syscall_fd_arg(syscall.args[0])?,
operation: FdOperation::Write,
},
LINUX_X86_64_SYS_READV => ClassifiedFdOperation {
fd: syscall_fd_arg(syscall.args[0])?,
operation: FdOperation::Readv,
},
LINUX_X86_64_SYS_WRITEV => ClassifiedFdOperation {
fd: syscall_fd_arg(syscall.args[0])?,
operation: FdOperation::Writev,
},
LINUX_X86_64_SYS_PREAD64 => ClassifiedFdOperation {
fd: syscall_fd_arg(syscall.args[0])?,
operation: FdOperation::Read,
},
LINUX_X86_64_SYS_PREADV | LINUX_X86_64_SYS_PREADV2 => ClassifiedFdOperation {
fd: syscall_fd_arg(syscall.args[0])?,
operation: FdOperation::Preadv,
},
LINUX_X86_64_SYS_PWRITEV | LINUX_X86_64_SYS_PWRITEV2 => ClassifiedFdOperation {
fd: syscall_fd_arg(syscall.args[0])?,
operation: FdOperation::Pwritev,
},
LINUX_X86_64_SYS_MMAP => {
let fd = syscall.args[4] as i32;
if fd < 0 {
return Ok(None);
}
ClassifiedFdOperation {
fd,
operation: FdOperation::Mmap,
}
}
LINUX_X86_64_SYS_FCNTL => {
let command = syscall.args[1];
let operation = if is_fcntl_lock_command(command) {
FdOperation::FcntlLock
} else {
FdOperation::Generic
};
ClassifiedFdOperation {
fd: syscall_fd_arg(syscall.args[0])?,
operation,
}
}
_ => return Ok(None),
};
Ok(Some(classified))
}
fn syscall_fd_arg(raw: u64) -> Result<FdNumber, FdError> {
let fd = raw as i32;
if fd < 0 {
return Err(FdError::InvalidFd(fd));
}
Ok(fd)
}
fn is_fcntl_lock_command(command: u64) -> bool {
matches!(
command,
LINUX_F_SETLK | LINUX_F_SETLKW | LINUX_F_OFD_SETLK | LINUX_F_OFD_SETLKW
)
}
pub(crate) fn handle_intercepted_syscall_stop(
fd_system: &mut KboxlikeFdSystem,
registry: &KboxlikeTraceeRegistry,
host_pid: i32,
syscall: InterceptedSyscall,
supervisor_pid: i32,
writer: &mut impl TraceeMemoryWriter,
executor: &mut impl TraceeSyscallExecutor,
) -> Result<SyscallStopOutcome, FdError> {
let Some(request) = registry.install_request_for_syscall(host_pid, syscall)? else {
return Ok(SyscallStopOutcome::Unclassified);
};
if matches!(
fd_system.descriptor(request.model_pid, request.fd),
Err(FdError::BadFd(_))
) && request.fd <= 2
&& linux_tracee_fd_exists(host_pid, request.fd)
{
if std::env::var_os("SUPERMACHINE_KBOXLIVE_TRACE").is_some() {
eprintln!(
"KBOXLIVE_RESEED_STDIO host_pid={} model_pid={} fd={} op={:?}",
host_pid, request.model_pid, request.fd, request.operation
);
}
fd_system.insert_plain_fd(
request.model_pid,
request.fd,
Some(request.fd as i64),
false,
)?;
}
let installed = match install_requested_operation_shadow_if_needed(
fd_system,
request,
supervisor_pid,
writer,
executor,
) {
Ok(installed) => installed,
Err(FdError::BadFd(_)) if request.operation == FdOperation::Generic => {
return Ok(SyscallStopOutcome::NoShadowNeeded);
}
Err(FdError::BadFd(fd)) if linux_tracee_fd_is_closed(host_pid, fd) => {
if std::env::var_os("SUPERMACHINE_KBOXLIVE_TRACE").is_some() {
eprintln!(
"KBOXLIVE_ALLOW_CLOSED_FD host_pid={} model_pid={} fd={} op={:?}",
host_pid, request.model_pid, fd, request.operation
);
}
return Ok(SyscallStopOutcome::NoShadowNeeded);
}
Err(err) => {
if std::env::var_os("SUPERMACHINE_KBOXLIVE_TRACE").is_some() {
eprintln!(
"KBOXLIVE_FD_INSTALL_ERR host_pid={} model_pid={} fd={} op={:?} err={:?} proc_fd_exists={}",
host_pid,
request.model_pid,
request.fd,
request.operation,
err,
linux_tracee_fd_exists(host_pid, request.fd)
);
}
return Err(err);
}
};
if installed {
Ok(SyscallStopOutcome::InstalledShadow)
} else {
Ok(SyscallStopOutcome::NoShadowNeeded)
}
}
fn linux_tracee_fd_is_closed(host_pid: i32, fd: FdNumber) -> bool {
!linux_tracee_fd_exists(host_pid, fd)
}
fn linux_tracee_fd_exists(host_pid: i32, fd: FdNumber) -> bool {
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
{
if host_pid <= 0 || fd < 0 {
return false;
}
return std::path::Path::new(&format!("/proc/{host_pid}/fd/{fd}")).exists();
}
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
{
let _ = (host_pid, fd);
false
}
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn handle_linux_x86_64_syscall_stop_in_stopped_tracee(
fd_system: &KboxlikeFdSystem,
registry: &KboxlikeTraceeRegistry,
host_pid: i32,
regs: &libc::user_regs_struct,
supervisor_pid: i32,
) -> Result<SyscallStopOutcome, FdError> {
let Some(request) = registry.install_request_for_linux_x86_64_regs(host_pid, regs)? else {
return Ok(SyscallStopOutcome::Unclassified);
};
if install_requested_operation_shadow_in_stopped_tracee(fd_system, request, supervisor_pid)? {
Ok(SyscallStopOutcome::InstalledShadow)
} else {
Ok(SyscallStopOutcome::NoShadowNeeded)
}
}
#[cfg(test)]
mod tests {
use super::super::fd::{ShadowKind, ShadowObject};
use super::super::tracee::TraceeSyscall;
use super::*;
use std::collections::VecDeque;
#[derive(Default)]
struct FakeTracee {
calls: Vec<TraceeSyscall>,
returns: VecDeque<i64>,
}
#[derive(Default)]
struct FakeMemory {
writes: Vec<(u64, Vec<u8>)>,
}
impl FakeTracee {
fn with_returns(returns: impl IntoIterator<Item = i64>) -> Self {
Self {
calls: Vec::new(),
returns: returns.into_iter().collect(),
}
}
}
impl TraceeSyscallExecutor for FakeTracee {
fn syscall(&mut self, syscall: TraceeSyscall) -> Result<i64, FdError> {
self.calls.push(syscall);
Ok(self.returns.pop_front().unwrap_or(0))
}
}
impl TraceeMemoryWriter for FakeMemory {
fn write_bytes(&mut self, addr: u64, bytes: &[u8]) -> Result<(), FdError> {
self.writes.push((addr, bytes.to_vec()));
Ok(())
}
}
fn syscall(nr: i64, args: [u64; 6]) -> InterceptedSyscall {
InterceptedSyscall::new(nr, args)
}
#[test]
fn registry_maps_host_tracee_to_model_process() {
let mut registry = KboxlikeTraceeRegistry::new();
registry.register_tracee(4321, 7).unwrap();
assert_eq!(registry.model_pid(4321).unwrap(), 7);
assert_eq!(registry.model_thread_id(4321).unwrap(), 1);
assert!(registry.contains_tracee(4321));
assert_eq!(registry.tracees(), vec![(4321, 7)]);
assert_eq!(
registry.tracee_identities(),
vec![KboxlikeTraceeIdentity {
host_pid: 4321,
model_pid: 7,
model_thread_id: 1,
}]
);
assert_eq!(registry.unregister_tracee(4321), Some(7));
assert!(!registry.contains_tracee(4321));
assert!(registry.tracees().is_empty());
assert_eq!(
registry.model_pid(4321).unwrap_err(),
FdError::TraceeInstallFailed("unknown tracee pid")
);
}
#[test]
fn registry_lists_tracees_in_host_pid_order() {
let mut registry = KboxlikeTraceeRegistry::new();
registry.register_tracee(5000, 9).unwrap();
registry.register_tracee(4000, 7).unwrap();
registry.register_tracee(4500, 7).unwrap();
assert_eq!(registry.tracees(), vec![(4000, 7), (4500, 7), (5000, 9)]);
assert_eq!(
registry.tracee_identities(),
vec![
KboxlikeTraceeIdentity {
host_pid: 4000,
model_pid: 7,
model_thread_id: 2,
},
KboxlikeTraceeIdentity {
host_pid: 4500,
model_pid: 7,
model_thread_id: 3,
},
KboxlikeTraceeIdentity {
host_pid: 5000,
model_pid: 9,
model_thread_id: 1,
},
]
);
}
#[test]
fn registry_rejects_invalid_host_pid() {
let mut registry = KboxlikeTraceeRegistry::new();
assert_eq!(
registry.register_tracee(0, 7).unwrap_err(),
FdError::TraceeInstallFailed("invalid tracee pid")
);
registry.register_tracee(4321, 7).unwrap();
assert_eq!(
registry.register_tracee(4321, 8).unwrap_err(),
FdError::TraceeInstallFailed("tracee pid already registered")
);
assert_eq!(
registry.model_pid(-1).unwrap_err(),
FdError::TraceeInstallFailed("invalid tracee pid")
);
assert_eq!(
registry.model_thread_id(-1).unwrap_err(),
FdError::TraceeInstallFailed("invalid tracee pid")
);
}
#[test]
fn classifier_maps_common_fd_syscalls() {
assert_eq!(
classify_fd_operation(syscall(LINUX_X86_64_SYS_READ, [4, 0, 0, 0, 0, 0])).unwrap(),
Some(ClassifiedFdOperation {
fd: 4,
operation: FdOperation::Read,
})
);
assert_eq!(
classify_fd_operation(syscall(LINUX_X86_64_SYS_WRITEV, [5, 0, 0, 0, 0, 0])).unwrap(),
Some(ClassifiedFdOperation {
fd: 5,
operation: FdOperation::Writev,
})
);
assert_eq!(
classify_fd_operation(syscall(LINUX_X86_64_SYS_PREADV2, [6, 0, 0, 0, 0, 0])).unwrap(),
Some(ClassifiedFdOperation {
fd: 6,
operation: FdOperation::Preadv,
})
);
assert_eq!(
classify_fd_operation(syscall(LINUX_X86_64_SYS_PWRITEV2, [7, 0, 0, 0, 0, 0])).unwrap(),
Some(ClassifiedFdOperation {
fd: 7,
operation: FdOperation::Pwritev,
})
);
}
#[test]
fn classifier_maps_mmap_fd_from_arg_four_and_skips_anonymous_mmap() {
assert_eq!(
classify_fd_operation(syscall(LINUX_X86_64_SYS_MMAP, [0, 0, 0, 0, 104, 0])).unwrap(),
Some(ClassifiedFdOperation {
fd: 104,
operation: FdOperation::Mmap,
})
);
assert_eq!(
classify_fd_operation(syscall(
LINUX_X86_64_SYS_MMAP,
[0, 0, 0, 0, (-1i64) as u64, 0],
))
.unwrap(),
None
);
assert_eq!(
classify_fd_operation(syscall(
LINUX_X86_64_SYS_MMAP,
[0, 0, 0, 0, u32::MAX as u64, 0]
))
.unwrap(),
None
);
}
#[test]
fn classifier_maps_fcntl_lock_commands_distinct_from_generic_fcntl() {
assert_eq!(
classify_fd_operation(syscall(
LINUX_X86_64_SYS_FCNTL,
[8, LINUX_F_SETLK, 0, 0, 0, 0]
))
.unwrap(),
Some(ClassifiedFdOperation {
fd: 8,
operation: FdOperation::FcntlLock,
})
);
assert_eq!(
classify_fd_operation(syscall(LINUX_X86_64_SYS_FCNTL, [8, 1, 0, 0, 0, 0])).unwrap(),
Some(ClassifiedFdOperation {
fd: 8,
operation: FdOperation::Generic,
})
);
}
#[test]
fn classifier_skips_non_fd_syscalls_and_rejects_bad_fds() {
assert_eq!(classify_fd_operation(syscall(9999, [0; 6])).unwrap(), None);
assert_eq!(
classify_fd_operation(syscall(
LINUX_X86_64_SYS_READ,
[(-1i64) as u64, 0, 0, 0, 0, 0]
))
.unwrap_err(),
FdError::InvalidFd(-1)
);
}
#[test]
fn registry_builds_tracee_install_request_from_classified_syscall() {
let mut registry = KboxlikeTraceeRegistry::new();
registry.register_tracee(4321, 7).unwrap();
assert_eq!(
registry
.install_request_for_syscall(
4321,
syscall(LINUX_X86_64_SYS_MMAP, [0, 0, 0, 0, 104, 0]),
)
.unwrap(),
Some(TraceeOperationInstallRequest {
model_pid: 7,
tracee_pid: 4321,
fd: 104,
operation: FdOperation::Mmap,
})
);
}
#[test]
fn registry_skips_unclassified_syscall_without_requiring_registered_tracee() {
let registry = KboxlikeTraceeRegistry::new();
assert_eq!(
registry
.install_request_for_syscall(4321, syscall(9999, [0; 6]))
.unwrap(),
None
);
}
#[test]
fn syscall_stop_handler_skips_unclassified_syscall() {
let mut fds = KboxlikeFdSystem::new();
let registry = KboxlikeTraceeRegistry::new();
let mut memory = FakeMemory::default();
let mut tracee = FakeTracee::with_returns([0x8000]);
assert_eq!(
handle_intercepted_syscall_stop(
&mut fds,
®istry,
4321,
syscall(9999, [0; 6]),
1234,
&mut memory,
&mut tracee,
)
.unwrap(),
SyscallStopOutcome::Unclassified
);
assert!(memory.writes.is_empty());
assert!(tracee.calls.is_empty());
}
#[test]
fn syscall_stop_handler_reports_no_shadow_for_plain_lkl_fd() {
let mut fds = KboxlikeFdSystem::new();
let pid = fds.spawn_init();
fds.insert_plain_fd(pid, 104, Some(204), false).unwrap();
let mut registry = KboxlikeTraceeRegistry::new();
registry.register_tracee(4321, pid).unwrap();
let mut memory = FakeMemory::default();
let mut tracee = FakeTracee::with_returns([0x8000]);
assert_eq!(
handle_intercepted_syscall_stop(
&mut fds,
®istry,
4321,
syscall(LINUX_X86_64_SYS_MMAP, [0, 0, 0, 0, 104, 0]),
1234,
&mut memory,
&mut tracee,
)
.unwrap(),
SyscallStopOutcome::NoShadowNeeded
);
assert!(memory.writes.is_empty());
assert!(tracee.calls.is_empty());
}
#[test]
fn syscall_stop_handler_allows_generic_fcntl_on_unknown_fd() {
let mut fds = KboxlikeFdSystem::new();
let pid = fds.spawn_init();
let mut registry = KboxlikeTraceeRegistry::new();
registry.register_tracee(4321, pid).unwrap();
let mut memory = FakeMemory::default();
let mut tracee = FakeTracee::with_returns([0x8000]);
assert_eq!(
handle_intercepted_syscall_stop(
&mut fds,
®istry,
4321,
syscall(LINUX_X86_64_SYS_FCNTL, [3, 2, 1, 0, 0, 0]),
1234,
&mut memory,
&mut tracee,
)
.unwrap(),
SyscallStopOutcome::NoShadowNeeded
);
assert!(memory.writes.is_empty());
assert!(tracee.calls.is_empty());
}
#[test]
fn syscall_stop_handler_installs_shadow_for_shadowed_mmap_fd() {
let mut fds = KboxlikeFdSystem::new();
let pid = fds.spawn_init();
fds.insert_shadow_fd(
pid,
104,
Some(204),
ShadowObject {
kind: ShadowKind::LocalOnly,
supervisor_fd: 700,
},
false,
)
.unwrap();
let mut registry = KboxlikeTraceeRegistry::new();
registry.register_tracee(4321, pid).unwrap();
let mut memory = FakeMemory::default();
let mut tracee = FakeTracee::with_returns([0x8000, 901, 104, 0, 0, 0]);
assert_eq!(
handle_intercepted_syscall_stop(
&mut fds,
®istry,
4321,
syscall(LINUX_X86_64_SYS_MMAP, [0, 0, 0, 0, 104, 0]),
1234,
&mut memory,
&mut tracee,
)
.unwrap(),
SyscallStopOutcome::InstalledShadow
);
assert_eq!(
memory.writes,
vec![(0x8000, b"/proc/1234/fd/700\0".to_vec())]
);
assert!(!tracee.calls.is_empty());
}
#[test]
fn syscall_stop_handler_errors_for_unknown_tracee_on_classified_syscall() {
let mut fds = KboxlikeFdSystem::new();
let registry = KboxlikeTraceeRegistry::new();
let mut memory = FakeMemory::default();
let mut tracee = FakeTracee::default();
assert_eq!(
handle_intercepted_syscall_stop(
&mut fds,
®istry,
4321,
syscall(LINUX_X86_64_SYS_MMAP, [0, 0, 0, 0, 104, 0]),
1234,
&mut memory,
&mut tracee,
)
.unwrap_err(),
FdError::TraceeInstallFailed("unknown tracee pid")
);
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[test]
fn intercepted_syscall_decodes_linux_x86_64_registers() {
let mut regs: libc::user_regs_struct = unsafe { std::mem::zeroed() };
regs.orig_rax = LINUX_X86_64_SYS_MMAP as u64;
regs.rdi = 1;
regs.rsi = 2;
regs.rdx = 3;
regs.r10 = 4;
regs.r8 = 104;
regs.r9 = 6;
assert_eq!(
InterceptedSyscall::from_linux_x86_64_regs(®s),
InterceptedSyscall {
nr: LINUX_X86_64_SYS_MMAP,
args: [1, 2, 3, 4, 104, 6],
}
);
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[test]
fn registry_builds_request_from_linux_x86_64_registers() {
let mut registry = KboxlikeTraceeRegistry::new();
registry.register_tracee(4321, 7).unwrap();
let mut regs: libc::user_regs_struct = unsafe { std::mem::zeroed() };
regs.orig_rax = LINUX_X86_64_SYS_MMAP as u64;
regs.r8 = 104;
assert_eq!(
registry
.install_request_for_linux_x86_64_regs(4321, ®s)
.unwrap(),
Some(TraceeOperationInstallRequest {
model_pid: 7,
tracee_pid: 4321,
fd: 104,
operation: FdOperation::Mmap,
})
);
}
}