panda/syscall_injection/
conversion.rs

1use crate::sys::target_ulong;
2use async_trait::async_trait;
3
4use std::convert::TryInto;
5
6#[cfg(doc)]
7use super::syscall;
8
9/// A trait for converting a single value into a syscall argument.
10///
11/// This trait is asynchronous to allow for system calls to be performed
12/// during the conversion (for example to map memory in the guest).
13#[async_trait]
14pub trait IntoSyscallArg {
15    async fn into_syscall_arg(self) -> target_ulong;
16}
17
18macro_rules! impl_for_ints {
19    ($($int:ty),*) => {
20        $(
21            #[async_trait]
22            impl IntoSyscallArg for $int {
23                async fn into_syscall_arg(self) -> target_ulong {
24                    self.try_into().unwrap()
25                }
26            }
27        )*
28    };
29}
30
31impl_for_ints!(u8, u16, u32, u64);
32
33/// A trait for converting a set of values into a full set of arguments for
34/// performing a system call. This trait is primarily used to provide arguments
35/// to the [`syscall`] function.
36///
37/// This trait is asynchronous to allow for system calls to be performed
38/// during the conversion (for example to map memory in the guest).
39///
40/// This is implemented both for arrays and tuples, up to length 6 (the max number of
41/// system call arguments).
42#[async_trait]
43pub trait IntoSyscallArgs {
44    async fn into_syscall_args(self) -> SyscallArgs;
45}
46
47/// Arguments to be passed to a system call
48///
49/// Should be converted to using [`IntoSyscallArgs`]. Conversion is handled generically
50/// by [`syscall`].
51pub struct SyscallArgs {
52    regs: [target_ulong; 6],
53    regs_used: usize,
54}
55
56impl SyscallArgs {
57    pub fn iter_args(&self) -> impl Iterator<Item = target_ulong> + '_ {
58        self.regs.iter().copied().take(self.regs_used)
59    }
60}
61
62#[doc(hidden)]
63pub struct SyscallCount<const N: usize>;
64
65#[doc(hidden)]
66pub trait LessThan7 {}
67
68impl LessThan7 for SyscallCount<0> {}
69impl LessThan7 for SyscallCount<1> {}
70impl LessThan7 for SyscallCount<2> {}
71impl LessThan7 for SyscallCount<3> {}
72impl LessThan7 for SyscallCount<4> {}
73impl LessThan7 for SyscallCount<5> {}
74impl LessThan7 for SyscallCount<6> {}
75
76#[async_trait]
77impl<Arg: IntoSyscallArg + Send, const N: usize> IntoSyscallArgs for [Arg; N]
78where
79    SyscallCount<N>: LessThan7,
80{
81    async fn into_syscall_args(self) -> SyscallArgs {
82        assert!(N <= 6, "Only up to 6 syscall arguments are allowed");
83        let mut regs = [0; 6];
84        for (i, arg) in IntoIterator::into_iter(self).enumerate() {
85            regs[i] = arg.into_syscall_arg().await;
86        }
87
88        SyscallArgs {
89            regs,
90            regs_used: N as _,
91        }
92    }
93}
94
95macro_rules! impl_for_tuples {
96    ($first:ident $(, $nth:ident)*) => {
97        #[async_trait]
98        impl<$first $(, $nth)*> IntoSyscallArgs for ($first, $($nth),*)
99            where $first: IntoSyscallArg + Send + Sync,
100                  $($nth: IntoSyscallArg + Send + Sync),*
101        {
102            #[allow(non_snake_case)]
103            async fn into_syscall_args(self) -> SyscallArgs {
104                let ($first, $($nth),*) = self;
105                let arr = [
106                    $first.into_syscall_arg().await,
107                    $($nth.into_syscall_arg().await),*
108                ];
109                let mut regs = [0; 6];
110                let regs_used = arr.len();
111
112                regs[..regs_used].copy_from_slice(&arr[..]);
113
114                SyscallArgs { regs, regs_used }
115            }
116        }
117
118        impl_for_tuples!($($nth),*);
119    };
120    () => {
121        #[async_trait]
122        impl IntoSyscallArgs for () {
123            async fn into_syscall_args(self) -> SyscallArgs {
124                SyscallArgs { regs: [0; 6], regs_used: 0 }
125            }
126        }
127    }
128}
129
130impl_for_tuples!(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6);