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::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 PidTableGuard<'_>) -> Option<ZombieNotification> {
498 self.artificial_zombie.release(pids);
499 self.notification
500 }
501}
502
503#[derive(Default)]
507pub struct ZombiePtracees {
508 zombies: BTreeMap<Pid, 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 PidTableGuard<'_>, tid: Pid, 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(
538 &mut self,
539 pids: &mut PidTableGuard<'_>,
540 tid: &Pid,
541 ) -> Option<ZombieNotification> {
542 self.zombies.remove(tid).and_then(|traced_zombie| traced_zombie.detach(pids))
543 }
544
545 pub fn detach_all(&mut self, pids: &mut PidTableGuard<'_>) -> Vec<ZombieNotification> {
549 let traced_zombies = std::mem::replace(&mut self.zombies, Default::default());
550 traced_zombies
551 .into_iter()
552 .filter_map(|(_, traced_zombie)| traced_zombie.detach(pids))
553 .collect()
554 }
555
556 pub fn is_empty(&self) -> bool {
557 self.zombies.is_empty()
558 }
559
560 pub fn set_parent_of(
563 &mut self,
564 tracee: &Pid,
565 new_zombie: Option<OwnedRef<ZombieProcess>>,
566 new_parent: &ThreadGroup,
567 ) {
568 match self.zombies.entry(tracee.clone()) {
569 std::collections::btree_map::Entry::Vacant(entry) => {
570 if let Some(new_zombie) = new_zombie {
571 entry.insert(TracedZombie::new_with_notification(
572 new_zombie.as_artificial(),
573 ZombieNotification::new(new_parent.weak_self.clone(), new_zombie),
574 ));
575 }
576 }
577 std::collections::btree_map::Entry::Occupied(mut entry) => {
578 entry.get_mut().set_parent(new_zombie, new_parent);
579 }
580 }
581 }
582
583 pub fn reparent(old_parent: &ThreadGroup, new_parent: &ThreadGroup) {
586 let mut lockless_list = old_parent.read().deferred_zombie_ptracers.clone();
587
588 for deferred_zombie_ptracer in &lockless_list {
589 if let Ok(tg) = deferred_zombie_ptracer.tracer_pid.get_thread_group() {
590 tg.write().zombie_ptracees.set_parent_of(
591 &deferred_zombie_ptracer.tracee_tid,
592 None,
593 new_parent,
594 );
595 }
596 }
597 let mut new_state = new_parent.write();
598 new_state.deferred_zombie_ptracers.append(&mut lockless_list);
599 }
600
601 pub fn has_zombie_matching(&self, selector: &ProcessSelector) -> bool {
604 self.zombies.values().any(|z| z.artificial_zombie.matches_selector(selector))
605 }
606
607 pub fn has_tracee(&self, tid: &Pid) -> bool {
610 self.zombies.contains_key(tid)
611 }
612
613 pub fn get_waitable_entry(
617 &mut self,
618 selector: &ProcessSelector,
619 options: &WaitingOptions,
620 ) -> Option<(ZombieProcess, Option<(Weak<ThreadGroup>, OwnedRef<ZombieProcess>)>)> {
621 let Some((t, found_zombie)) = self
624 .zombies
625 .iter()
626 .map(|(t, z)| (t.clone(), &z.artificial_zombie))
627 .rfind(|(_, zombie)| zombie.matches_selector_and_waiting_option(selector, options))
628 else {
629 return None;
630 };
631
632 let result;
633 if !options.keep_waitable_state {
634 result = self.zombies.remove(&t).map(|traced_zombie| {
636 (
637 traced_zombie.artificial_zombie,
638 traced_zombie.notification.map(|n| (n.recipient, n.zombie)),
639 )
640 });
641 } else {
642 result = Some((found_zombie.as_artificial(), None));
643 }
644
645 result
646 }
647}
648
649pub const PR_SET_PTRACER_ANY: u64 = u64::MAX;
651pub const PR_SET_PTRACER_ANY_ARCH32: u64 = u32::MAX as u64;
652
653#[derive(Clone, Debug, Default, PartialEq, Eq)]
656pub enum PtraceAllowedPtracers {
657 #[default]
658 None,
659 Some(Pid),
660 Any,
661}
662
663#[derive(Copy, Clone)]
664pub enum PtraceTracer<'a> {
665 Exiting(&'a ThreadGroup),
667 Syscall(&'a Arc<Task>),
669}
670
671impl<'a> PtraceTracer<'a> {
672 fn thread_group(self) -> &'a ThreadGroup {
673 match self {
674 PtraceTracer::Exiting(tg) => tg,
675 PtraceTracer::Syscall(task) => &task.thread_group,
676 }
677 }
678
679 fn matches_task(self, task: &Arc<Task>) -> bool {
680 match self {
681 PtraceTracer::Syscall(expected_task) => task == expected_task,
682 PtraceTracer::Exiting(expected_tg) => &*task.thread_group == expected_tg,
683 }
684 }
685}
686
687fn ptrace_cont(
695 tracer: PtraceTracer<'_>,
696 tracee: &Task,
697 data: &UserAddress,
698 detach: bool,
699) -> Result<(), Errno> {
700 let data = data.ptr() as u64;
701 let new_state;
702 let mut siginfo = if data != 0 {
703 let signal = Signal::try_from(UncheckedSignal::new(data))?;
704 Some(SignalInfo::kernel(signal))
705 } else {
706 None
707 };
708
709 let mut state = tracee.write();
710
711 {
718 let ptrace = state.ptrace.as_ref().ok_or_else(|| errno!(ESRCH))?;
719 let tracer_tg = ptrace.core_state.thread_group.upgrade().ok_or_else(|| errno!(ESRCH))?;
720 let tracer_task = ptrace.core_state.task.upgrade();
721 let is_match = match tracer_task {
722 Some(tracer_task) if !tracer_task.is_exitted() => tracer.matches_task(&tracer_task),
723 _ => tracer.thread_group() == &*tracer_tg,
724 };
725 if !is_match {
726 return error!(ESRCH);
727 }
728 }
729 let is_listen = state.is_ptrace_listening();
730
731 if tracee.load_stopped().is_waking_or_awake() && !is_listen {
732 if detach && matches!(tracer, PtraceTracer::Exiting(_)) {
733 state.set_ptrace(None)?;
737 return Ok(());
738 }
739 return error!(EIO);
742 }
743
744 if !state.can_accept_ptrace_commands() && !detach {
745 return error!(ESRCH);
746 }
747
748 let signal_mask = state.signal_mask();
749 if let Some(ptrace) = &mut state.ptrace {
750 if data != 0 {
751 if let Some(last_signal) = &mut ptrace.last_signal {
752 if let Some(si) = &siginfo {
757 if last_signal.signal == si.signal {
758 siginfo = Some(last_signal.clone());
759 } else {
760 *last_signal = si.clone();
761 }
762 }
763 }
764 new_state = if siginfo
765 .as_ref()
766 .is_some_and(|si| !signal_mask.has_signal(si.signal) || si.force)
767 {
768 PtraceStatus::Continuing
769 } else {
770 PtraceStatus::Default
771 };
772 } else {
773 new_state = PtraceStatus::Default;
774 ptrace.last_signal = None;
775 ptrace.event_data = None;
776 }
777 ptrace.stop_status = new_state;
778
779 if is_listen {
780 state.notify_ptracees();
781 }
782 }
783
784 if let Some(siginfo) = siginfo {
785 send_signal_first(&tracee, state, siginfo);
787 } else {
788 state.set_stopped(StopState::Waking, None, None, None);
789 drop(state);
790 tracee.thread_group().set_stopped(StopState::Waking, None, false);
791 }
792 if detach {
793 tracee.write().set_ptrace(None)?;
794 }
795 Ok(())
796}
797
798fn ptrace_interrupt(tracee: &Task) -> Result<(), Errno> {
799 let mut state = tracee.write();
800 if let Some(ptrace) = &mut state.ptrace {
801 if !ptrace.is_seized() {
802 return error!(EIO);
803 }
804 let status = ptrace.stop_status.clone();
805 ptrace.stop_status = PtraceStatus::Default;
806 let event_data = Some(PtraceEventData::new_from_event(PtraceEvent::Stop, 0));
807 if status == PtraceStatus::Listening {
808 let signal = ptrace.last_signal.clone();
809 state.set_stopped(StopState::PtraceEventStopped, signal, None, event_data);
813 } else {
814 state.set_stopped(
815 StopState::PtraceEventStopping,
816 Some(SignalInfo::kernel(SIGTRAP)),
817 None,
818 event_data,
819 );
820 drop(state);
821 tracee.interrupt();
822 }
823 }
824 Ok(())
825}
826
827fn ptrace_listen(tracee: &Task) -> Result<(), Errno> {
828 let mut state = tracee.write();
829 if let Some(ptrace) = &mut state.ptrace {
830 if !ptrace.is_seized()
831 || (ptrace.last_signal_waitable
832 && ptrace
833 .event_data
834 .as_ref()
835 .is_some_and(|event_data| event_data.event != PtraceEvent::Stop))
836 {
837 return error!(EIO);
838 }
839 ptrace.stop_status = PtraceStatus::Listening;
840 }
841 Ok(())
842}
843
844pub fn ptrace_detach(
845 pids: &mut PidTableGuard<'_>,
846 tracer: PtraceTracer<'_>,
847 tracee: &Task,
848 data: &UserAddress,
849) -> Result<(), Errno> {
850 let thread_group = tracer.thread_group();
851 {
852 let mut ptracees = thread_group.ptracees.lock();
853 ptrace_cont(tracer, tracee, &data, true)?;
854 ptracees.remove(&tracee.persistent_info);
855 }
856 let zombie_notification = thread_group.write().zombie_ptracees.detach(pids, &tracee.tid);
857 if let Some(zombie_notification) = zombie_notification {
858 zombie_notification.deliver(pids);
859 }
860 Ok(())
861}
862
863pub fn ptrace_dispatch(
865 current_task: &mut CurrentTask,
866 request: u32,
867 pid: &Pid,
868 addr: UserAddress,
869 data: UserAddress,
870) -> Result<SyscallResult, Errno> {
871 let tracee = pid.get_task()?;
872
873 if let Some(ptrace) = &tracee.read().ptrace {
874 let is_tracer = ptrace
875 .core_state
876 .thread_group
877 .upgrade()
878 .is_some_and(|tg| Arc::ptr_eq(&tg, current_task.thread_group()));
879 if !is_tracer {
880 return error!(ESRCH);
881 }
882 }
883
884 match request {
887 PTRACE_KILL => {
888 let siginfo = SignalInfo::with_detail(
889 SIGKILL,
890 (SIGTRAP.number() | PTRACE_KILL << 8) as i32,
891 SignalDetail::None,
892 );
893 send_standard_signal(&tracee, siginfo);
894 return Ok(starnix_syscalls::SUCCESS);
895 }
896 PTRACE_INTERRUPT => {
897 ptrace_interrupt(tracee.as_ref())?;
898 return Ok(starnix_syscalls::SUCCESS);
899 }
900 PTRACE_LISTEN => {
901 ptrace_listen(&tracee)?;
902 return Ok(starnix_syscalls::SUCCESS);
903 }
904 PTRACE_CONT => {
905 ptrace_cont(PtraceTracer::Syscall(¤t_task.task), tracee.as_ref(), &data, false)?;
906 return Ok(starnix_syscalls::SUCCESS);
907 }
908 PTRACE_SYSCALL => {
909 tracee.trace_syscalls.store(true, std::sync::atomic::Ordering::Relaxed);
910 ptrace_cont(PtraceTracer::Syscall(¤t_task.task), tracee.as_ref(), &data, false)?;
911 return Ok(starnix_syscalls::SUCCESS);
912 }
913 PTRACE_DETACH => {
914 let mut pids = current_task.kernel().pids.lock();
915 ptrace_detach(
916 &mut pids,
917 PtraceTracer::Syscall(¤t_task.task),
918 tracee.as_ref(),
919 &data,
920 )?;
921 return Ok(starnix_syscalls::SUCCESS);
922 }
923 _ => {}
924 }
925
926 let mut state = tracee.write();
928 if !state.can_accept_ptrace_commands() {
929 return error!(ESRCH);
930 }
931
932 match request {
933 PTRACE_PEEKDATA | PTRACE_PEEKTEXT => {
934 let Some(captured) = &mut state.captured_thread_state else {
935 return error!(ESRCH);
936 };
937
938 let src = LongPtr::new(captured.as_ref(), addr);
941 let val = tracee.read_multi_arch_object(src)?;
942
943 let dst = LongPtr::new(&src, data);
944 current_task.write_multi_arch_object(dst, val)?;
945 Ok(starnix_syscalls::SUCCESS)
946 }
947 PTRACE_POKEDATA | PTRACE_POKETEXT => {
948 let Some(captured) = &mut state.captured_thread_state else {
949 return error!(ESRCH);
950 };
951
952 let bytes = if captured.is_arch32() {
953 u32::try_from(data.ptr()).map_err(|_| errno!(EINVAL))?.to_ne_bytes().to_vec()
954 } else {
955 data.ptr().to_ne_bytes().to_vec()
956 };
957
958 tracee.mm()?.force_write_memory(addr, &bytes)?;
959
960 Ok(starnix_syscalls::SUCCESS)
961 }
962 PTRACE_PEEKUSR => {
963 let Some(captured) = &mut state.captured_thread_state else {
964 return error!(ESRCH);
965 };
966
967 let dst = LongPtr::new(captured.as_ref(), data);
968 let val = ptrace_peekuser(&mut captured.thread_state, addr.ptr() as usize)?;
969 current_task.write_multi_arch_object(dst, val as u64)?;
970 return Ok(starnix_syscalls::SUCCESS);
971 }
972 PTRACE_POKEUSR => {
973 ptrace_pokeuser(&mut *state, data.ptr() as usize, addr.ptr() as usize)?;
974 return Ok(starnix_syscalls::SUCCESS);
975 }
976 PTRACE_GETREGSET => {
977 if let Some(ref mut captured) = state.captured_thread_state {
978 let uiv = IOVecPtr::new(current_task, data);
979 let mut iv = current_task.read_multi_arch_object(uiv)?;
980 let base = iv.iov_base.addr;
981 let mut len = iv.iov_len as usize;
982 ptrace_getregset(
983 current_task,
984 &captured.thread_state,
985 ElfNoteType::try_from(addr.ptr() as usize)?,
986 base,
987 &mut len,
988 )?;
989 iv.iov_len = len as u64;
990 current_task.write_multi_arch_object(uiv, iv)?;
991 return Ok(starnix_syscalls::SUCCESS);
992 }
993 error!(ESRCH)
994 }
995 PTRACE_SETREGSET => {
996 if let Some(ref mut captured) = state.captured_thread_state {
997 captured.dirty = true;
998 let uiv = IOVecPtr::new(current_task, data);
999 let iv = current_task.read_multi_arch_object(uiv)?;
1000 let base = iv.iov_base.addr;
1001 let len = iv.iov_len as usize;
1002 ptrace_setregset(
1003 current_task,
1004 &mut captured.thread_state,
1005 ElfNoteType::try_from(addr.ptr() as usize)?,
1006 base,
1007 len,
1008 )?;
1009 return Ok(starnix_syscalls::SUCCESS);
1010 }
1011 error!(ESRCH)
1012 }
1013 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
1014 PTRACE_GETREGS => {
1015 if let Some(captured) = &mut state.captured_thread_state {
1016 let mut len = usize::MAX;
1017 ptrace_getregset(
1018 current_task,
1019 &captured.thread_state,
1020 ElfNoteType::PrStatus,
1021 data.ptr() as u64,
1022 &mut len,
1023 )?;
1024 return Ok(starnix_syscalls::SUCCESS);
1025 }
1026 error!(ESRCH)
1027 }
1028 PTRACE_SETSIGMASK => {
1029 if addr.ptr() != std::mem::size_of::<SigSet>() {
1032 return error!(EINVAL);
1033 }
1034 let src: UserRef<SigSet> = UserRef::from(data);
1036 let val = current_task.read_object(src)?;
1037 state.set_signal_mask(val);
1038
1039 Ok(starnix_syscalls::SUCCESS)
1040 }
1041 PTRACE_GETSIGMASK => {
1042 if addr.ptr() != std::mem::size_of::<SigSet>() {
1045 return error!(EINVAL);
1046 }
1047 let dst: UserRef<SigSet> = UserRef::from(data);
1049 let val = state.signal_mask();
1050 current_task.write_object(dst, &val)?;
1051 Ok(starnix_syscalls::SUCCESS)
1052 }
1053 PTRACE_GETSIGINFO => {
1054 if let Some(ptrace) = &state.ptrace {
1055 if let Some(signal) = ptrace.last_signal.as_ref() {
1056 let dst = MultiArchUserRef::<uapi::siginfo_t, uapi::arch32::siginfo_t>::new(
1057 current_task,
1058 data,
1059 );
1060 signal.write(current_task, dst)?;
1061 } else {
1062 return error!(EINVAL);
1063 }
1064 }
1065 Ok(starnix_syscalls::SUCCESS)
1066 }
1067 PTRACE_SETSIGINFO => {
1068 let mut siginfo: SignalInfo =
1069 UncheckedSignalInfo::read_from_siginfo(current_task, data)?.try_into()?;
1070 if let Some(ptrace) = &mut state.ptrace {
1071 if let Some(last_signal) = &ptrace.last_signal {
1072 if last_signal.signal == siginfo.signal {
1073 siginfo.force = last_signal.force;
1074 }
1075 }
1076 ptrace.last_signal = Some(siginfo);
1077 }
1078 Ok(starnix_syscalls::SUCCESS)
1079 }
1080 PTRACE_GET_SYSCALL_INFO => {
1081 if let Some(ptrace) = &state.ptrace {
1082 let (size, info) = ptrace.get_target_syscall(&tracee, &state)?;
1083 let dst: UserRef<ptrace_syscall_info> = UserRef::from(data);
1084 let len = std::cmp::min(std::mem::size_of::<ptrace_syscall_info>(), addr.ptr());
1085 let src = unsafe {
1088 std::slice::from_raw_parts(
1089 &info as *const ptrace_syscall_info as *const u8,
1090 len as usize,
1091 )
1092 };
1093 current_task.write_memory(dst.addr(), src)?;
1094 Ok(size.into())
1095 } else {
1096 error!(ESRCH)
1097 }
1098 }
1099 PTRACE_SETOPTIONS => {
1100 let mask = data.ptr() as u32;
1101 if mask != 0
1103 && (mask
1104 & !(PTRACE_O_TRACESYSGOOD
1105 | PTRACE_O_TRACECLONE
1106 | PTRACE_O_TRACEFORK
1107 | PTRACE_O_TRACEVFORK
1108 | PTRACE_O_TRACEVFORKDONE
1109 | PTRACE_O_TRACEEXEC
1110 | PTRACE_O_TRACEEXIT
1111 | PTRACE_O_EXITKILL)
1112 != 0)
1113 {
1114 track_stub!(TODO("https://fxbug.dev/322874463"), "ptrace(PTRACE_SETOPTIONS)", mask);
1115 return error!(ENOSYS);
1116 }
1117 if let Some(ptrace) = &mut state.ptrace {
1118 ptrace.set_options_from_bits(mask)?;
1119 }
1120 Ok(starnix_syscalls::SUCCESS)
1121 }
1122 PTRACE_GETEVENTMSG => {
1123 if let Some(ptrace) = &state.ptrace {
1124 if let Some(event_data) = &ptrace.event_data {
1125 let dst = LongPtr::new(current_task, data);
1126 current_task.write_multi_arch_object(dst, event_data.msg)?;
1127 return Ok(starnix_syscalls::SUCCESS);
1128 }
1129 }
1130 error!(EIO)
1131 }
1132 _ => {
1133 track_stub!(TODO("https://fxbug.dev/322874463"), "ptrace", request);
1134 error!(ENOSYS)
1135 }
1136 }
1137}
1138
1139fn do_attach(
1141 thread_group: &ThreadGroup,
1142 tracer_task: Weak<Task>,
1143 task: &Arc<Task>,
1144 attach_type: PtraceAttachType,
1145 options: PtraceOptions,
1146) -> Result<(), Errno> {
1147 let mut ptracees = thread_group.ptracees.lock();
1148
1149 if !thread_group.read().is_running() {
1150 return error!(ESRCH);
1151 }
1152
1153 let process_state = &mut task.thread_group().write();
1154 let mut state = task.write();
1155 state.set_ptrace(Some(PtraceState::new(
1156 tracer_task,
1157 thread_group.weak_self.clone(),
1158 attach_type,
1159 options,
1160 )))?;
1161
1162 ptracees.insert(task.persistent_info.clone());
1163
1164 if process_state.is_waitable()
1167 && process_state.base.load_stopped() == StopState::GroupStopped
1168 && task.load_stopped() == StopState::GroupStopped
1169 {
1170 if let Some(ptrace) = &mut state.ptrace {
1171 ptrace.last_signal_waitable = true;
1172 }
1173 }
1174
1175 Ok(())
1176}
1177
1178pub fn ptrace_attach_from_state(
1182 tracee_task: &Arc<Task>,
1183 ptrace_state: PtraceCoreState,
1184) -> Result<(), Errno> {
1185 {
1186 let tracer_tg = ptrace_state.thread_group.upgrade().ok_or_else(|| errno!(ESRCH))?;
1187 do_attach(
1188 &tracer_tg,
1189 ptrace_state.task.clone(),
1190 tracee_task,
1191 ptrace_state.attach_type,
1192 ptrace_state.options,
1193 )?;
1194 }
1195 let mut state = tracee_task.write();
1196 if let Some(ptrace) = &mut state.ptrace {
1197 ptrace.core_state.tracer_waiters = Arc::clone(&ptrace_state.tracer_waiters);
1198 }
1199
1200 let signal = if ptrace_state.attach_type == PtraceAttachType::Seize {
1202 if let Some(ptrace) = &mut state.ptrace {
1203 ptrace.set_last_event(Some(PtraceEventData::new_from_event(PtraceEvent::Stop, 0)));
1204 }
1205 SignalInfo::forced(SIGTRAP)
1207 } else {
1208 SignalInfo::forced(SIGSTOP)
1210 };
1211 send_signal_first(tracee_task, state, signal);
1212
1213 ptrace_state.tracer_waiters.notify_all();
1217
1218 Ok(())
1219}
1220
1221pub fn ptrace_traceme(current_task: &mut CurrentTask) -> Result<SyscallResult, Errno> {
1222 let parent = current_task.thread_group().read().parent.clone();
1223 if let Some(parent) = parent {
1224 let parent = parent.upgrade();
1225 let parent_task = parent.leader.get_task().map_err(|_| errno!(EINVAL))?;
1227 security::ptrace_traceme(current_task, &parent_task)?;
1228 let parent_task = Arc::downgrade(&parent_task);
1229
1230 do_attach(
1231 &parent,
1232 parent_task,
1233 ¤t_task.task,
1234 PtraceAttachType::Attach,
1235 PtraceOptions::empty(),
1236 )?;
1237 Ok(starnix_syscalls::SUCCESS)
1238 } else {
1239 error!(EPERM)
1240 }
1241}
1242
1243pub fn ptrace_attach(
1244 current_task: &mut CurrentTask,
1245 pid: &Pid,
1246 attach_type: PtraceAttachType,
1247 data: UserAddress,
1248) -> Result<SyscallResult, Errno> {
1249 let tracee = pid.get_task()?;
1250
1251 if tracee.thread_group == current_task.thread_group {
1252 return error!(EPERM);
1253 }
1254
1255 current_task.check_ptrace_access_mode(PTRACE_MODE_ATTACH_REALCREDS, &tracee)?;
1256 let tracer_task = Arc::downgrade(¤t_task.task);
1257 do_attach(
1258 current_task.thread_group(),
1259 tracer_task,
1260 &tracee,
1261 attach_type,
1262 PtraceOptions::empty(),
1263 )?;
1264 if attach_type == PtraceAttachType::Attach {
1265 send_standard_signal(&tracee, SignalInfo::kernel(SIGSTOP));
1266 } else if attach_type == PtraceAttachType::Seize {
1267 let mut state = tracee.write();
1269 if let Some(ptrace) = &mut state.ptrace {
1270 ptrace.set_options_from_bits(data.ptr() as u32)?;
1271 }
1272 }
1273 Ok(starnix_syscalls::SUCCESS)
1274}
1275
1276pub fn ptrace_peekuser(
1280 thread_state: &mut ThreadState<HeapRegs>,
1281 offset: usize,
1282) -> Result<usize, Errno> {
1283 #[cfg(any(target_arch = "x86_64"))]
1284 if offset >= std::mem::size_of::<user>() {
1285 return error!(EIO);
1286 }
1287 if offset < UserRegsStructPtr::size_of_object_for(thread_state) {
1288 let result = thread_state.get_user_register(offset)?;
1289 return Ok(result);
1290 }
1291 error!(EIO)
1292}
1293
1294pub fn ptrace_pokeuser(
1295 state: &mut TaskMutableState,
1296 value: usize,
1297 offset: usize,
1298) -> Result<(), Errno> {
1299 if let Some(ref mut thread_state) = state.captured_thread_state {
1300 thread_state.dirty = true;
1301
1302 #[cfg(any(target_arch = "x86_64"))]
1303 if offset >= std::mem::size_of::<user>() {
1304 return error!(EIO);
1305 }
1306 if offset < UserRegsStructPtr::size_of_object_for(thread_state.as_ref()) {
1307 return thread_state.thread_state.set_user_register(offset, value);
1308 }
1309 }
1310 error!(EIO)
1311}
1312
1313pub fn ptrace_getregset(
1314 current_task: &CurrentTask,
1315 thread_state: &ThreadState<HeapRegs>,
1316 regset_type: ElfNoteType,
1317 base: u64,
1318 len: &mut usize,
1319) -> Result<(), Errno> {
1320 match regset_type {
1321 ElfNoteType::PrStatus => {
1322 let user_regs_struct_len = UserRegsStructPtr::size_of_object_for(thread_state);
1323 *len = std::cmp::min(*len, user_regs_struct_len);
1324
1325 if thread_state.is_arch32() {
1326 let regs = thread_state.registers.to_user_regs_struct_arch32();
1327 current_task.write_memory(UserAddress::from(base), ®s.as_bytes()[..*len])?;
1328 } else {
1329 let regs = thread_state.registers.to_user_regs_struct();
1330 current_task.write_memory(UserAddress::from(base), ®s.as_bytes()[..*len])?;
1331 }
1332 Ok(())
1333 }
1334 _ => {
1335 error!(EINVAL)
1336 }
1337 }
1338}
1339
1340pub fn ptrace_setregset(
1341 current_task: &CurrentTask,
1342 thread_state: &mut ThreadState<HeapRegs>,
1343 regset_type: ElfNoteType,
1344 base: u64,
1345 len: usize,
1346) -> Result<(), Errno> {
1347 match regset_type {
1348 ElfNoteType::PrStatus => {
1349 let user_regs_struct_len = UserRegsStructPtr::size_of_object_for(thread_state);
1350 if len < user_regs_struct_len {
1351 return error!(EINVAL);
1352 }
1353
1354 if thread_state.is_arch32() {
1355 let mut regs = starnix_uapi::arch32::user_regs_struct::default();
1356 current_task.read_memory_to_slice(UserAddress::from(base), regs.as_mut_bytes())?;
1357 thread_state.registers.from_user_regs_struct_arch32(®s);
1358 } else {
1359 let mut regs = starnix_uapi::user_regs_struct::default();
1360 current_task.read_memory_to_slice(UserAddress::from(base), regs.as_mut_bytes())?;
1361 thread_state.registers.from_user_regs_struct(®s);
1362 }
1363 Ok(())
1364 }
1365 _ => error!(EINVAL),
1366 }
1367}
1368
1369#[inline(never)]
1370pub fn ptrace_syscall_enter(current_task: &mut CurrentTask) {
1371 let block = {
1372 let mut state = current_task.write();
1373 if state.ptrace.is_some() {
1374 current_task.trace_syscalls.store(false, Ordering::Relaxed);
1375 let mut sig = SignalInfo::with_detail(
1376 SIGTRAP,
1377 (linux_uapi::SIGTRAP | 0x80) as i32,
1378 SignalDetail::None,
1379 );
1380 if state
1381 .ptrace
1382 .as_ref()
1383 .is_some_and(|ptrace| ptrace.has_option(PtraceOptions::TRACESYSGOOD))
1384 {
1385 sig.signal.set_ptrace_syscall_bit();
1386 }
1387 state.set_stopped(StopState::SyscallEnterStopping, Some(sig), None, None);
1388 true
1389 } else {
1390 false
1391 }
1392 };
1393 if block {
1394 current_task.block_if_stopped();
1395 }
1396}
1397
1398#[inline(never)]
1399pub fn ptrace_syscall_exit(current_task: &mut CurrentTask, is_error: bool) {
1400 let block = {
1401 let mut state = current_task.write();
1402 current_task.trace_syscalls.store(false, Ordering::Relaxed);
1403 if state.ptrace.is_some() {
1404 let mut sig = SignalInfo::with_detail(
1405 SIGTRAP,
1406 (linux_uapi::SIGTRAP | 0x80) as i32,
1407 SignalDetail::None,
1408 );
1409 if state
1410 .ptrace
1411 .as_ref()
1412 .is_some_and(|ptrace| ptrace.has_option(PtraceOptions::TRACESYSGOOD))
1413 {
1414 sig.signal.set_ptrace_syscall_bit();
1415 }
1416
1417 state.set_stopped(StopState::SyscallExitStopping, Some(sig), None, None);
1418 if let Some(ptrace) = &mut state.ptrace {
1419 ptrace.last_syscall_was_error = is_error;
1420 }
1421 true
1422 } else {
1423 false
1424 }
1425 };
1426 if block {
1427 current_task.block_if_stopped();
1428 }
1429}
1430
1431#[cfg(test)]
1432mod tests {
1433 use super::*;
1434 use crate::task::syscalls::sys_prctl;
1435 use crate::testing::{create_task, spawn_kernel_and_run};
1436 use starnix_uapi::PR_SET_PTRACER;
1437 use starnix_uapi::auth::CAP_SYS_PTRACE;
1438
1439 #[::fuchsia::test]
1440 async fn test_set_ptracer() {
1441 spawn_kernel_and_run(async |current_task| {
1442 let kernel = current_task.kernel().clone();
1443 let mut tracee = create_task(&kernel, "tracee");
1444 let mut tracer = create_task(&kernel, "tracer");
1445
1446 let mut creds = tracer.real_creds().clone();
1447 creds.cap_effective &= !CAP_SYS_PTRACE;
1448 tracer.set_creds(creds);
1449
1450 kernel.ptrace_scope.store(security::yama::SCOPE_RESTRICTED, Ordering::Relaxed);
1451 assert_eq!(sys_prctl(&mut tracee, PR_SET_PTRACER, 0xFFF, 0, 0, 0), error!(EINVAL));
1452
1453 assert_eq!(
1454 ptrace_attach(
1455 &mut tracer,
1456 &tracee.as_ref().task.tid,
1457 PtraceAttachType::Attach,
1458 UserAddress::NULL,
1459 ),
1460 error!(EPERM)
1461 );
1462
1463 assert!(
1464 sys_prctl(
1465 &mut tracee,
1466 PR_SET_PTRACER,
1467 tracer.thread_group().leader.id as u64,
1468 0,
1469 0,
1470 0
1471 )
1472 .is_ok()
1473 );
1474
1475 let mut not_tracer = create_task(&kernel, "not-tracer");
1476 not_tracer.set_creds(tracer.real_creds().clone());
1477 assert_eq!(
1478 ptrace_attach(
1479 &mut not_tracer,
1480 &tracee.as_ref().task.tid,
1481 PtraceAttachType::Attach,
1482 UserAddress::NULL,
1483 ),
1484 error!(EPERM)
1485 );
1486
1487 assert!(
1488 ptrace_attach(
1489 &mut tracer,
1490 &tracee.as_ref().task.tid,
1491 PtraceAttachType::Attach,
1492 UserAddress::NULL,
1493 )
1494 .is_ok()
1495 );
1496 })
1497 .await;
1498 }
1499
1500 #[::fuchsia::test]
1501 async fn test_set_ptracer_any() {
1502 spawn_kernel_and_run(async |current_task| {
1503 let kernel = current_task.kernel().clone();
1504 let mut tracee = create_task(&kernel, "tracee");
1505 let mut tracer = create_task(&kernel, "tracer");
1506
1507 let mut creds = tracer.real_creds().clone();
1508 creds.cap_effective &= !CAP_SYS_PTRACE;
1509 tracer.set_creds(creds);
1510
1511 kernel.ptrace_scope.store(security::yama::SCOPE_RESTRICTED, Ordering::Relaxed);
1512 assert_eq!(sys_prctl(&mut tracee, PR_SET_PTRACER, 0xFFF, 0, 0, 0), error!(EINVAL));
1513
1514 assert_eq!(
1515 ptrace_attach(
1516 &mut tracer,
1517 &tracee.as_ref().task.tid,
1518 PtraceAttachType::Attach,
1519 UserAddress::NULL,
1520 ),
1521 error!(EPERM)
1522 );
1523
1524 assert!(sys_prctl(&mut tracee, PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0).is_ok());
1525
1526 assert!(
1527 ptrace_attach(
1528 &mut tracer,
1529 &tracee.as_ref().task.tid,
1530 PtraceAttachType::Attach,
1531 UserAddress::NULL,
1532 )
1533 .is_ok()
1534 );
1535 })
1536 .await;
1537 }
1538
1539 #[::fuchsia::test]
1540 async fn test_unspawned_task_remove_does_not_register_zombie() {
1541 spawn_kernel_and_run(async |current_task| {
1542 let kernel = current_task.kernel().clone();
1543 let tracee = create_task(&kernel, "tracee");
1544 let mut tracer = create_task(&kernel, "tracer");
1545
1546 assert!(
1547 ptrace_attach(
1548 &mut tracer,
1549 &tracee.as_ref().task.tid,
1550 PtraceAttachType::Attach,
1551 UserAddress::NULL,
1552 )
1553 .is_ok()
1554 );
1555
1556 assert!(!tracee.is_spawned());
1560 drop(tracee);
1561 assert!(tracer.thread_group().write().zombie_ptracees.is_empty());
1562 })
1563 .await;
1564 }
1565}