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