panda/syscall_injection/
syscall_future.rs

1use std::{
2    future::Future,
3    pin::Pin,
4    sync::{
5        atomic::{AtomicBool, AtomicU64, Ordering},
6        Arc,
7    },
8    task::{Context, Poll},
9};
10
11use super::arch::{SYSCALL_ARGS, SYSCALL_NUM_REG, SYSCALL_RET};
12use super::{IntoSyscallArgs, SyscallArgs, ThreadId};
13use crate::regs;
14
15use dashmap::DashMap;
16use lazy_static::lazy_static;
17use once_cell::sync::OnceCell;
18use panda_sys::{get_cpu, target_ulong, CPUState};
19
20pub(crate) struct SyscallFuture {
21    ret_val: Arc<OnceCell<target_ulong>>,
22}
23
24// write all the syscall arguments to their corresponding registers
25fn set_syscall_args(cpu: &mut CPUState, args: SyscallArgs) {
26    for (storage_location, arg) in SYSCALL_ARGS.iter().copied().zip(args.iter_args()) {
27        storage_location.write(cpu, arg);
28    }
29}
30
31lazy_static! {
32    static ref LAST_INJECTED_SYSCALL: DashMap<ThreadId, AtomicU64> = DashMap::new();
33}
34
35pub(crate) fn last_injected_syscall() -> target_ulong {
36    LAST_INJECTED_SYSCALL
37        .get(&ThreadId::current())
38        .map(|num| num.load(Ordering::SeqCst) as target_ulong)
39        .unwrap_or_else(|| {
40            log::warn!("No syscall num found for thread {:?}", ThreadId::current());
41            0xBADCA11
42        })
43}
44
45fn set_syscall_num(cpu: &mut CPUState, num: target_ulong) {
46    LAST_INJECTED_SYSCALL
47        .entry(ThreadId::current())
48        .or_default()
49        .store(num as u64, Ordering::SeqCst);
50    regs::set_reg(cpu, SYSCALL_NUM_REG, num);
51}
52
53/// Perform a system call in the guest. Should only be run within an injector being
54/// run by [`run_injector`](crate::syscall_injection::run_injector)
55pub async fn syscall(num: target_ulong, args: impl IntoSyscallArgs) -> target_ulong {
56    log::trace!("Injecting syscall {}", num);
57    let cpu = unsafe { &mut *get_cpu() };
58
59    let saved_sp = regs::get_reg(cpu, regs::reg_sp());
60
61    #[cfg(feature = "i386")]
62    let saved_bp = regs::get_reg(cpu, regs::Reg::EBP);
63
64    // Setup the system call
65    set_syscall_num(cpu, num);
66    set_syscall_args(cpu, args.into_syscall_args().await);
67
68    // Wait until the system call has returned to get the return value
69    let ret = Pin::new(&mut SyscallFuture {
70        ret_val: Arc::new(OnceCell::new()),
71    })
72    .await;
73
74    log::trace!("Injected syscall {} returned {}", num, ret);
75
76    regs::set_reg(cpu, regs::reg_sp(), saved_sp);
77
78    #[cfg(feature = "i386")]
79    regs::set_reg(cpu, regs::Reg::EBP, saved_bp);
80
81    ret
82}
83
84/// Perform a system call in the guest. Should only be run within an injector being
85/// run by [`run_injector`](crate::syscall_injection::run_injector). Registers will
86/// not be restored after this syscall has been ran.
87pub async fn syscall_no_return(num: target_ulong, args: impl IntoSyscallArgs) -> ! {
88    log::trace!("syscall_no_return num={}", num);
89    let cpu = unsafe { &mut *get_cpu() };
90
91    // Setup the system call
92    set_syscall_num(cpu, num);
93    set_syscall_args(cpu, args.into_syscall_args().await);
94
95    bail_no_restore_regs().await
96}
97
98/// Bail from the current injector without restoring the original registers
99pub async fn bail_no_restore_regs() -> ! {
100    log::trace!("Bailing without restoring syscall args");
101    INJECTOR_BAIL.store(true, Ordering::SeqCst);
102
103    std::future::pending().await
104}
105
106pub(crate) static INJECTOR_BAIL: AtomicBool = AtomicBool::new(false);
107pub(crate) static WAITING_FOR_SYSCALL: AtomicBool = AtomicBool::new(false);
108
109// Maps ASID to RET_SLOT
110lazy_static! {
111    static ref RET_SLOT: DashMap<ThreadId, Arc<OnceCell<target_ulong>>> = DashMap::new();
112}
113
114pub(crate) fn set_ret_value(cpu: &mut CPUState) {
115    if let Some(ret_slot) = RET_SLOT.get(&ThreadId::current()) {
116        if ret_slot.set(regs::get_reg(cpu, SYSCALL_RET)).is_err() {
117            println!("WARNING: Attempted to double-set syscall return value");
118        }
119
120        log::trace!(
121            "Return value set to {:#x?}",
122            regs::get_reg(cpu, SYSCALL_RET)
123        );
124    }
125}
126
127impl Future for SyscallFuture {
128    type Output = target_ulong;
129
130    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
131        match self.ret_val.get() {
132            // if the return value of the syscall has already been set, then this
133            // future can return
134            Some(ret_val) => Poll::Ready(*ret_val),
135
136            // if the return value hasn't been set, set this future as the next
137            // return value to set
138            None => {
139                let ret_val = Arc::clone(&self.ret_val);
140
141                WAITING_FOR_SYSCALL.store(true, Ordering::SeqCst);
142                RET_SLOT.insert(ThreadId::current(), ret_val);
143
144                Poll::Pending
145            }
146        }
147    }
148}