diff --git a/reverie-ptrace/src/task.rs b/reverie-ptrace/src/task.rs index 8e4f1de..c3b8ad3 100644 --- a/reverie-ptrace/src/task.rs +++ b/reverie-ptrace/src/task.rs @@ -1485,16 +1485,24 @@ impl TracedTask { /// 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 { - 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 TracedTask { loop { match running.next_state().await? { Wait::Stopped(task, Event::Signal(Signal::SIGTRAP)) => { + #[cfg(target_arch = "x86_64")] task.setregs(regs)?; break Ok(task); } diff --git a/safeptrace/src/lib.rs b/safeptrace/src/lib.rs index 67e942b..2cea566 100644 --- a/safeptrace/src/lib.rs +++ b/safeptrace/src/lib.rs @@ -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 { ptrace::getsiginfo(self.0.into()).map_err(|err| self.map_nix_err(err))