1use crate::arch::execution::new_syscall_from_state;
6use crate::mm::{IOVecPtr, MemoryAccessor, MemoryAccessorExt};
7use crate::ptrace::StopState;
8use crate::security;
9use crate::signals::syscalls::WaitingOptions;
10use crate::signals::{
11 SignalDetail, SignalInfo, UncheckedSignalInfo, send_signal_first, send_standard_signal,
12};
13use crate::task::{
14 CurrentTask, PidTable, ProcessSelector, Task, TaskMutableState, ThreadGroup, ThreadState,
15 WaitQueue, ZombieNotification, ZombieProcess,
16};
17use bitflags::bitflags;
18use starnix_logging::track_stub;
19use starnix_registers::HeapRegs;
20use starnix_sync::{LockBefore, Locked, MmDumpable, ThreadGroupLimits, Unlocked};
21use starnix_syscalls::SyscallResult;
22use starnix_syscalls::decls::SyscallDecl;
23use starnix_types::ownership::{OwnedRef, Releasable};
24use starnix_uapi::auth::PTRACE_MODE_ATTACH_REALCREDS;
25use starnix_uapi::elf::ElfNoteType;
26use starnix_uapi::errors::Errno;
27use starnix_uapi::signals::{SIGKILL, SIGSTOP, SIGTRAP, SigSet, Signal, UncheckedSignal};
28#[allow(unused_imports)]
29use starnix_uapi::user_address::ArchSpecific;
30use starnix_uapi::user_address::{LongPtr, MultiArchUserRef, UserAddress, UserRef};
31use starnix_uapi::{
32 PTRACE_CONT, PTRACE_DETACH, PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC, PTRACE_EVENT_EXIT,
33 PTRACE_EVENT_FORK, PTRACE_EVENT_SECCOMP, PTRACE_EVENT_STOP, PTRACE_EVENT_VFORK,
34 PTRACE_EVENT_VFORK_DONE, PTRACE_GET_SYSCALL_INFO, PTRACE_GETEVENTMSG, PTRACE_GETREGSET,
35 PTRACE_GETSIGINFO, PTRACE_GETSIGMASK, PTRACE_INTERRUPT, PTRACE_KILL, PTRACE_LISTEN,
36 PTRACE_O_EXITKILL, PTRACE_O_TRACECLONE, PTRACE_O_TRACEEXEC, PTRACE_O_TRACEEXIT,
37 PTRACE_O_TRACEFORK, PTRACE_O_TRACESYSGOOD, PTRACE_O_TRACEVFORK, PTRACE_O_TRACEVFORKDONE,
38 PTRACE_PEEKDATA, PTRACE_PEEKTEXT, PTRACE_PEEKUSR, PTRACE_POKEDATA, PTRACE_POKETEXT,
39 PTRACE_POKEUSR, PTRACE_SETOPTIONS, PTRACE_SETREGSET, PTRACE_SETSIGINFO, PTRACE_SETSIGMASK,
40 PTRACE_SYSCALL, PTRACE_SYSCALL_INFO_ENTRY, PTRACE_SYSCALL_INFO_EXIT, PTRACE_SYSCALL_INFO_NONE,
41 clone_args, errno, error, pid_t, ptrace_syscall_info, tid_t, uapi,
42};
43use zerocopy::IntoBytes;
44
45use std::collections::BTreeMap;
46use std::sync::atomic::Ordering;
47use std::sync::{Arc, Weak};
48
49#[cfg(target_arch = "x86_64")]
50use starnix_uapi::{PTRACE_GETREGS, user};
51
52#[cfg(all(target_arch = "aarch64"))]
53use starnix_uapi::arch32::PTRACE_GETREGS;
54
55type UserRegsStructPtr =
56 MultiArchUserRef<starnix_uapi::user_regs_struct, starnix_uapi::arch32::user_regs_struct>;
57
58uapi::check_arch_independent_layout! {
59 ptrace_syscall_info {
60 op,
61 arch,
62 instruction_pointer,
63 stack_pointer,
64 __bindgen_anon_1,
65 }
66
67 ptrace_syscall_info__bindgen_ty_1 {
68 entry,
69 exit,
70 seccomp,
71 }
72
73 ptrace_syscall_info__bindgen_ty_1__bindgen_ty_1 {
74 nr,
75 args,
76 }
77
78 ptrace_syscall_info__bindgen_ty_1__bindgen_ty_2 {
79 rval,
80 is_error,
81 }
82
83 ptrace_syscall_info__bindgen_ty_1__bindgen_ty_3 {
84 nr,
85 args,
86 ret_data,
87 }
88}
89
90#[derive(Clone, Default, PartialEq)]
94pub enum PtraceStatus {
95 #[default]
97 Default,
98 Continuing,
100 Listening,
107}
108
109impl PtraceStatus {
110 pub fn is_continuing(&self) -> bool {
111 *self == PtraceStatus::Continuing
112 }
113}
114
115#[derive(Copy, Clone, PartialEq)]
117pub enum PtraceAttachType {
118 Attach,
120 Seize,
122}
123
124bitflags! {
125 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
126 #[repr(transparent)]
127 pub struct PtraceOptions: u32 {
128 const EXITKILL = starnix_uapi::PTRACE_O_EXITKILL;
129 const TRACECLONE = starnix_uapi::PTRACE_O_TRACECLONE;
130 const TRACEEXEC = starnix_uapi::PTRACE_O_TRACEEXEC;
131 const TRACEEXIT = starnix_uapi::PTRACE_O_TRACEEXIT;
132 const TRACEFORK = starnix_uapi::PTRACE_O_TRACEFORK;
133 const TRACESYSGOOD = starnix_uapi::PTRACE_O_TRACESYSGOOD;
134 const TRACEVFORK = starnix_uapi::PTRACE_O_TRACEVFORK;
135 const TRACEVFORKDONE = starnix_uapi::PTRACE_O_TRACEVFORKDONE;
136 const TRACESECCOMP = starnix_uapi::PTRACE_O_TRACESECCOMP;
137 const SUSPEND_SECCOMP = starnix_uapi::PTRACE_O_SUSPEND_SECCOMP;
138 }
139}
140
141#[repr(u32)]
142#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
143pub enum PtraceEvent {
144 #[default]
145 None = 0,
146 Stop = PTRACE_EVENT_STOP,
147 Clone = PTRACE_EVENT_CLONE,
148 Fork = PTRACE_EVENT_FORK,
149 Vfork = PTRACE_EVENT_VFORK,
150 VforkDone = PTRACE_EVENT_VFORK_DONE,
151 Exec = PTRACE_EVENT_EXEC,
152 Exit = PTRACE_EVENT_EXIT,
153 Seccomp = PTRACE_EVENT_SECCOMP,
154}
155
156impl PtraceEvent {
157 pub fn from_option(option: &PtraceOptions) -> Self {
158 match *option {
159 PtraceOptions::TRACECLONE => PtraceEvent::Clone,
160 PtraceOptions::TRACEFORK => PtraceEvent::Fork,
161 PtraceOptions::TRACEVFORK => PtraceEvent::Vfork,
162 PtraceOptions::TRACEVFORKDONE => PtraceEvent::VforkDone,
163 PtraceOptions::TRACEEXEC => PtraceEvent::Exec,
164 PtraceOptions::TRACEEXIT => PtraceEvent::Exit,
165 PtraceOptions::TRACESECCOMP => PtraceEvent::Seccomp,
166 _ => unreachable!("Bad ptrace event specified"),
167 }
168 }
169}
170
171pub struct PtraceEventData {
173 pub event: PtraceEvent,
175
176 pub msg: u64,
178}
179
180impl PtraceEventData {
181 pub fn new(option: PtraceOptions, msg: u64) -> Self {
182 Self { event: PtraceEvent::from_option(&option), msg }
183 }
184 pub fn new_from_event(event: PtraceEvent, msg: u64) -> Self {
185 Self { event, msg }
186 }
187}
188
189#[derive(Clone)]
192pub struct PtraceCoreState {
193 pub task: Weak<Task>,
195
196 pub thread_group: Weak<ThreadGroup>,
198
199 pub attach_type: PtraceAttachType,
202
203 pub options: PtraceOptions,
205
206 pub tracer_waiters: Arc<WaitQueue>,
209}
210
211impl PtraceCoreState {
212 pub fn has_option(&self, option: PtraceOptions) -> bool {
213 self.options.contains(option)
214 }
215}
216
217pub struct PtraceState {
219 pub core_state: PtraceCoreState,
221
222 pub tracee_waiters: WaitQueue,
225
226 pub last_signal: Option<SignalInfo>,
229
230 pub last_signal_waitable: bool,
233
234 pub event_data: Option<PtraceEventData>,
236
237 pub stop_status: PtraceStatus,
240
241 pub last_syscall_was_error: bool,
243}
244
245impl PtraceState {
246 pub fn new(
247 task: Weak<Task>,
248 thread_group: Weak<ThreadGroup>,
249 attach_type: PtraceAttachType,
250 options: PtraceOptions,
251 ) -> Box<Self> {
252 Box::new(PtraceState {
253 core_state: PtraceCoreState {
254 task,
255 thread_group,
256 attach_type,
257 options,
258 tracer_waiters: Arc::new(WaitQueue::default()),
259 },
260 tracee_waiters: WaitQueue::default(),
261 last_signal: None,
262 last_signal_waitable: false,
263 event_data: None,
264 stop_status: PtraceStatus::default(),
265 last_syscall_was_error: false,
266 })
267 }
268
269 pub fn is_seized(&self) -> bool {
270 self.core_state.attach_type == PtraceAttachType::Seize
271 }
272
273 pub fn get_attach_type(&self) -> PtraceAttachType {
274 self.core_state.attach_type
275 }
276
277 pub fn is_waitable(&self, stop: StopState, options: &WaitingOptions) -> bool {
278 if self.stop_status == PtraceStatus::Listening {
279 return self.last_signal_waitable;
281 }
282 if !options.wait_for_continued && !stop.is_stopping_or_stopped() {
283 return false;
285 }
286 self.last_signal_waitable && !stop.is_in_progress()
287 }
288
289 pub fn set_last_signal(&mut self, mut signal: Option<SignalInfo>) {
290 if let Some(ref mut siginfo) = signal {
291 if siginfo.signal == SIGKILL {
294 return;
295 }
296 self.last_signal_waitable = true;
297 self.last_signal = signal;
298 }
299 }
300
301 pub fn set_last_event(&mut self, event: Option<PtraceEventData>) {
302 if event.is_some() {
303 self.event_data = event;
304 }
305 }
306
307 pub fn get_last_signal_ref(&self) -> Option<&SignalInfo> {
308 self.last_signal.as_ref()
309 }
310
311 pub fn get_last_signal(&mut self, keep_signal_waitable: bool) -> Option<SignalInfo> {
313 self.last_signal_waitable = keep_signal_waitable;
314 self.last_signal.clone()
315 }
316
317 pub fn has_option(&self, option: PtraceOptions) -> bool {
318 self.core_state.has_option(option)
319 }
320
321 pub fn set_options_from_bits(&mut self, option: u32) -> Result<(), Errno> {
322 if let Some(options) = PtraceOptions::from_bits(option) {
323 self.core_state.options = options;
324 Ok(())
325 } else {
326 error!(EINVAL)
327 }
328 }
329
330 pub fn get_options(&self) -> PtraceOptions {
331 self.core_state.options
332 }
333
334 pub fn get_core_state(&self) -> PtraceCoreState {
336 self.core_state.clone()
337 }
338
339 pub fn tracer_waiters(&self) -> &Arc<WaitQueue> {
340 &self.core_state.tracer_waiters
341 }
342
343 pub fn get_target_syscall(
350 &self,
351 target: &Task,
352 state: &TaskMutableState,
353 ) -> Result<(i32, ptrace_syscall_info), Errno> {
354 #[cfg(target_arch = "x86_64")]
355 let arch = starnix_uapi::AUDIT_ARCH_X86_64;
356 #[cfg(target_arch = "aarch64")]
357 let arch = starnix_uapi::AUDIT_ARCH_AARCH64;
358 #[cfg(target_arch = "riscv64")]
359 let arch = starnix_uapi::AUDIT_ARCH_RISCV64;
360
361 let mut info = ptrace_syscall_info { arch, ..Default::default() };
362 let mut info_len = memoffset::offset_of!(ptrace_syscall_info, __bindgen_anon_1);
363
364 match &state.captured_thread_state {
365 Some(captured) => {
366 let registers = captured.thread_state.registers.clone();
367 info.instruction_pointer = registers.instruction_pointer_register();
368 info.stack_pointer = registers.stack_pointer_register();
369 #[cfg(target_arch = "aarch64")]
370 if captured.thread_state.is_arch32() {
371 info.arch = starnix_uapi::AUDIT_ARCH_ARM;
374 }
375 match target.load_stopped() {
376 StopState::SyscallEnterStopped => {
377 let syscall_decl = SyscallDecl::from_number(
378 registers.syscall_register(),
379 captured.thread_state.arch_width(),
380 );
381 let syscall = new_syscall_from_state(syscall_decl, &captured.thread_state);
382 info.op = PTRACE_SYSCALL_INFO_ENTRY as u8;
383 let entry = linux_uapi::ptrace_syscall_info__bindgen_ty_1__bindgen_ty_1 {
384 nr: syscall.decl.number,
385 args: [
386 syscall.arg0.raw(),
387 syscall.arg1.raw(),
388 syscall.arg2.raw(),
389 syscall.arg3.raw(),
390 syscall.arg4.raw(),
391 syscall.arg5.raw(),
392 ],
393 };
394 info_len += memoffset::offset_of!(
395 linux_uapi::ptrace_syscall_info__bindgen_ty_1__bindgen_ty_1,
396 args
397 ) + std::mem::size_of_val(&entry.args);
398 info.__bindgen_anon_1.entry = entry;
399 }
400 StopState::SyscallExitStopped => {
401 info.op = PTRACE_SYSCALL_INFO_EXIT as u8;
402 let exit = linux_uapi::ptrace_syscall_info__bindgen_ty_1__bindgen_ty_2 {
403 rval: registers.return_register() as i64,
404 is_error: state
405 .ptrace
406 .as_ref()
407 .map_or(0, |ptrace| ptrace.last_syscall_was_error as u8),
408 ..Default::default()
409 };
410 info_len += memoffset::offset_of!(
411 linux_uapi::ptrace_syscall_info__bindgen_ty_1__bindgen_ty_2,
412 is_error
413 ) + std::mem::size_of_val(&exit.is_error);
414 info.__bindgen_anon_1.exit = exit;
415 }
416 _ => {
417 info.op = PTRACE_SYSCALL_INFO_NONE as u8;
418 }
419 };
420 }
421 _ => (),
422 }
423 Ok((info_len as i32, info))
424 }
425
426 pub fn get_core_state_for_clone(
431 &self,
432 clone_args: &clone_args,
433 ) -> (PtraceOptions, Option<PtraceCoreState>) {
434 let trace_type = if clone_args.flags & (starnix_uapi::CLONE_UNTRACED as u64) != 0 {
440 PtraceOptions::empty()
441 } else {
442 if clone_args.flags & (starnix_uapi::CLONE_VFORK as u64) != 0 {
443 PtraceOptions::TRACEVFORK
444 } else if clone_args.exit_signal != (starnix_uapi::SIGCHLD as u64) {
445 PtraceOptions::TRACECLONE
446 } else {
447 PtraceOptions::TRACEFORK
448 }
449 };
450
451 if !self.has_option(trace_type)
452 && (clone_args.flags & (starnix_uapi::CLONE_PTRACE as u64) == 0)
453 {
454 return (PtraceOptions::empty(), None);
455 }
456
457 (trace_type, Some(self.get_core_state()))
458 }
459}
460
461struct TracedZombie {
463 artificial_zombie: ZombieProcess,
465
466 notification: Option<ZombieNotification>,
469}
470
471impl TracedZombie {
472 fn new(artificial_zombie: ZombieProcess) -> Self {
473 Self { artificial_zombie, notification: None }
474 }
475
476 fn new_with_notification(
477 artificial_zombie: ZombieProcess,
478 notification: ZombieNotification,
479 ) -> Self {
480 Self { artificial_zombie, notification: Some(notification) }
481 }
482
483 fn set_parent(
484 &mut self,
485 new_zombie: Option<OwnedRef<ZombieProcess>>,
486 new_parent: &ThreadGroup,
487 ) {
488 if let Some(new_zombie) = new_zombie {
489 self.notification = Some(ZombieNotification {
490 recipient: new_parent.weak_self.clone(),
491 zombie: new_zombie,
492 });
493 } else if let Some(ref mut notification) = self.notification {
494 notification.recipient = new_parent.weak_self.clone();
495 }
496 }
497
498 fn detach(self, pids: &mut PidTable) -> Option<ZombieNotification> {
499 self.artificial_zombie.release(pids);
500 self.notification
501 }
502}
503
504#[derive(Default)]
508pub struct ZombiePtracees {
509 zombies: BTreeMap<tid_t, TracedZombie>,
512}
513
514impl Drop for ZombiePtracees {
515 fn drop(&mut self) {
516 assert_eq!(self.zombies.len(), 0);
517 }
518}
519
520impl ZombiePtracees {
521 pub fn new() -> Self {
522 Self::default()
523 }
524
525 pub fn add(&mut self, pids: &mut PidTable, tid: tid_t, zombie: ZombieProcess) {
528 if let std::collections::btree_map::Entry::Vacant(entry) = self.zombies.entry(tid) {
529 entry.insert(TracedZombie::new(zombie));
530 } else {
531 zombie.release(pids);
532 }
533 }
534
535 pub fn detach(&mut self, pids: &mut PidTable, tid: tid_t) -> Option<ZombieNotification> {
539 self.zombies.remove(&tid).and_then(|traced_zombie| traced_zombie.detach(pids))
540 }
541
542 pub fn detach_all(&mut self, pids: &mut PidTable) -> Vec<ZombieNotification> {
546 let traced_zombies = std::mem::replace(&mut self.zombies, Default::default());
547 traced_zombies
548 .into_iter()
549 .filter_map(|(_, traced_zombie)| traced_zombie.detach(pids))
550 .collect()
551 }
552
553 pub fn is_empty(&self) -> bool {
554 self.zombies.is_empty()
555 }
556
557 pub fn set_parent_of(
560 &mut self,
561 tracee: tid_t,
562 new_zombie: Option<OwnedRef<ZombieProcess>>,
563 new_parent: &ThreadGroup,
564 ) {
565 match self.zombies.entry(tracee) {
566 std::collections::btree_map::Entry::Vacant(entry) => {
567 if let Some(new_zombie) = new_zombie {
568 entry.insert(TracedZombie::new_with_notification(
569 new_zombie.as_artificial(),
570 ZombieNotification::new(new_parent.weak_self.clone(), new_zombie),
571 ));
572 }
573 }
574 std::collections::btree_map::Entry::Occupied(mut entry) => {
575 entry.get_mut().set_parent(new_zombie, new_parent);
576 }
577 }
578 }
579
580 pub fn reparent(old_parent: &ThreadGroup, new_parent: &ThreadGroup) {
583 let mut lockless_list = old_parent.read().deferred_zombie_ptracers.clone();
584
585 for deferred_zombie_ptracer in &lockless_list {
586 if let Some(tg) = deferred_zombie_ptracer.tracer_thread_group_key.upgrade() {
587 tg.write().zombie_ptracees.set_parent_of(
588 deferred_zombie_ptracer.tracee_tid,
589 None,
590 new_parent,
591 );
592 }
593 }
594 let mut new_state = new_parent.write();
595 new_state.deferred_zombie_ptracers.append(&mut lockless_list);
596 }
597
598 pub fn has_zombie_matching(&self, selector: &ProcessSelector) -> bool {
601 self.zombies.values().any(|z| z.artificial_zombie.matches_selector(selector))
602 }
603
604 pub fn has_tracee(&self, tid: tid_t) -> bool {
607 self.zombies.contains_key(&tid)
608 }
609
610 pub fn get_waitable_entry(
614 &mut self,
615 selector: &ProcessSelector,
616 options: &WaitingOptions,
617 ) -> Option<(ZombieProcess, Option<(Weak<ThreadGroup>, OwnedRef<ZombieProcess>)>)> {
618 let Some((t, found_zombie)) = self
621 .zombies
622 .iter()
623 .map(|(t, z)| (*t, &z.artificial_zombie))
624 .rfind(|(_, zombie)| zombie.matches_selector_and_waiting_option(selector, options))
625 else {
626 return None;
627 };
628
629 let result;
630 if !options.keep_waitable_state {
631 result = self.zombies.remove(&t).map(|traced_zombie| {
633 (
634 traced_zombie.artificial_zombie,
635 traced_zombie.notification.map(|n| (n.recipient, n.zombie)),
636 )
637 });
638 } else {
639 result = Some((found_zombie.as_artificial(), None));
640 }
641
642 result
643 }
644}
645
646pub const PR_SET_PTRACER_ANY: i32 = -1;
649
650#[derive(Copy, Clone, Default, PartialEq)]
653pub enum PtraceAllowedPtracers {
654 #[default]
655 None,
656 Some(pid_t),
657 Any,
658}
659
660fn ptrace_cont<L>(
665 locked: &mut Locked<L>,
666 tracee: &Task,
667 data: &UserAddress,
668 detach: bool,
669) -> Result<(), Errno>
670where
671 L: LockBefore<ThreadGroupLimits>,
672{
673 let data = data.ptr() as u64;
674 let new_state;
675 let mut siginfo = if data != 0 {
676 let signal = Signal::try_from(UncheckedSignal::new(data))?;
677 Some(SignalInfo::kernel(signal))
678 } else {
679 None
680 };
681
682 let mut state = tracee.write();
683 let is_listen = state.is_ptrace_listening();
684
685 if tracee.load_stopped().is_waking_or_awake() && !is_listen {
686 if detach {
687 state.set_ptrace(None)?;
688 }
689 return error!(EIO);
690 }
691
692 if !state.can_accept_ptrace_commands() && !detach {
693 return error!(ESRCH);
694 }
695
696 if let Some(ptrace) = &mut state.ptrace {
697 if data != 0 {
698 new_state = PtraceStatus::Continuing;
699 if let Some(last_signal) = &mut ptrace.last_signal {
700 if let Some(si) = siginfo {
701 let new_signal = si.signal;
702 last_signal.signal = new_signal;
703 }
704 siginfo = Some(last_signal.clone());
705 }
706 } else {
707 new_state = PtraceStatus::Default;
708 ptrace.last_signal = None;
709 ptrace.event_data = None;
710 }
711 ptrace.stop_status = new_state;
712
713 if is_listen {
714 state.notify_ptracees();
715 }
716 }
717
718 if let Some(siginfo) = siginfo {
719 send_signal_first(locked, &tracee, state, siginfo);
721 } else {
722 state.set_stopped(StopState::Waking, None, None, None);
723 drop(state);
724 tracee.thread_group().set_stopped(StopState::Waking, None, false);
725 }
726 if detach {
727 tracee.write().set_ptrace(None)?;
728 }
729 Ok(())
730}
731
732fn ptrace_interrupt(tracee: &Task) -> Result<(), Errno> {
733 let mut state = tracee.write();
734 if let Some(ptrace) = &mut state.ptrace {
735 if !ptrace.is_seized() {
736 return error!(EIO);
737 }
738 let status = ptrace.stop_status.clone();
739 ptrace.stop_status = PtraceStatus::Default;
740 let event_data = Some(PtraceEventData::new_from_event(PtraceEvent::Stop, 0));
741 if status == PtraceStatus::Listening {
742 let signal = ptrace.last_signal.clone();
743 state.set_stopped(StopState::PtraceEventStopped, signal, None, event_data);
747 } else {
748 state.set_stopped(
749 StopState::PtraceEventStopping,
750 Some(SignalInfo::kernel(SIGTRAP)),
751 None,
752 event_data,
753 );
754 drop(state);
755 tracee.interrupt();
756 }
757 }
758 Ok(())
759}
760
761fn ptrace_listen(tracee: &Task) -> Result<(), Errno> {
762 let mut state = tracee.write();
763 if let Some(ptrace) = &mut state.ptrace {
764 if !ptrace.is_seized()
765 || (ptrace.last_signal_waitable
766 && ptrace
767 .event_data
768 .as_ref()
769 .is_some_and(|event_data| event_data.event != PtraceEvent::Stop))
770 {
771 return error!(EIO);
772 }
773 ptrace.stop_status = PtraceStatus::Listening;
774 }
775 Ok(())
776}
777
778pub fn ptrace_detach<L>(
779 locked: &mut Locked<L>,
780 pids: &mut PidTable,
781 thread_group: &ThreadGroup,
782 tracee: &Task,
783 data: &UserAddress,
784) -> Result<(), Errno>
785where
786 L: LockBefore<ThreadGroupLimits>,
787{
788 if let Err(x) = ptrace_cont(locked, &tracee, &data, true) {
789 return Err(x);
790 }
791 let tid = tracee.get_tid();
792 thread_group.ptracees.lock().remove(&tid);
793 let zombie_notification = thread_group.write().zombie_ptracees.detach(pids, tid);
794 if let Some(zombie_notification) = zombie_notification {
795 zombie_notification.deliver(pids);
796 }
797 Ok(())
798}
799
800pub fn ptrace_dispatch<L>(
802 locked: &mut Locked<L>,
803 current_task: &mut CurrentTask,
804 request: u32,
805 pid: pid_t,
806 addr: UserAddress,
807 data: UserAddress,
808) -> Result<SyscallResult, Errno>
809where
810 L: LockBefore<ThreadGroupLimits>,
811{
812 let mut pids = current_task.kernel().pids.write();
813 let tracee = pids.get_task(pid)?;
814
815 if let Some(ptrace) = &tracee.read().ptrace {
816 let is_tracer = ptrace
817 .core_state
818 .thread_group
819 .upgrade()
820 .is_some_and(|tg| Arc::ptr_eq(&tg, current_task.thread_group()));
821 if !is_tracer {
822 return error!(ESRCH);
823 }
824 }
825
826 match request {
829 PTRACE_KILL => {
830 let siginfo = SignalInfo::with_detail(
831 SIGKILL,
832 (SIGTRAP.number() | PTRACE_KILL << 8) as i32,
833 SignalDetail::None,
834 );
835 send_standard_signal(locked, &tracee, siginfo);
836 return Ok(starnix_syscalls::SUCCESS);
837 }
838 PTRACE_INTERRUPT => {
839 ptrace_interrupt(tracee.as_ref())?;
840 return Ok(starnix_syscalls::SUCCESS);
841 }
842 PTRACE_LISTEN => {
843 ptrace_listen(&tracee)?;
844 return Ok(starnix_syscalls::SUCCESS);
845 }
846 PTRACE_CONT => {
847 ptrace_cont(locked, &tracee, &data, false)?;
848 return Ok(starnix_syscalls::SUCCESS);
849 }
850 PTRACE_SYSCALL => {
851 tracee.trace_syscalls.store(true, std::sync::atomic::Ordering::Relaxed);
852 ptrace_cont(locked, &tracee, &data, false)?;
853 return Ok(starnix_syscalls::SUCCESS);
854 }
855 PTRACE_DETACH => {
856 ptrace_detach(locked, &mut pids, current_task.thread_group(), tracee.as_ref(), &data)?;
857 return Ok(starnix_syscalls::SUCCESS);
858 }
859 _ => {}
860 }
861
862 let mut state = tracee.write();
864 if !state.can_accept_ptrace_commands() {
865 return error!(ESRCH);
866 }
867
868 match request {
869 PTRACE_PEEKDATA | PTRACE_PEEKTEXT => {
870 let Some(captured) = &mut state.captured_thread_state else {
871 return error!(ESRCH);
872 };
873
874 let src = LongPtr::new(captured.as_ref(), addr);
877 let val = tracee.read_multi_arch_object(src)?;
878
879 let dst = LongPtr::new(&src, data);
880 current_task.write_multi_arch_object(dst, val)?;
881 Ok(starnix_syscalls::SUCCESS)
882 }
883 PTRACE_POKEDATA | PTRACE_POKETEXT => {
884 let Some(captured) = &mut state.captured_thread_state else {
885 return error!(ESRCH);
886 };
887
888 let bytes = if captured.is_arch32() {
889 u32::try_from(data.ptr()).map_err(|_| errno!(EINVAL))?.to_ne_bytes().to_vec()
890 } else {
891 data.ptr().to_ne_bytes().to_vec()
892 };
893
894 tracee.mm()?.force_write_memory(addr, &bytes)?;
895
896 Ok(starnix_syscalls::SUCCESS)
897 }
898 PTRACE_PEEKUSR => {
899 let Some(captured) = &mut state.captured_thread_state else {
900 return error!(ESRCH);
901 };
902
903 let dst = LongPtr::new(captured.as_ref(), data);
904 let val = ptrace_peekuser(&mut captured.thread_state, addr.ptr() as usize)?;
905 current_task.write_multi_arch_object(dst, val as u64)?;
906 return Ok(starnix_syscalls::SUCCESS);
907 }
908 PTRACE_POKEUSR => {
909 ptrace_pokeuser(&mut *state, data.ptr() as usize, addr.ptr() as usize)?;
910 return Ok(starnix_syscalls::SUCCESS);
911 }
912 PTRACE_GETREGSET => {
913 if let Some(ref mut captured) = state.captured_thread_state {
914 let uiv = IOVecPtr::new(current_task, data);
915 let mut iv = current_task.read_multi_arch_object(uiv)?;
916 let base = iv.iov_base.addr;
917 let mut len = iv.iov_len as usize;
918 ptrace_getregset(
919 current_task,
920 &captured.thread_state,
921 ElfNoteType::try_from(addr.ptr() as usize)?,
922 base,
923 &mut len,
924 )?;
925 iv.iov_len = len as u64;
926 current_task.write_multi_arch_object(uiv, iv)?;
927 return Ok(starnix_syscalls::SUCCESS);
928 }
929 error!(ESRCH)
930 }
931 PTRACE_SETREGSET => {
932 if let Some(ref mut captured) = state.captured_thread_state {
933 captured.dirty = true;
934 let uiv = IOVecPtr::new(current_task, data);
935 let iv = current_task.read_multi_arch_object(uiv)?;
936 let base = iv.iov_base.addr;
937 let len = iv.iov_len as usize;
938 ptrace_setregset(
939 current_task,
940 &mut captured.thread_state,
941 ElfNoteType::try_from(addr.ptr() as usize)?,
942 base,
943 len,
944 )?;
945 return Ok(starnix_syscalls::SUCCESS);
946 }
947 error!(ESRCH)
948 }
949 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
950 PTRACE_GETREGS => {
951 if let Some(captured) = &mut state.captured_thread_state {
952 let mut len = usize::MAX;
953 ptrace_getregset(
954 current_task,
955 &captured.thread_state,
956 ElfNoteType::PrStatus,
957 data.ptr() as u64,
958 &mut len,
959 )?;
960 return Ok(starnix_syscalls::SUCCESS);
961 }
962 error!(ESRCH)
963 }
964 PTRACE_SETSIGMASK => {
965 if addr.ptr() != std::mem::size_of::<SigSet>() {
968 return error!(EINVAL);
969 }
970 let src: UserRef<SigSet> = UserRef::from(data);
972 let val = current_task.read_object(src)?;
973 state.set_signal_mask(val);
974
975 Ok(starnix_syscalls::SUCCESS)
976 }
977 PTRACE_GETSIGMASK => {
978 if addr.ptr() != std::mem::size_of::<SigSet>() {
981 return error!(EINVAL);
982 }
983 let dst: UserRef<SigSet> = UserRef::from(data);
985 let val = state.signal_mask();
986 current_task.write_object(dst, &val)?;
987 Ok(starnix_syscalls::SUCCESS)
988 }
989 PTRACE_GETSIGINFO => {
990 if let Some(ptrace) = &state.ptrace {
991 if let Some(signal) = ptrace.last_signal.as_ref() {
992 let dst = MultiArchUserRef::<uapi::siginfo_t, uapi::arch32::siginfo_t>::new(
993 current_task,
994 data,
995 );
996 signal.write(current_task, dst)?;
997 } else {
998 return error!(EINVAL);
999 }
1000 }
1001 Ok(starnix_syscalls::SUCCESS)
1002 }
1003 PTRACE_SETSIGINFO => {
1004 let siginfo = UncheckedSignalInfo::read_from_siginfo(current_task, data)?.try_into()?;
1005 if let Some(ptrace) = &mut state.ptrace {
1006 ptrace.last_signal = Some(siginfo);
1007 }
1008 Ok(starnix_syscalls::SUCCESS)
1009 }
1010 PTRACE_GET_SYSCALL_INFO => {
1011 if let Some(ptrace) = &state.ptrace {
1012 let (size, info) = ptrace.get_target_syscall(&tracee, &state)?;
1013 let dst: UserRef<ptrace_syscall_info> = UserRef::from(data);
1014 let len = std::cmp::min(std::mem::size_of::<ptrace_syscall_info>(), addr.ptr());
1015 let src = unsafe {
1018 std::slice::from_raw_parts(
1019 &info as *const ptrace_syscall_info as *const u8,
1020 len as usize,
1021 )
1022 };
1023 current_task.write_memory(dst.addr(), src)?;
1024 Ok(size.into())
1025 } else {
1026 error!(ESRCH)
1027 }
1028 }
1029 PTRACE_SETOPTIONS => {
1030 let mask = data.ptr() as u32;
1031 if mask != 0
1033 && (mask
1034 & !(PTRACE_O_TRACESYSGOOD
1035 | PTRACE_O_TRACECLONE
1036 | PTRACE_O_TRACEFORK
1037 | PTRACE_O_TRACEVFORK
1038 | PTRACE_O_TRACEVFORKDONE
1039 | PTRACE_O_TRACEEXEC
1040 | PTRACE_O_TRACEEXIT
1041 | PTRACE_O_EXITKILL)
1042 != 0)
1043 {
1044 track_stub!(TODO("https://fxbug.dev/322874463"), "ptrace(PTRACE_SETOPTIONS)", mask);
1045 return error!(ENOSYS);
1046 }
1047 if let Some(ptrace) = &mut state.ptrace {
1048 ptrace.set_options_from_bits(mask)?;
1049 }
1050 Ok(starnix_syscalls::SUCCESS)
1051 }
1052 PTRACE_GETEVENTMSG => {
1053 if let Some(ptrace) = &state.ptrace {
1054 if let Some(event_data) = &ptrace.event_data {
1055 let dst = LongPtr::new(current_task, data);
1056 current_task.write_multi_arch_object(dst, event_data.msg)?;
1057 return Ok(starnix_syscalls::SUCCESS);
1058 }
1059 }
1060 error!(EIO)
1061 }
1062 _ => {
1063 track_stub!(TODO("https://fxbug.dev/322874463"), "ptrace", request);
1064 error!(ENOSYS)
1065 }
1066 }
1067}
1068
1069fn do_attach(
1071 thread_group: &ThreadGroup,
1072 tracer_task: Weak<Task>,
1073 task: &Arc<Task>,
1074 attach_type: PtraceAttachType,
1075 options: PtraceOptions,
1076) -> Result<(), Errno> {
1077 thread_group.ptracees.lock().insert(task.get_tid(), task.into());
1078
1079 let process_state = &mut task.thread_group().write();
1080 let mut state = task.write();
1081 state.set_ptrace(Some(PtraceState::new(
1082 tracer_task,
1083 thread_group.weak_self.clone(),
1084 attach_type,
1085 options,
1086 )))?;
1087
1088 if process_state.is_waitable()
1091 && process_state.base.load_stopped() == StopState::GroupStopped
1092 && task.load_stopped() == StopState::GroupStopped
1093 {
1094 if let Some(ptrace) = &mut state.ptrace {
1095 ptrace.last_signal_waitable = true;
1096 }
1097 }
1098
1099 Ok(())
1100}
1101
1102pub fn ptrace_attach_from_state<L>(
1106 locked: &mut Locked<L>,
1107 tracee_task: &Arc<Task>,
1108 ptrace_state: PtraceCoreState,
1109) -> Result<(), Errno>
1110where
1111 L: LockBefore<ThreadGroupLimits>,
1112{
1113 {
1114 let tracer_tg = ptrace_state.thread_group.upgrade().ok_or_else(|| errno!(ESRCH))?;
1115 do_attach(
1116 &tracer_tg,
1117 ptrace_state.task.clone(),
1118 tracee_task,
1119 ptrace_state.attach_type,
1120 ptrace_state.options,
1121 )?;
1122 }
1123 let mut state = tracee_task.write();
1124 if let Some(ptrace) = &mut state.ptrace {
1125 ptrace.core_state.tracer_waiters = Arc::clone(&ptrace_state.tracer_waiters);
1126 }
1127
1128 let signal = if ptrace_state.attach_type == PtraceAttachType::Seize {
1130 if let Some(ptrace) = &mut state.ptrace {
1131 ptrace.set_last_event(Some(PtraceEventData::new_from_event(PtraceEvent::Stop, 0)));
1132 }
1133 SignalInfo::forced(SIGTRAP)
1135 } else {
1136 SignalInfo::forced(SIGSTOP)
1138 };
1139 send_signal_first(locked, tracee_task, state, signal);
1140
1141 ptrace_state.tracer_waiters.notify_all();
1145
1146 Ok(())
1147}
1148
1149pub fn ptrace_traceme(current_task: &mut CurrentTask) -> Result<SyscallResult, Errno> {
1150 let parent = current_task.thread_group().read().parent.clone();
1151 if let Some(parent) = parent {
1152 let parent = parent.upgrade();
1153 let parent_task = {
1155 let pids = current_task.kernel().pids.read();
1156 let parent_task = pids.get_task(parent.leader).map_err(|_| errno!(EINVAL))?;
1157 security::ptrace_traceme(current_task, &parent_task)?;
1158 Arc::downgrade(&parent_task)
1159 };
1160
1161 do_attach(
1162 &parent,
1163 parent_task,
1164 ¤t_task.task,
1165 PtraceAttachType::Attach,
1166 PtraceOptions::empty(),
1167 )?;
1168 Ok(starnix_syscalls::SUCCESS)
1169 } else {
1170 error!(EPERM)
1171 }
1172}
1173
1174pub fn ptrace_attach<L>(
1175 locked: &mut Locked<L>,
1176 current_task: &mut CurrentTask,
1177 pid: pid_t,
1178 attach_type: PtraceAttachType,
1179 data: UserAddress,
1180) -> Result<SyscallResult, Errno>
1181where
1182 L: LockBefore<MmDumpable>,
1183{
1184 let tracee = current_task.kernel().pids.read().get_task(pid)?;
1185
1186 if tracee.thread_group == current_task.thread_group {
1187 return error!(EPERM);
1188 }
1189
1190 current_task.check_ptrace_access_mode(locked, PTRACE_MODE_ATTACH_REALCREDS, &tracee)?;
1191 let tracer_task = Arc::downgrade(¤t_task.task);
1192 do_attach(
1193 current_task.thread_group(),
1194 tracer_task,
1195 &tracee,
1196 attach_type,
1197 PtraceOptions::empty(),
1198 )?;
1199 if attach_type == PtraceAttachType::Attach {
1200 send_standard_signal(
1201 locked.cast_locked::<MmDumpable>(),
1202 &tracee,
1203 SignalInfo::kernel(SIGSTOP),
1204 );
1205 } else if attach_type == PtraceAttachType::Seize {
1206 let mut state = tracee.write();
1208 if let Some(ptrace) = &mut state.ptrace {
1209 ptrace.set_options_from_bits(data.ptr() as u32)?;
1210 }
1211 }
1212 Ok(starnix_syscalls::SUCCESS)
1213}
1214
1215pub fn ptrace_peekuser(
1219 thread_state: &mut ThreadState<HeapRegs>,
1220 offset: usize,
1221) -> Result<usize, Errno> {
1222 #[cfg(any(target_arch = "x86_64"))]
1223 if offset >= std::mem::size_of::<user>() {
1224 return error!(EIO);
1225 }
1226 if offset < UserRegsStructPtr::size_of_object_for(thread_state) {
1227 let result = thread_state.get_user_register(offset)?;
1228 return Ok(result);
1229 }
1230 error!(EIO)
1231}
1232
1233pub fn ptrace_pokeuser(
1234 state: &mut TaskMutableState,
1235 value: usize,
1236 offset: usize,
1237) -> Result<(), Errno> {
1238 if let Some(ref mut thread_state) = state.captured_thread_state {
1239 thread_state.dirty = true;
1240
1241 #[cfg(any(target_arch = "x86_64"))]
1242 if offset >= std::mem::size_of::<user>() {
1243 return error!(EIO);
1244 }
1245 if offset < UserRegsStructPtr::size_of_object_for(thread_state.as_ref()) {
1246 return thread_state.thread_state.set_user_register(offset, value);
1247 }
1248 }
1249 error!(EIO)
1250}
1251
1252pub fn ptrace_getregset(
1253 current_task: &CurrentTask,
1254 thread_state: &ThreadState<HeapRegs>,
1255 regset_type: ElfNoteType,
1256 base: u64,
1257 len: &mut usize,
1258) -> Result<(), Errno> {
1259 match regset_type {
1260 ElfNoteType::PrStatus => {
1261 let user_regs_struct_len = UserRegsStructPtr::size_of_object_for(thread_state);
1262 *len = std::cmp::min(*len, user_regs_struct_len);
1263
1264 if thread_state.is_arch32() {
1265 let regs = thread_state.registers.to_user_regs_struct_arch32();
1266 current_task.write_memory(UserAddress::from(base), ®s.as_bytes()[..*len])?;
1267 } else {
1268 let regs = thread_state.registers.to_user_regs_struct();
1269 current_task.write_memory(UserAddress::from(base), ®s.as_bytes()[..*len])?;
1270 }
1271 Ok(())
1272 }
1273 _ => {
1274 error!(EINVAL)
1275 }
1276 }
1277}
1278
1279pub fn ptrace_setregset(
1280 current_task: &CurrentTask,
1281 thread_state: &mut ThreadState<HeapRegs>,
1282 regset_type: ElfNoteType,
1283 base: u64,
1284 len: usize,
1285) -> Result<(), Errno> {
1286 match regset_type {
1287 ElfNoteType::PrStatus => {
1288 let user_regs_struct_len = UserRegsStructPtr::size_of_object_for(thread_state);
1289 if len < user_regs_struct_len {
1290 return error!(EINVAL);
1291 }
1292
1293 if thread_state.is_arch32() {
1294 let mut regs = starnix_uapi::arch32::user_regs_struct::default();
1295 current_task.read_memory_to_slice(UserAddress::from(base), regs.as_mut_bytes())?;
1296 thread_state.registers.from_user_regs_struct_arch32(®s);
1297 } else {
1298 let mut regs = starnix_uapi::user_regs_struct::default();
1299 current_task.read_memory_to_slice(UserAddress::from(base), regs.as_mut_bytes())?;
1300 thread_state.registers.from_user_regs_struct(®s);
1301 }
1302 Ok(())
1303 }
1304 _ => error!(EINVAL),
1305 }
1306}
1307
1308#[inline(never)]
1309pub fn ptrace_syscall_enter(locked: &mut Locked<Unlocked>, current_task: &mut CurrentTask) {
1310 let block = {
1311 let mut state = current_task.write();
1312 if state.ptrace.is_some() {
1313 current_task.trace_syscalls.store(false, Ordering::Relaxed);
1314 let mut sig = SignalInfo::with_detail(
1315 SIGTRAP,
1316 (linux_uapi::SIGTRAP | 0x80) as i32,
1317 SignalDetail::None,
1318 );
1319 if state
1320 .ptrace
1321 .as_ref()
1322 .is_some_and(|ptrace| ptrace.has_option(PtraceOptions::TRACESYSGOOD))
1323 {
1324 sig.signal.set_ptrace_syscall_bit();
1325 }
1326 state.set_stopped(StopState::SyscallEnterStopping, Some(sig), None, None);
1327 true
1328 } else {
1329 false
1330 }
1331 };
1332 if block {
1333 current_task.block_if_stopped(locked);
1334 }
1335}
1336
1337#[inline(never)]
1338pub fn ptrace_syscall_exit(
1339 locked: &mut Locked<Unlocked>,
1340 current_task: &mut CurrentTask,
1341 is_error: bool,
1342) {
1343 let block = {
1344 let mut state = current_task.write();
1345 current_task.trace_syscalls.store(false, Ordering::Relaxed);
1346 if state.ptrace.is_some() {
1347 let mut sig = SignalInfo::with_detail(
1348 SIGTRAP,
1349 (linux_uapi::SIGTRAP | 0x80) as i32,
1350 SignalDetail::None,
1351 );
1352 if state
1353 .ptrace
1354 .as_ref()
1355 .is_some_and(|ptrace| ptrace.has_option(PtraceOptions::TRACESYSGOOD))
1356 {
1357 sig.signal.set_ptrace_syscall_bit();
1358 }
1359
1360 state.set_stopped(StopState::SyscallExitStopping, Some(sig), None, None);
1361 if let Some(ptrace) = &mut state.ptrace {
1362 ptrace.last_syscall_was_error = is_error;
1363 }
1364 true
1365 } else {
1366 false
1367 }
1368 };
1369 if block {
1370 current_task.block_if_stopped(locked);
1371 }
1372}
1373
1374#[cfg(test)]
1375mod tests {
1376 use super::*;
1377 use crate::task::syscalls::sys_prctl;
1378 use crate::testing::{create_task, spawn_kernel_and_run};
1379 use starnix_uapi::PR_SET_PTRACER;
1380 use starnix_uapi::auth::CAP_SYS_PTRACE;
1381
1382 #[::fuchsia::test]
1383 async fn test_set_ptracer() {
1384 spawn_kernel_and_run(async |locked, current_task| {
1385 let kernel = current_task.kernel().clone();
1386 let mut tracee = create_task(locked, &kernel, "tracee");
1387 let mut tracer = create_task(locked, &kernel, "tracer");
1388
1389 let mut creds = tracer.real_creds().clone();
1390 creds.cap_effective &= !CAP_SYS_PTRACE;
1391 tracer.set_creds(creds);
1392
1393 kernel.ptrace_scope.store(security::yama::SCOPE_RESTRICTED, Ordering::Relaxed);
1394 assert_eq!(
1395 sys_prctl(locked, &mut tracee, PR_SET_PTRACER, 0xFFF, 0, 0, 0),
1396 error!(EINVAL)
1397 );
1398
1399 assert_eq!(
1400 ptrace_attach(
1401 locked,
1402 &mut tracer,
1403 tracee.as_ref().task.tid,
1404 PtraceAttachType::Attach,
1405 UserAddress::NULL,
1406 ),
1407 error!(EPERM)
1408 );
1409
1410 assert!(
1411 sys_prctl(
1412 locked,
1413 &mut tracee,
1414 PR_SET_PTRACER,
1415 tracer.thread_group().leader as u64,
1416 0,
1417 0,
1418 0
1419 )
1420 .is_ok()
1421 );
1422
1423 let mut not_tracer = create_task(locked, &kernel, "not-tracer");
1424 not_tracer.set_creds(tracer.real_creds().clone());
1425 assert_eq!(
1426 ptrace_attach(
1427 locked,
1428 &mut not_tracer,
1429 tracee.as_ref().task.tid,
1430 PtraceAttachType::Attach,
1431 UserAddress::NULL,
1432 ),
1433 error!(EPERM)
1434 );
1435
1436 assert!(
1437 ptrace_attach(
1438 locked,
1439 &mut tracer,
1440 tracee.as_ref().task.tid,
1441 PtraceAttachType::Attach,
1442 UserAddress::NULL,
1443 )
1444 .is_ok()
1445 );
1446 })
1447 .await;
1448 }
1449
1450 #[::fuchsia::test]
1451 async fn test_set_ptracer_any() {
1452 spawn_kernel_and_run(async |locked, current_task| {
1453 let kernel = current_task.kernel().clone();
1454 let mut tracee = create_task(locked, &kernel, "tracee");
1455 let mut tracer = create_task(locked, &kernel, "tracer");
1456
1457 let mut creds = tracer.real_creds().clone();
1458 creds.cap_effective &= !CAP_SYS_PTRACE;
1459 tracer.set_creds(creds);
1460
1461 kernel.ptrace_scope.store(security::yama::SCOPE_RESTRICTED, Ordering::Relaxed);
1462 assert_eq!(
1463 sys_prctl(locked, &mut tracee, PR_SET_PTRACER, 0xFFF, 0, 0, 0),
1464 error!(EINVAL)
1465 );
1466
1467 assert_eq!(
1468 ptrace_attach(
1469 locked,
1470 &mut tracer,
1471 tracee.as_ref().task.tid,
1472 PtraceAttachType::Attach,
1473 UserAddress::NULL,
1474 ),
1475 error!(EPERM)
1476 );
1477
1478 assert!(
1479 sys_prctl(locked, &mut tracee, PR_SET_PTRACER, PR_SET_PTRACER_ANY as u64, 0, 0, 0)
1480 .is_ok()
1481 );
1482
1483 assert!(
1484 ptrace_attach(
1485 locked,
1486 &mut tracer,
1487 tracee.as_ref().task.tid,
1488 PtraceAttachType::Attach,
1489 UserAddress::NULL,
1490 )
1491 .is_ok()
1492 );
1493 })
1494 .await;
1495 }
1496}