Skip to main content

starnix_core/ptrace/
ptrace.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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/// For most of the time, for the purposes of ptrace, a tracee is either "going"
86/// or "stopped".  However, after certain ptrace calls, there are special rules
87/// to be followed.
88#[derive(Clone, Default, PartialEq)]
89pub enum PtraceStatus {
90    /// Proceed as otherwise indicated by the task's stop status.
91    #[default]
92    Default,
93    /// Resuming after a ptrace_cont with a signal, so do not stop for signal-delivery-stop
94    Continuing,
95    /// "The state of the tracee after PTRACE_LISTEN is somewhat of a
96    /// gray area: it is not in any ptrace-stop (ptrace commands won't work on it,
97    /// and it will deliver waitpid(2) notifications), but it also may be considered
98    /// "stopped" because it is not executing instructions (is not scheduled), and
99    /// if it was in group-stop before PTRACE_LISTEN, it will not respond to signals
100    /// until SIGCONT is received."
101    Listening,
102}
103
104impl PtraceStatus {
105    pub fn is_continuing(&self) -> bool {
106        *self == PtraceStatus::Continuing
107    }
108}
109
110/// Indicates the way that ptrace attached to the task.
111#[derive(Copy, Clone, PartialEq)]
112pub enum PtraceAttachType {
113    /// Attached with PTRACE_ATTACH
114    Attach,
115    /// Attached with PTRACE_SEIZE
116    Seize,
117}
118
119/// The ptrace state that a new task needs to connect to the same tracer as the
120/// task that clones it.
121#[derive(Clone)]
122pub struct PtraceCoreState {
123    /// The task of the tracer
124    pub task: Weak<Task>,
125
126    /// The thread group of the tracer
127    pub thread_group: Weak<ThreadGroup>,
128
129    /// Whether the attach was a seize or an attach.  There are a few subtle
130    /// differences in behavior of the different attach types - see ptrace(2).
131    pub attach_type: PtraceAttachType,
132
133    /// The options set by PTRACE_SETOPTIONS
134    pub options: PtraceOptions,
135
136    /// The tracer waits on this WaitQueue to find out if the tracee has done
137    /// something worth being notified about.
138    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
147/// Per-task ptrace-related state
148pub struct PtraceState {
149    /// The core state of the tracer, which can be shared between processes
150    pub core_state: PtraceCoreState,
151
152    /// The tracee waits on this WaitQueue to find out when it should stop or wake
153    /// for ptrace-related shenanigans.
154    pub tracee_waiters: WaitQueue,
155
156    /// The signal that caused the task to enter the given state (for
157    /// signal-delivery-stop)
158    pub last_signal: Option<SignalInfo>,
159
160    /// Whether waitpid() will return the last signal.  The presence of last_signal
161    /// can't be used for that, because that needs to be saved for GETSIGINFO.
162    pub last_signal_waitable: bool,
163
164    /// Data about the PTRACE_EVENT that caused the most recent stop (if any).
165    pub event_data: Option<PtraceEventData>,
166
167    /// Indicates whether the last ptrace call put this thread into a state with
168    /// special semantics for stopping behavior.
169    pub stop_status: PtraceStatus,
170
171    /// For SYSCALL_INFO_EXIT
172    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            // Waiting for any change of state
210            return self.last_signal_waitable;
211        }
212        if !options.wait_for_continued && !stop.is_stopping_or_stopped() {
213            // Only waiting for stops, but is not stopped.
214            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            // We don't want waiters to think the process was unstopped because
222            // of a sigkill. They will get woken when the process dies.
223            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    // Gets the last signal, and optionally clears the wait state of the ptrace.
242    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    /// Returns enough of the ptrace state to propagate it to a fork / clone / vforked task.
265    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    /// Returns an (i32, ptrace_syscall_info) pair.  The ptrace_syscall_info is
274    /// the info associated with the syscall that the target task is currently
275    /// blocked on, The i32 is (per ptrace(2)) "the number of bytes available to
276    /// be written by the kernel.  If the size of the data to be written by the
277    /// kernel exceeds the size specified by the addr argument, the output data
278    /// is truncated."; ptrace(PTRACE_GET_SYSCALL_INFO) returns that value"
279    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                    // If any additional arch32 archs are added, just use a cfg
302                    // macro here.
303                    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
357/// A zombie that must delivered to a tracer process for a traced process.
358struct TracedZombie {
359    /// An artificial zombie that must be delivered to the tracer program.
360    artificial_zombie: ZombieProcess,
361
362    /// The real zombie notification to be sent after the artificial zombie has been delivered to
363    /// the tracer.
364    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/// A list of zombie processes that were traced by a given tracer, but which
401/// have not yet notified that tracer of their exit.  Once the tracer is
402/// notified, the original parent will be notified.
403#[derive(Default)]
404pub struct ZombiePtracees {
405    /// A list of zombies that have to be delivered to the ptracer.  The key is
406    /// the tid of the traced process.
407    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    /// Adds a zombie tracee to the list, but does not provide a parent task to
422    /// notify when the tracer is done.
423    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    /// Detaches from the zombie tracee with the given TID.
432    ///
433    /// Returns the notification to deliver to the tracee's real parent.
434    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    /// Detaches from every zombie tracee.
443    ///
444    /// Returns the notifications to deliver to the tracees' real parents.
445    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    /// Provide a parent task and a zombie to notify when the tracer has been
458    /// notified.
459    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    /// When a parent dies without having been notified, replace it with a given
481    /// new parent.
482    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    /// Returns true iff there is a zombie waiting to be delivered to the tracers matching the
499    /// given selector.
500    pub fn has_zombie_matching(&self, selector: &ProcessSelector) -> bool {
501        self.zombies.values().any(|z| z.artificial_zombie.matches_selector(selector))
502    }
503
504    /// Returns true iff the given `tid` is a traced thread that needs to deliver a zombie to the
505    /// tracer.
506    pub fn has_tracee(&self, tid: &Pid) -> bool {
507        self.zombies.contains_key(tid)
508    }
509
510    /// Returns a zombie matching the given selector and options, and
511    /// (optionally) a thread group to notify after the caller has consumed that
512    /// zombie.
513    pub fn get_waitable_entry(
514        &mut self,
515        selector: &ProcessSelector,
516        options: &WaitingOptions,
517    ) -> Option<(ZombieProcess, Option<(Weak<ThreadGroup>, OwnedRef<ZombieProcess>)>)> {
518        // Look for the last zombie in the map that matches process
519        // selector and waiting options.
520        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            // Maybe notify child waiters.
532            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
546// PR_SET_PTRACER_ANY is defined as ((unsigned long) -1) in Linux UAPI.
547pub const PR_SET_PTRACER_ANY: u64 = u64::MAX;
548pub const PR_SET_PTRACER_ANY_ARCH32: u64 = u32::MAX as u64;
549
550/// Indicates processes specifically allowed to trace a given process if using
551/// SCOPE_RESTRICTED.  Used by prctl(PR_SET_PTRACER).
552#[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    /// Tracer thread group is exiting, which detaches all its tracees.
563    Exiting(&'a ThreadGroup),
564    /// Tracer invoked a ptrace syscall.
565    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
584/// Continues the target thread, optionally detaching from it.
585///
586/// # Arguments
587/// * `tracer` - The context of the tracer performing the operation.
588/// * `tracee` - The target thread to continue.
589/// * `data` - Is treated as it is in PTRACE_CONT.
590/// * `detach` - If true, the tracer will detach from the tracee.
591fn 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    // Verify under lock that we are still the tracer.
609    // This check is performed under the tracee task lock to prevent races where the tracee
610    // is concurrently detached or re-attached to another tracer in the window between our
611    // initial check (e.g. in ptrace_dispatch or wait_on_pid) and when we actually execute
612    // the continuation. This ensures we don't accidentally control a task we no longer trace,
613    // or interfere with another tracer's control.
614    {
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            // If the tracer thread group is exiting, we must force-detach even if the
631            // tracee is currently running (waking/awake), otherwise the tracee would
632            // be left permanently attached to a dead tracer.
633            state.set_ptrace(None)?;
634            return Ok(());
635        }
636        // Manual PTRACE_DETACH via syscall (tracer is CurrentTask) is not allowed
637        // on running tracees and returns EIO.
638        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            // If the tracer resumes the tracee with the same signal number (or one already
649            // updated via `PTRACE_SETSIGINFO`), preserve `last_signal`'s `siginfo_t` details
650            // and `force` state; otherwise replace `last_signal` with the newly injected
651            // signal so details and `force` from the stopping signal do not leak.
652            // Note: Suppressing unforced `SIGSTOP` / `SIGKILL` here when resuming `init` (PID 1)
653            // differs from Linux, where signals injected via `PTRACE_CONT` / `PTRACE_DETACH`
654            // bypass send-time filtering and only catchable signals are filtered at delivery time.
655            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        // This will wake up the task for us, and also release state
692        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            // "If the tracee was already stopped by a signal and PTRACE_LISTEN
716            // was sent to it, the tracee stops with PTRACE_EVENT_STOP and
717            // WSTOPSIG(status) returns the stop signal"
718            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
769/// For all ptrace requests that require an attached tracee
770pub 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    // These requests may be run without the thread in a stop state, or
791    // check the stop state themselves.
792    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(&current_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(&current_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(&current_task.task),
824                tracee.as_ref(),
825                &data,
826            )?;
827            return Ok(starnix_syscalls::SUCCESS);
828        }
829        _ => {}
830    }
831
832    // The remaining requests (to be added) require the thread to be stopped.
833    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            // NB: The behavior of the syscall is different from the behavior in ptrace(2),
845            // which is provided by libc.
846            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            // addr is the size of the buffer pointed to
936            // by data, but has to be sizeof(sigset_t).
937            if addr.ptr() != std::mem::size_of::<SigSet>() {
938                return error!(EINVAL);
939            }
940            // sigset comes from *data.
941            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            // addr is the size of the buffer pointed to
949            // by data, but has to be sizeof(sigset_t).
950            if addr.ptr() != std::mem::size_of::<SigSet>() {
951                return error!(EINVAL);
952            }
953            // sigset goes in *data.
954            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                // SAFETY: ptrace_syscall_info does not implement FromBytes/IntoBytes,
992                // so this has to happen manually.
993                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            // This is what we currently support.
1008            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
1045/// Makes the given thread group trace the given task.
1046fn 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 the tracee is already stopped, make sure that the tracer can
1071    // identify that right away.
1072    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
1084/// Uses the given core ptrace state (including tracer, attach type, etc) to
1085/// attach to another task, given by `tracee_task`.  Also sends a signal to stop
1086/// tracee_task.  Typical for when inheriting ptrace state from another task.
1087pub(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    // The newly started tracee starts with a signal that depends on the attach type.
1107    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        // Ptrace-emitted SIGTRAP signal cannot be blocked.
1112        SignalInfo::forced(SIGTRAP)
1113    } else {
1114        SignalInfo::kernel(SIGSTOP)
1115    };
1116    send_signal_first(tracee_task, state, signal);
1117
1118    // If the tracer is already sleeping in waitpid, it is waiting on the shared `tracer_waiters`
1119    // queue. We must wake it up here so it can register on the new tracee's queue (and update its
1120    // wait registration loop) rather than missing the initial stopped status notification.
1121    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        // TODO: Move this check into `do_attach()` so that there is a single `ptrace_access_check(tracer, tracee)`?
1131        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            &current_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(&current_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        // When seizing, |data| should be used as the options bitmask.
1173        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
1181/// Implementation of ptrace(PTRACE_PEEKUSER).  The user struct holds the
1182/// registers and other information about the process.  See ptrace(2) and
1183/// sys/user.h for full details.
1184pub 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), &regs.as_bytes()[..*len])?;
1233            } else {
1234                let regs = thread_state.registers.to_user_regs_struct();
1235                current_task.write_memory(UserAddress::from(base), &regs.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(&regs);
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(&regs);
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            // create_task() returns an unspawned task. Dropping the tracee causes it to exit, which
1401            // triggers zombie tracee registration. The tracee must not register with the tracer
1402            // because it never spawned.
1403            assert!(!tracee.is_spawned());
1404            drop(tracee);
1405            assert!(tracer.thread_group().write().zombie_ptracees.is_empty());
1406        })
1407        .await;
1408    }
1409}