panda/syscall_injection/
syscall_future.rs1use 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
24fn 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
53pub 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 set_syscall_num(cpu, num);
66 set_syscall_args(cpu, args.into_syscall_args().await);
67
68 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
84pub 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 set_syscall_num(cpu, num);
93 set_syscall_args(cpu, args.into_syscall_args().await);
94
95 bail_no_restore_regs().await
96}
97
98pub 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
109lazy_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 Some(ret_val) => Poll::Ready(*ret_val),
135
136 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}