starnix_core/ptrace/
hooks.rs1use std::sync::Arc;
6use std::sync::atomic::Ordering;
7
8use starnix_logging::CATEGORY_STARNIX;
9use starnix_uapi::signals::SIGTRAP;
10use starnix_uapi::{CLONE_PTRACE, CLONE_UNTRACED, CLONE_VFORK, SIGCHLD, clone_args};
11
12use crate::ptrace::{PtraceEventData, PtraceOptions, StopState, ptrace_attach_from_state};
13use crate::signals::{SignalDetail, SignalInfo, send_signal_first};
14use crate::task::{CurrentTask, Task};
15
16#[derive(Debug)]
18pub struct CurrentTaskPtrace<'a> {
19 current_task: &'a mut CurrentTask,
20}
21
22impl CurrentTask {
23 pub fn ptrace(&mut self) -> CurrentTaskPtrace<'_> {
25 CurrentTaskPtrace { current_task: self }
26 }
27}
28
29impl<'a> CurrentTaskPtrace<'a> {
30 pub fn on_syscall_enter(self) {
32 if self.current_task.trace_syscalls.load(Ordering::Relaxed) {
33 fuchsia_trace::duration!(CATEGORY_STARNIX, "ptrace:syscall_enter");
34 self.syscall_stop(StopState::SyscallEnterStopping, None);
35 }
36 }
37
38 pub fn on_syscall_exit(self, is_error: bool) {
40 if self.current_task.trace_syscalls.load(Ordering::Relaxed) {
41 fuchsia_trace::duration!(CATEGORY_STARNIX, "ptrace:syscall_exit");
42 self.syscall_stop(StopState::SyscallExitStopping, Some(is_error));
43 }
44 }
45
46 #[inline(never)]
47 fn syscall_stop(self, stop_state: StopState, is_error: Option<bool>) {
48 {
49 let mut state = self.current_task.write();
50 self.current_task.trace_syscalls.store(false, Ordering::Relaxed);
51 let Some(ptrace) = &mut state.ptrace else {
52 return;
53 };
54
55 let mut sig = SignalInfo::with_detail(
56 SIGTRAP,
57 (linux_uapi::SIGTRAP | 0x80) as i32,
58 SignalDetail::None,
59 );
60 if ptrace.has_option(PtraceOptions::TRACESYSGOOD) {
61 sig.signal.set_ptrace_syscall_bit();
62 }
63 if let Some(is_error) = is_error {
64 ptrace.last_syscall_was_error = is_error;
65 }
66 state.set_stopped(stop_state, Some(sig), None, None);
67 }
68 self.current_task.block_if_stopped();
69 }
70
71 pub fn on_event(self, trace_kind: PtraceOptions, msg: u64) {
79 if trace_kind.is_empty() {
80 return;
81 }
82 {
83 let mut state = self.current_task.write();
84 let Some(ptrace) = &mut state.ptrace else {
85 return;
86 };
87
88 if !ptrace.has_option(trace_kind) {
89 if trace_kind == PtraceOptions::TRACEEXEC && !ptrace.is_seized() {
92 send_signal_first(self.current_task, state, SignalInfo::kernel(SIGTRAP));
94 }
95
96 return;
97 }
98 let event_data = PtraceEventData::new(trace_kind, msg);
99 let siginfo = SignalInfo::with_detail(
100 SIGTRAP,
101 (((event_data.event as u32) << 8) | SIGTRAP.number()) as i32,
102 SignalDetail::None,
103 );
104 state.set_stopped(
105 StopState::PtraceEventStopping,
106 Some(siginfo),
107 None,
108 Some(event_data),
109 );
110 }
111 self.current_task.block_if_stopped();
112 }
113
114 pub fn on_clone(self, clone_args: &clone_args, child_task: &Arc<Task>) -> PtraceOptions {
117 let (trace_type, ptrace_state) = {
118 let state = self.current_task.read();
119 let Some(ptrace) = &state.ptrace else {
120 return PtraceOptions::empty();
121 };
122
123 let trace_type = if clone_args.flags & (CLONE_UNTRACED as u64) != 0 {
129 PtraceOptions::empty()
130 } else if clone_args.flags & (CLONE_VFORK as u64) != 0 {
131 PtraceOptions::TRACEVFORK
132 } else if clone_args.exit_signal != (SIGCHLD as u64) {
133 PtraceOptions::TRACECLONE
134 } else {
135 PtraceOptions::TRACEFORK
136 };
137
138 let trace_type = if ptrace.has_option(trace_type) {
139 trace_type
140 } else if clone_args.flags & (CLONE_PTRACE as u64) != 0 {
141 PtraceOptions::empty()
142 } else {
143 return PtraceOptions::empty();
144 };
145
146 (trace_type, ptrace.get_core_state())
147 };
148
149 if ptrace_attach_from_state(child_task, ptrace_state).is_err() {
150 return PtraceOptions::empty();
151 }
152 trace_type
153 }
154}