Skip to main content

specter/memory/platform/
thread.rs

1//! Thread suspension utilities for safe memory patching
2
3use std::ptr;
4
5use mach2::{
6    kern_return::KERN_SUCCESS,
7    mach_init::mach_thread_self,
8    mach_types::{thread_act_array_t, thread_act_t},
9    message::mach_msg_type_number_t,
10    port::mach_port_t,
11    task::task_threads,
12    thread_act::{thread_resume, thread_suspend},
13    traps::mach_task_self,
14    vm::mach_vm_deallocate,
15    vm_types::mach_vm_size_t,
16};
17
18use thiserror::Error;
19
20#[derive(Error, Debug)]
21/// Errors that can occur during thread manipulation
22pub enum ThreadError {
23    /// Failed to retrieve the list of threads for the current task
24    #[error("Failed to get task threads (kern_return: {0})")]
25    TaskThreadsFailed(i32),
26}
27
28/// Suspends all other threads in the current task to prevent race conditions during memory modification.
29///
30/// This is critical when applying patches or hooks to ensure that no thread is executing the code
31/// being modified.
32///
33/// # Returns
34/// * `Result<Vec<mach_port_t>, ThreadError>` - A list of suspended thread ports (needed to resume them later)
35pub unsafe fn suspend_other_threads() -> Result<Vec<mach_port_t>, ThreadError> {
36    unsafe {
37        let mut thread_list: thread_act_array_t = ptr::null_mut();
38        let mut thread_count: mach_msg_type_number_t = 0;
39
40        let kret = task_threads(mach_task_self(), &mut thread_list, &mut thread_count);
41        if kret != KERN_SUCCESS {
42            return Err(ThreadError::TaskThreadsFailed(kret));
43        }
44
45        let this_thread = mach_thread_self();
46        let mut suspended_threads = Vec::with_capacity(thread_count as usize);
47
48        let threads = std::slice::from_raw_parts(thread_list, thread_count as usize);
49
50        for &thread in threads {
51            if thread != this_thread && thread_suspend(thread) == KERN_SUCCESS {
52                suspended_threads.push(thread);
53            }
54        }
55
56        mach_vm_deallocate(
57            mach_task_self(),
58            thread_list as u64,
59            (thread_count as mach_vm_size_t)
60                * (std::mem::size_of::<thread_act_t>() as mach_vm_size_t),
61        );
62
63        Ok(suspended_threads)
64    }
65}
66
67/// Resumes threads that were previously suspended
68///
69/// # Arguments
70/// * `threads` - A slice of thread ports to resume (returned by `suspend_other_threads`)
71pub unsafe fn resume_threads(threads: &[mach_port_t]) {
72    unsafe {
73        for &thread in threads {
74            thread_resume(thread);
75        }
76    }
77}