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