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