Fix skip_seccomp_syscall

Summary: Fixes `skip_seccomp_syscall` on aarch64 to *actually* skip the syscall. I plan on cleaning this up a bit more in a later diff because we are calling `getregs` and `setregs` more than necessary on a per-interception basis.

Reviewed By: VladimirMakaev

Differential Revision: D40867423

fbshipit-source-id: d72b4998b5c1c44f426a9129eccb205ccfc320fa
This commit is contained in:
Jason White 2022-11-01 10:11:35 -07:00 committed by Facebook GitHub Bot
parent 0ae449caba
commit 90e39abc59
2 changed files with 31 additions and 5 deletions

View file

@ -1485,16 +1485,24 @@ impl<L: Tool + 'static> TracedTask<L> {
/// Set tracee state to Stopped/SIGTRP.
/// Restore the registers to the state specified by the regs arg.
async fn skip_seccomp_syscall(&mut self, task: Stopped) -> Result<Stopped, TraceError> {
let regs = task.getregs()?;
// So here we are, at ptrace seccomp stop, if we simply resume, the kernel
// would do the syscall, without our patch. we change to syscall number to
// -1, so that kernel would simply skip the syscall, so that we can jump to
// our patched syscall on the first run. Please note after calling this
// function, the task state will no longer be in ptrace event seccomp.
let mut new_regs = regs;
*new_regs.orig_syscall_mut() = -1i64 as u64;
task.setregs(new_regs)?;
#[cfg(target_arch = "x86_64")]
let regs = task.getregs()?;
#[cfg(target_arch = "x86_64")]
{
let mut new_regs = regs;
*new_regs.orig_syscall_mut() = -1i64 as u64;
task.setregs(new_regs)?;
}
#[cfg(target_arch = "aarch64")]
task.set_syscall(-1)?;
let mut running = task.step(None)?;
// After the step, wait for the next transition. Note that this can return
@ -1503,6 +1511,7 @@ impl<L: Tool + 'static> TracedTask<L> {
loop {
match running.next_state().await? {
Wait::Stopped(task, Event::Signal(Signal::SIGTRAP)) => {
#[cfg(target_arch = "x86_64")]
task.setregs(regs)?;
break Ok(task);
}

View file

@ -600,6 +600,23 @@ impl Stopped {
Ok(Running::new(self.0))
}
/// Sets the syscall to be executed. Only available on `aarch64`.
///
/// Normally, on x86_64, the register `orig_rax` should be set instead to
/// modify the syscall number, which typically involves 3 ptrace calls:
/// 1. getregs to get the current registers.
/// 2. setregs to change `orig_rax` to set the syscall number.
/// 3. setregs again to restore the original registers after the syscall
/// has been executed.
///
/// `set_syscall` on `aarch64` has the advantage of only requiring a single
/// ptrace call.
#[cfg(target_arch = "aarch64")]
pub fn set_syscall(&self, nr: i32) -> Result<(), Error> {
const NT_ARM_SYSTEM_CALL: i32 = 0x404;
self.setregset(NT_ARM_SYSTEM_CALL, nr)
}
/// Gets info about the signal that caused the process to be stopped.
pub fn getsiginfo(&self) -> Result<libc::siginfo_t, Error> {
ptrace::getsiginfo(self.0.into()).map_err(|err| self.map_nix_err(err))