panda/callbacks/
ppp_closures.rs

1use std::{
2    collections::HashMap,
3    ffi::c_void,
4    sync::{
5        atomic::{AtomicU64, Ordering},
6        Mutex,
7    },
8};
9
10/// A reference to a given callback slot which can be used to install,
11/// enable, disable, or otherwise reference, a closure-based callback for
12/// PANDA plugin-to-plugin ("PPP") callbacks.
13///
14/// Since this is a reference to a callback slot and does not include storage
15/// for the callback itself, it can be trivially copied, as well as included in
16/// the callback itself (for the purposes of enabling/disabling).
17///
18/// In order to actually install the callback, you will need to import the trait
19/// for the specific plugin whose callbacks you want to run. In the example below,
20/// the [`ProcStartLinuxCallbacks`] trait provides the [`on_rec_auxv`] method in
21/// order to add the callback.
22///
23/// [`ProcStartLinuxCallbacks`]: crate::plugins::proc_start_linux::ProcStartLinuxCallbacks
24/// [`on_rec_auxv`]: crate::plugins::proc_start_linux::ProcStartLinuxCallbacks::on_rec_auxv
25///
26/// ## Example
27///
28/// ```
29/// use panda::plugins::proc_start_linux::ProcStartLinuxCallbacks;
30/// use panda::PppCallback;
31/// use panda::prelude::*;
32///
33/// PppCallback::new().on_rec_auxv(|_, _, auxv| {
34///     dbg!(auxv);
35/// });
36///
37/// Panda::new().generic("x86_64").replay("test").run();
38/// ```
39///
40/// The above installs a callback to print out the contents of the auxillary vector
41/// using [`dbg`] whenever a new process is spawned in the guest.
42///
43/// Example output:
44///
45/// ```no_run
46/// ...
47/// [panda-rs/examples/closures.rs:18] auxv = AuxvValues {
48///     argc: 3,
49///     argv_ptr_ptr: 0x7fffffffebb8,
50///     arg_ptr: [
51///         0x7fffffffede6,
52///         0x7fffffffedeb,
53///         0x7fffffffedee,
54///     ],
55///     argv: [
56///         "bash",
57///         "-c",
58///         "echo test2",
59///     ],
60///     envc: 20,
61///     env_ptr_ptr: 0x7fffffffebd8,
62///     env_ptr: [
63///         0x7fffffffedf9,
64///         0x7fffffffee04,
65///         // ...
66///         0x7fffffffefc2,
67///         0x7fffffffefe2,
68///     ],
69///     envp: [
70///         "LS_COLORS=",
71///         "LESSCLOSE=/usr/bin/lesspipe %s %s",
72///         "LANG=C.UTwcap2: 0,F-8",
73///         "INVOCATION_ID=0b2d5ea4eb39435388bf53e507047b2f",
74///         "XDG_SESSION_ID=1",
75///         "HUSHLOGIN=FALSE",
76///         "USER=root",
77///         "PWD=/root",
78///         "HOME=/root",
79///         "JOURNAL_STREAM=9:16757",
80///         "XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop",
81///         "MAIL=/var/mail/root",
82///         "SHELL=/bin/bash",
83///         "TERM=vt220",
84///         "SHLVL=1",
85///         "LOGNAME=root",
86///         "XDG_RUNTIME_DIR=/run/user/0",
87///         "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",
88///         "LESSOPEN=| /usr/bin/lesspipe %s",
89///         "_=/bin/bash",
90///     ],
91///     execfn_ptr: 0x7fffffffefee,
92///     execfn: "/bin/bash",
93///     phdr: 0x555555554040,
94///     entry: 0x555555585520,
95///     ehdr: 0x7ffff7ffa000,
96///     // ...
97/// }
98/// Replay completed successfully
99/// Exiting cpu_handle_exception loop
100/// ```
101///
102/// ## Note
103///
104/// Callback closures must have a `'static` lifetime in order to live past the end of the
105/// function. This means that the only references a callback can include are references
106/// to static variables or leaked objects on the heap (See [`Box::leak`] for more info).
107///
108/// If you'd like to reference shared data without leaking, this can be accomplished via
109/// reference counting. See [`Arc`] for more info. If you want to capture data owned
110/// by the current function without sharing it, you can mark your closure as `move` in
111/// order to move all the variables you capture into your closure. (Such as in the above
112/// example, where `count` is moved into the closure for modification)
113///
114/// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
115#[repr(transparent)]
116#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
117pub struct PppCallback(pub(crate) u64);
118
119static CURRENT_CALLBACK_ID: AtomicU64 = AtomicU64::new(0);
120
121impl PppCallback {
122    /// Create a new callback slot which can then be used to install or modify
123    /// a given callback.
124    pub fn new() -> Self {
125        Self(CURRENT_CALLBACK_ID.fetch_add(1, Ordering::SeqCst))
126    }
127
128    /// Enable the callback assigned to the given slot, if any.
129    pub fn enable(&self) {
130        let mut callbacks = CALLBACKS.lock().unwrap();
131        if let Some(callback) = callbacks.get_mut(&self.0) {
132            if !callback.is_enabled {
133                unsafe {
134                    (callback.enable)(callback.closure_ref);
135                }
136                callback.is_enabled = true;
137            }
138        }
139    }
140
141    /// Disable the callback assigned to the given slot, if any.
142    pub fn disable(&self) {
143        let mut callbacks = CALLBACKS.lock().unwrap();
144
145        if let Some(callback) = callbacks.get_mut(&self.0) {
146            if callback.is_enabled {
147                unsafe {
148                    (callback.disable)(callback.closure_ref);
149                }
150                callback.is_enabled = false;
151            }
152        }
153    }
154}
155
156lazy_static::lazy_static! {
157    static ref CALLBACKS: Mutex<HashMap<u64, InternalPppClosureCallback>> = Mutex::new(HashMap::new());
158}
159
160#[doc(hidden)]
161pub struct InternalPppClosureCallback {
162    pub closure_ref: *mut c_void,
163    pub enable: unsafe fn(*mut c_void),
164    pub disable: unsafe fn(*mut c_void),
165    pub drop_fn: unsafe fn(*mut c_void),
166    pub is_enabled: bool,
167}
168
169unsafe impl Sync for InternalPppClosureCallback {}
170unsafe impl Send for InternalPppClosureCallback {}
171
172#[doc(hidden)]
173pub unsafe fn __internal_install_ppp_closure_callback(
174    PppCallback(id): PppCallback,
175    mut callback: InternalPppClosureCallback,
176) {
177    (callback.enable)(callback.closure_ref);
178    callback.is_enabled = true;
179
180    let mut callbacks = CALLBACKS.lock().unwrap();
181    callbacks.insert(id, callback);
182}