Skip to main content

starnix_core/task/
thread_group.rs

1// Copyright 2021 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::device::terminal::{Terminal, TerminalController};
6use crate::mutable_state::{state_accessor, state_implementation};
7use crate::ptrace::{
8    AtomicStopState, PtraceAllowedPtracers, PtraceEvent, PtraceOptions, PtraceStatus, PtraceTracer,
9    StopState, ZombiePtracees, ptrace_detach,
10};
11use crate::security;
12use crate::signals::syscalls::WaitingOptions;
13use crate::signals::{
14    DeliveryAction, IntoSignalInfoOptions, QueuedSignals, SignalActions, SignalDetail, SignalInfo,
15    UncheckedSignalInfo, action_for_signal, send_standard_signal,
16};
17use crate::task::memory_attribution::MemoryAttributionLifecycleEvent;
18use crate::task::{
19    ControllingTerminal, CurrentTask, ExitStatus, Kernel, PidTable, ProcessGroup, Session,
20    SessionDisassociation, Task, TaskMutableState, TaskPersistentInfo, TypedWaitQueue,
21};
22use crate::time::{IntervalTimerHandle, TimerTable};
23use itertools::Itertools;
24use macro_rules_attribute::apply;
25use starnix_lifecycle::{AtomicCounter, DropNotifier};
26use starnix_logging::{log_debug, log_error, log_info, log_warn, track_stub};
27use starnix_sync::{
28    LockDepMutex, LockDepRwLock, RwLockWriteGuard, ThreadGroupLimits, ThreadGroupMutableStateLock,
29    ThreadGroupPendingSignalsLock, ThreadGroupPtraceesLock, allow_subclass, ordered_write_lock,
30};
31use starnix_task_command::TaskCommand;
32use starnix_types::ownership::{OwnedRef, Releasable};
33use starnix_types::stats::TaskTimeStats;
34use starnix_types::time::{itimerspec_from_itimerval, timeval_from_duration};
35use starnix_uapi::arc_key::WeakKey;
36use starnix_uapi::auth::{CAP_SYS_ADMIN, CAP_SYS_RESOURCE, Credentials};
37use starnix_uapi::errors::Errno;
38use starnix_uapi::personality::PersonalityFlags;
39use starnix_uapi::resource_limits::{Resource, ResourceLimits};
40use starnix_uapi::signals::{
41    SIGCHLD, SIGCONT, SIGHUP, SIGKILL, SIGTERM, SIGTTOU, SigSet, Signal, UncheckedSignal,
42};
43use starnix_uapi::user_address::UserAddress;
44use starnix_uapi::{
45    ITIMER_PROF, ITIMER_REAL, ITIMER_VIRTUAL, SI_TKILL, SI_USER, SIG_IGN, errno, error, itimerval,
46    pid_t, rlimit, tid_t, uid_t,
47};
48use std::collections::BTreeMap;
49use std::fmt;
50use std::sync::atomic::{AtomicBool, Ordering};
51use std::sync::{Arc, OnceLock, Weak};
52use zx::{Koid, Status};
53
54#[derive(Debug)]
55pub struct ZirconProcess {
56    process: zx::Process,
57    koid: Result<Koid, Status>,
58}
59
60impl ZirconProcess {
61    pub fn new(process: zx::Process) -> Self {
62        let koid = process.koid();
63        Self { process, koid }
64    }
65
66    pub fn koid(&self) -> Result<Koid, Status> {
67        self.koid
68    }
69}
70
71impl std::ops::Deref for ZirconProcess {
72    type Target = zx::Process;
73    fn deref(&self) -> &Self::Target {
74        &self.process
75    }
76}
77
78/// A weak reference to a thread group that can be used in set and maps.
79#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
80pub struct ThreadGroupKey {
81    pid: pid_t,
82    thread_group: WeakKey<ThreadGroup>,
83}
84
85impl ThreadGroupKey {
86    /// The pid of the thread group keyed by this object.
87    ///
88    /// As the key is weak (and pid are not unique due to pid namespaces), this should not be used
89    /// as an unique identifier of the thread group.
90    pub fn pid(&self) -> pid_t {
91        self.pid
92    }
93}
94
95impl std::ops::Deref for ThreadGroupKey {
96    type Target = Weak<ThreadGroup>;
97    fn deref(&self) -> &Self::Target {
98        &self.thread_group.0
99    }
100}
101
102impl From<&ThreadGroup> for ThreadGroupKey {
103    fn from(tg: &ThreadGroup) -> Self {
104        Self { pid: tg.leader, thread_group: WeakKey::from(&tg.weak_self.upgrade().unwrap()) }
105    }
106}
107
108impl<T: AsRef<ThreadGroup>> From<T> for ThreadGroupKey {
109    fn from(tg: T) -> Self {
110        tg.as_ref().into()
111    }
112}
113
114/// Values used for waiting on the [ThreadGroup] lifecycle wait queue.
115#[repr(u64)]
116pub enum ThreadGroupLifecycleWaitValue {
117    /// Wait for updates to the WaitResults of tasks in the group.
118    ChildStatus,
119    /// Wait for updates to `stopped`.
120    Stopped,
121}
122
123impl Into<u64> for ThreadGroupLifecycleWaitValue {
124    fn into(self) -> u64 {
125        self as u64
126    }
127}
128
129/// Child process that have exited, but the zombie ptrace needs to be consumed
130/// before they can be waited for.
131#[derive(Clone, Debug)]
132pub struct DeferredZombiePTracer {
133    /// Original tracer
134    pub tracer_thread_group_key: ThreadGroupKey,
135    /// Tracee tid
136    pub tracee_tid: tid_t,
137    /// Tracee pgid
138    pub tracee_pgid: pid_t,
139    /// Tracee thread group
140    pub tracee_thread_group_key: ThreadGroupKey,
141}
142
143impl DeferredZombiePTracer {
144    fn new(tracer: &ThreadGroup, tracee: &Task, tracee_pgid: pid_t) -> Self {
145        Self {
146            tracer_thread_group_key: tracer.into(),
147            tracee_tid: tracee.tid,
148            tracee_pgid,
149            tracee_thread_group_key: tracee.thread_group_key.clone(),
150        }
151    }
152}
153
154/// The mutable state of the ThreadGroup.
155pub struct ThreadGroupMutableState {
156    /// The parent thread group.
157    ///
158    /// The value needs to be writable so that it can be re-parent to the correct subreaper if the
159    /// parent ends before the child.
160    pub parent: Option<ThreadGroupParent>,
161
162    /// The signal this process generates on exit.
163    pub exit_signal: Option<Signal>,
164
165    /// The tasks in the thread group.
166    ///
167    /// The references to Task is weak to prevent cycles as Task have a Arc reference to their
168    /// thread group.
169    /// It is still expected that these weak references are always valid, as tasks must unregister
170    /// themselves before they are deleted.
171    tasks: BTreeMap<tid_t, TaskContainer>,
172
173    /// The children of this thread group.
174    ///
175    /// The references to ThreadGroup is weak to prevent cycles as ThreadGroup have a Arc reference
176    /// to their parent.
177    /// It is still expected that these weak references are always valid, as thread groups must unregister
178    /// themselves before they are deleted.
179    pub children: BTreeMap<pid_t, Weak<ThreadGroup>>,
180
181    /// Child tasks that have exited, but not yet been waited for.
182    pub zombie_children: Vec<OwnedRef<ZombieProcess>>,
183
184    /// ptracees of this process that have exited, but not yet been waited for.
185    pub zombie_ptracees: ZombiePtracees,
186
187    /// Child processes that have exited, but the zombie ptrace needs to be consumed
188    /// before they can be waited for.
189    pub deferred_zombie_ptracers: Vec<DeferredZombiePTracer>,
190
191    /// Unified [WaitQueue] for all waited ThreadGroup events.
192    pub lifecycle_waiters: TypedWaitQueue<ThreadGroupLifecycleWaitValue>,
193
194    /// Whether this thread group will inherit from children of dying processes in its descendant
195    /// tree.
196    pub is_child_subreaper: bool,
197
198    /// The IDs used to perform shell job control.
199    pub process_group: Arc<ProcessGroup>,
200
201    pub did_exec: bool,
202
203    /// A signal that indicates whether the process is going to become waitable
204    /// via waitid and waitpid for either WSTOPPED or WCONTINUED, depending on
205    /// the value of `stopped`. If not None, contains the SignalInfo to return.
206    pub last_signal: Option<SignalInfo>,
207
208    /// Whether the `ThreadGroup` is running or not.
209    ///
210    /// For exited thread groups, this contains the exit status.
211    run_state: ThreadGroupRunState,
212
213    /// Time statistics accumulated from the children.
214    pub children_time_stats: TaskTimeStats,
215
216    /// Personality flags set with `sys_personality()`.
217    pub personality: PersonalityFlags,
218
219    /// Thread groups allowed to trace tasks in this this thread group.
220    pub allowed_ptracers: PtraceAllowedPtracers,
221
222    /// Channel to message when this thread group exits.
223    exit_notifier: Option<futures::channel::oneshot::Sender<()>>,
224
225    /// Notifier for name changes.
226    pub notifier: Option<std::sync::mpsc::Sender<MemoryAttributionLifecycleEvent>>,
227}
228
229/// A collection of `Task` objects that roughly correspond to a "process".
230///
231/// Userspace programmers often think about "threads" and "process", but those concepts have no
232/// clear analogs inside the kernel because tasks are typically created using `clone(2)`, which
233/// takes a complex set of flags that describes how much state is shared between the original task
234/// and the new task.
235///
236/// If a new task is created with the `CLONE_THREAD` flag, the new task will be placed in the same
237/// `ThreadGroup` as the original task. Userspace typically uses this flag in conjunction with the
238/// `CLONE_FILES`, `CLONE_VM`, and `CLONE_FS`, which corresponds to the userspace notion of a
239/// "thread". For example, that's how `pthread_create` behaves. In that sense, a `ThreadGroup`
240/// normally corresponds to the set of "threads" in a "process". However, this pattern is purely a
241/// userspace convention, and nothing stops userspace from using `CLONE_THREAD` without
242/// `CLONE_FILES`, for example.
243///
244/// In Starnix, a `ThreadGroup` corresponds to a Zircon process, which means we do not support the
245/// `CLONE_THREAD` flag without the `CLONE_VM` flag. If we run into problems with this limitation,
246/// we might need to revise this correspondence.
247///
248/// Each `Task` in a `ThreadGroup` has the same thread group ID (`tgid`). The task with the same
249/// `pid` as the `tgid` is called the thread group leader.
250///
251/// Thread groups are destroyed when the last task in the group exits.
252pub struct ThreadGroup {
253    /// Weak reference to the `OwnedRef` of this `ThreadGroup`. This allows to retrieve the
254    /// `TempRef` from a raw `ThreadGroup`.
255    pub weak_self: Weak<ThreadGroup>,
256
257    /// The kernel to which this thread group belongs.
258    pub kernel: Arc<Kernel>,
259
260    /// A handle to the underlying Zircon process object.
261    ///
262    /// Currently, we have a 1-to-1 mapping between thread groups and zx::process
263    /// objects. This approach might break down if/when we implement CLONE_VM
264    /// without CLONE_THREAD because that creates a situation where two thread
265    /// groups share an address space. To implement that situation, we might
266    /// need to break the 1-to-1 mapping between thread groups and zx::process
267    /// or teach zx::process to share address spaces.
268    pub process: ZirconProcess,
269
270    /// A handle to the restricted address space for the Zircon process object.
271    pub root_vmar: zx::Vmar,
272
273    /// The lead task of this thread group.
274    ///
275    /// The lead task is typically the initial thread created in the thread group.
276    pub leader: pid_t,
277
278    // TODO(https://fxbug.dev/508746892): Remove this once the `PidTable` lock is removed.
279    /// Cached weak reference to the leader task.
280    ///
281    /// This is used to break a deadlock in signal delivery, where a reference to the leader task
282    /// must be obtained in order to do access checks in situations where the leader has exited and
283    /// is no longer in the task list.
284    pub leader_task: OnceLock<Weak<Task>>,
285
286    /// The signal actions that are registered for this process.
287    pub signal_actions: Arc<SignalActions>,
288
289    /// The timers for this thread group (from timer_create(), etc.).
290    pub timers: TimerTable,
291
292    /// A mechanism to be notified when this `ThreadGroup` is destroyed.
293    pub drop_notifier: DropNotifier,
294
295    /// Whether the process is currently stopped.
296    ///
297    /// Must only be set when the `mutable_state` write lock is held.
298    stop_state: AtomicStopState,
299
300    /// The mutable state of the ThreadGroup.
301    mutable_state: LockDepRwLock<ThreadGroupMutableState, ThreadGroupMutableStateLock>,
302
303    /// The resource limits for this thread group.  This is outside mutable_state
304    /// to avoid deadlocks where the thread_group lock is held when acquiring
305    /// the task lock, and vice versa.
306    pub limits: LockDepMutex<ResourceLimits, ThreadGroupLimits>,
307
308    /// The next unique identifier for a seccomp filter.  These are required to be
309    /// able to distinguish identical seccomp filters, which are treated differently
310    /// for the purposes of SECCOMP_FILTER_FLAG_TSYNC.  Inherited across clone because
311    /// seccomp filters are also inherited across clone.
312    pub next_seccomp_filter_id: AtomicCounter<u64>,
313
314    /// Tasks ptraced by this process
315    pub ptracees: LockDepMutex<BTreeMap<tid_t, TaskContainer>, ThreadGroupPtraceesLock>,
316
317    /// The signals that are currently pending for this thread group.
318    pub pending_signals: LockDepMutex<QueuedSignals, ThreadGroupPendingSignalsLock>,
319
320    /// Whether or not there are any pending signals available for tasks in this thread group.
321    /// Used to avoid having to acquire the signal state lock in hot paths.
322    pub has_pending_signals: AtomicBool,
323
324    /// The monotonic time at which the thread group started.
325    pub start_time: zx::MonotonicInstant,
326
327    /// Whether to log syscalls at INFO level for this thread group.
328    log_syscalls_as_info: AtomicBool,
329}
330
331impl fmt::Debug for ThreadGroup {
332    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333        write!(
334            f,
335            "{}({})",
336            self.process.get_name().unwrap_or(zx::Name::new_lossy("<unknown>")),
337            self.leader
338        )
339    }
340}
341
342impl ThreadGroup {
343    pub fn sync_syscall_log_level(&self) {
344        let command = self.read().leader_command();
345        let filters = self.kernel.syscall_log_filters.lock();
346        let should_log = filters.iter().any(|f| f.matches(&command));
347        let prev_should_log = self.log_syscalls_as_info.swap(should_log, Ordering::Relaxed);
348        let change_str = match (should_log, prev_should_log) {
349            (true, false) => Some("Enabled"),
350            (false, true) => Some("Disabled"),
351            _ => None,
352        };
353        if let Some(change_str) = change_str {
354            log_info!(
355                "{change_str} info syscall logs for thread group {} (command: {command})",
356                self.leader
357            );
358        }
359    }
360
361    #[inline]
362    pub fn syscall_log_level(&self) -> starnix_logging::Level {
363        if self.log_syscalls_as_info.load(Ordering::Relaxed) {
364            starnix_logging::Level::Info
365        } else {
366            starnix_logging::Level::Trace
367        }
368    }
369}
370
371impl PartialEq for ThreadGroup {
372    fn eq(&self, other: &Self) -> bool {
373        self.leader == other.leader
374    }
375}
376
377impl Drop for ThreadGroup {
378    fn drop(&mut self) {
379        let state = self.mutable_state.get_mut();
380        assert!(state.tasks.is_empty());
381        assert!(state.children.is_empty());
382        assert!(state.zombie_children.is_empty());
383        assert!(state.zombie_ptracees.is_empty());
384        #[cfg(any(test, debug_assertions))]
385        assert!(
386            state
387                .parent
388                .as_ref()
389                .and_then(|p| p.0.upgrade().as_ref().map(|p| p
390                    .read()
391                    .children
392                    .get(&self.leader)
393                    .is_none()))
394                .unwrap_or(true)
395        );
396    }
397}
398
399/// A wrapper around a `Weak<ThreadGroup>` that expects the underlying `Weak` to always be
400/// valid. The wrapper will check this at runtime during creation and upgrade.
401pub struct ThreadGroupParent(Weak<ThreadGroup>);
402
403impl ThreadGroupParent {
404    pub fn new(t: Weak<ThreadGroup>) -> Self {
405        debug_assert!(t.upgrade().is_some());
406        Self(t)
407    }
408
409    pub fn upgrade(&self) -> Arc<ThreadGroup> {
410        self.0.upgrade().expect("ThreadGroupParent references must always be valid")
411    }
412}
413
414impl Clone for ThreadGroupParent {
415    fn clone(&self) -> Self {
416        Self(self.0.clone())
417    }
418}
419
420/// A selector that can match a process. Works as a representation of the pid argument to syscalls
421/// like wait and kill.
422#[derive(Debug, Clone)]
423pub enum ProcessSelector {
424    /// Matches any process at all.
425    Any,
426    /// Matches only the process with the specified pid
427    Pid(pid_t),
428    /// Matches all the processes in the given process group
429    Pgid(pid_t),
430    /// Match the thread group with the given key
431    Process(ThreadGroupKey),
432}
433
434impl ProcessSelector {
435    pub fn match_tid(&self, tid: tid_t, pid_table: &PidTable) -> bool {
436        match *self {
437            ProcessSelector::Pid(p) => {
438                if p == tid {
439                    true
440                } else {
441                    if let Ok(task_ref) = pid_table.get_task(tid) {
442                        task_ref.get_pid() == p
443                    } else {
444                        false
445                    }
446                }
447            }
448            ProcessSelector::Any => true,
449            ProcessSelector::Pgid(pgid) => {
450                if let Ok(task_ref) = pid_table.get_task(tid) {
451                    pid_table.get_process_group(pgid).as_ref()
452                        == Some(&task_ref.thread_group().read().process_group)
453                } else {
454                    false
455                }
456            }
457            ProcessSelector::Process(ref key) => {
458                if let Some(tg) = key.upgrade() {
459                    tg.read().tasks.contains_key(&tid)
460                } else {
461                    false
462                }
463            }
464        }
465    }
466}
467
468#[derive(Clone, Debug, PartialEq, Eq)]
469pub struct ProcessExitInfo {
470    pub status: ExitStatus,
471    pub exit_signal: Option<Signal>,
472}
473
474#[derive(Clone, Debug, Default, PartialEq, Eq)]
475enum ThreadGroupRunState {
476    #[default]
477    Running,
478    Exiting(ExitStatus),
479    Exited(ExitStatus),
480}
481
482#[derive(Clone, Debug, PartialEq, Eq)]
483pub struct WaitResult {
484    pub pid: pid_t,
485    pub uid: uid_t,
486
487    pub exit_info: ProcessExitInfo,
488
489    /// Cumulative time stats for the process and its children.
490    pub time_stats: TaskTimeStats,
491}
492
493impl WaitResult {
494    // According to wait(2) man page, SignalInfo.signal needs to always be set to SIGCHLD
495    pub fn as_signal_info(&self) -> SignalInfo {
496        SignalInfo::with_detail(
497            SIGCHLD,
498            self.exit_info.status.signal_info_code(),
499            SignalDetail::SIGCHLD {
500                pid: self.pid,
501                uid: self.uid,
502                status: self.exit_info.status.signal_info_status(),
503            },
504        )
505    }
506}
507
508#[derive(Debug)]
509pub struct ZombieProcess {
510    pub thread_group_key: ThreadGroupKey,
511    pub pgid: pid_t,
512    pub uid: uid_t,
513
514    pub exit_info: ProcessExitInfo,
515
516    /// Cumulative time stats for the process and its children.
517    pub time_stats: TaskTimeStats,
518
519    /// Whether dropping this ZombieProcess should imply removing the pid from
520    /// the PidTable
521    pub is_canonical: bool,
522}
523
524impl PartialEq for ZombieProcess {
525    fn eq(&self, other: &Self) -> bool {
526        // We assume only one set of ZombieProcess data per process, so this should cover it.
527        self.thread_group_key == other.thread_group_key
528            && self.pgid == other.pgid
529            && self.uid == other.uid
530            && self.is_canonical == other.is_canonical
531    }
532}
533
534impl Eq for ZombieProcess {}
535
536impl PartialOrd for ZombieProcess {
537    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
538        Some(self.cmp(other))
539    }
540}
541
542impl Ord for ZombieProcess {
543    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
544        self.thread_group_key.cmp(&other.thread_group_key)
545    }
546}
547
548impl ZombieProcess {
549    pub fn new(
550        thread_group: ThreadGroupStateRef<'_>,
551        credentials: &Credentials,
552        exit_info: ProcessExitInfo,
553    ) -> OwnedRef<Self> {
554        let time_stats = thread_group.base.time_stats() + thread_group.children_time_stats;
555        OwnedRef::new(ZombieProcess {
556            thread_group_key: thread_group.base.into(),
557            pgid: thread_group.process_group.leader,
558            uid: credentials.uid,
559            exit_info,
560            time_stats,
561            is_canonical: true,
562        })
563    }
564
565    pub fn pid(&self) -> pid_t {
566        self.thread_group_key.pid()
567    }
568
569    pub fn to_wait_result(&self) -> WaitResult {
570        WaitResult {
571            pid: self.pid(),
572            uid: self.uid,
573            exit_info: self.exit_info.clone(),
574            time_stats: self.time_stats,
575        }
576    }
577
578    pub fn as_artificial(&self) -> Self {
579        ZombieProcess {
580            thread_group_key: self.thread_group_key.clone(),
581            pgid: self.pgid,
582            uid: self.uid,
583            exit_info: self.exit_info.clone(),
584            time_stats: self.time_stats,
585            is_canonical: false,
586        }
587    }
588
589    pub fn matches_selector(&self, selector: &ProcessSelector) -> bool {
590        match *selector {
591            ProcessSelector::Any => true,
592            ProcessSelector::Pid(pid) => self.pid() == pid,
593            ProcessSelector::Pgid(pgid) => self.pgid == pgid,
594            ProcessSelector::Process(ref key) => self.thread_group_key == *key,
595        }
596    }
597
598    pub fn matches_selector_and_waiting_option(
599        &self,
600        selector: &ProcessSelector,
601        options: &WaitingOptions,
602    ) -> bool {
603        if !self.matches_selector(selector) {
604            return false;
605        }
606
607        if options.wait_for_all {
608            true
609        } else {
610            // A "clone" zombie is one which has delivered no signal, or a
611            // signal other than SIGCHLD to its parent upon termination.
612            options.wait_for_clone == (self.exit_info.exit_signal != Some(SIGCHLD))
613        }
614    }
615}
616
617impl Releasable for ZombieProcess {
618    type Context<'a> = &'a mut PidTable;
619
620    fn release<'a>(self, pids: &'a mut PidTable) {
621        if self.is_canonical {
622            pids.remove_zombie(self.pid());
623        }
624    }
625}
626
627/// A zombie process that is pending notification.
628///
629/// # Thread Safety
630///
631/// Notifications are generally produced in contexts in which a [`ThreadGroup`] state lock is held.
632/// Any such lock must be released before notifications are delivered. The notification's
633/// recipient thread group may be:
634/// - The originating thread group, in which case delivery while locked would self-deadlock.
635/// - One of this thread group's ancestors, in which case delivery while locked would invert the
636///   parent-child ordering of [`ThreadGroup`] locks.
637///
638/// The [`PidTable`] lock must be held continuously between [`ZombieNotification`] production and
639/// delivery to protect against concurrent exit races. Delivery requires releasing [`ThreadGroup`]
640/// state locks. If the recipient thread group exits before the notification is delivered, subreaper
641/// identification becomes impossible and the zombie must be reaped without notifying observers.
642/// Holding the [`PidTable`] lock throughout notification ensures the recipient cannot concurrently
643/// exit.
644#[must_use = "Notifications must be explicitly delivered or discarded"]
645pub struct ZombieNotification {
646    /// The recipient [`ThreadGroup`], which is generally the zombie's parent.
647    pub recipient: Weak<ThreadGroup>,
648
649    /// The zombie process to notify the parent of.
650    pub zombie: OwnedRef<ZombieProcess>,
651}
652
653impl ZombieNotification {
654    pub fn new(recipient: Weak<ThreadGroup>, zombie: OwnedRef<ZombieProcess>) -> Self {
655        Self { recipient, zombie }
656    }
657
658    /// Delivers the zombie notification to the parent.
659    ///
660    /// # Thread Safety
661    ///
662    /// Acquires [`ThreadGroup`] state locks.
663    pub fn deliver(self, pids: &mut PidTable) {
664        if let Some(parent) = self.recipient.upgrade() {
665            parent.do_zombie_notifications(self.zombie);
666        } else {
667            log_warn!("Zombie {} reaped silently", self.zombie.pid());
668            self.zombie.release(pids);
669        }
670    }
671
672    /// Discards the zombie notification without delivering it.
673    ///
674    /// If the [`ZombieProcess`] has no other owners, it will be reaped.
675    pub fn discard(self, pids: &mut PidTable) {
676        self.zombie.release(pids);
677    }
678}
679
680impl ThreadGroup {
681    /// Creates a ThreadGroup for a regular userspace process.
682    pub fn new(
683        kernel: Arc<Kernel>,
684        process: zx::Process,
685        root_vmar: zx::Vmar,
686        parent: Option<ThreadGroupWriteGuard<'_>>,
687        leader: pid_t,
688        exit_signal: Option<Signal>,
689        process_group: Arc<ProcessGroup>,
690        signal_actions: Arc<SignalActions>,
691    ) -> Arc<ThreadGroup> {
692        debug_assert!(!process.is_invalid());
693        debug_assert!(!root_vmar.is_invalid());
694        Self::new_internal(
695            kernel,
696            process,
697            root_vmar,
698            parent,
699            leader,
700            exit_signal,
701            process_group,
702            signal_actions,
703        )
704    }
705
706    /// Creates a ThreadGroup for a kernel system task (e.g., kthreadd).
707    pub fn for_system(
708        kernel: Arc<Kernel>,
709        leader: pid_t,
710        process_group: Arc<ProcessGroup>,
711    ) -> Arc<ThreadGroup> {
712        Self::new_internal(
713            kernel,
714            zx::Process::invalid(),
715            zx::Vmar::invalid(),
716            None,
717            leader,
718            Some(SIGCHLD),
719            process_group,
720            SignalActions::default(),
721        )
722    }
723
724    /// Creates a ThreadGroup suitable for use in tests.
725    ///
726    /// This function performs the minimal setup necessary to produce a valid `ThreadGroup`
727    /// instance. It uses an invalid handle for the root VMAR, sets no parent, and uses
728    /// default signal actions with `SIGCHLD` as the exit signal.
729    ///
730    /// This should only be used in tests where a full process environment is not required.
731    pub fn for_test(
732        kernel: Arc<Kernel>,
733        process: zx::Process,
734        parent: ThreadGroupWriteGuard<'_>,
735        leader: pid_t,
736        process_group: Arc<ProcessGroup>,
737    ) -> Arc<ThreadGroup> {
738        Self::new_internal(
739            kernel,
740            process,
741            zx::Vmar::invalid(),
742            Some(parent),
743            leader,
744            Some(SIGCHLD),
745            process_group,
746            SignalActions::default(),
747        )
748    }
749
750    fn new_internal(
751        kernel: Arc<Kernel>,
752        process: zx::Process,
753        root_vmar: zx::Vmar,
754        parent: Option<ThreadGroupWriteGuard<'_>>,
755        leader: pid_t,
756        exit_signal: Option<Signal>,
757        process_group: Arc<ProcessGroup>,
758        signal_actions: Arc<SignalActions>,
759    ) -> Arc<ThreadGroup> {
760        Arc::new_cyclic(|weak_self| {
761            let process = ZirconProcess::new(process);
762            let mut thread_group = ThreadGroup {
763                weak_self: weak_self.clone(),
764                kernel,
765                process,
766                root_vmar,
767                leader,
768                leader_task: OnceLock::new(),
769                signal_actions,
770                timers: Default::default(),
771                drop_notifier: Default::default(),
772                // A child process created via fork(2) inherits its parent's
773                // resource limits.  Resource limits are preserved across execve(2).
774                limits: LockDepMutex::new(
775                    parent
776                        .as_ref()
777                        .map(|p| p.base.limits.lock().clone())
778                        .unwrap_or(Default::default()),
779                ),
780                next_seccomp_filter_id: Default::default(),
781                ptracees: Default::default(),
782                stop_state: AtomicStopState::new(StopState::Awake),
783                pending_signals: Default::default(),
784                has_pending_signals: Default::default(),
785                start_time: zx::MonotonicInstant::get(),
786                mutable_state: ThreadGroupMutableState {
787                    parent: parent
788                        .as_ref()
789                        .map(|p| ThreadGroupParent::new(p.base.weak_self.clone())),
790                    exit_signal,
791                    tasks: BTreeMap::new(),
792                    children: BTreeMap::new(),
793                    zombie_children: vec![],
794                    zombie_ptracees: ZombiePtracees::new(),
795                    deferred_zombie_ptracers: vec![],
796                    lifecycle_waiters: TypedWaitQueue::<ThreadGroupLifecycleWaitValue>::default(),
797                    is_child_subreaper: false,
798                    process_group: Arc::clone(&process_group),
799                    did_exec: false,
800                    last_signal: None,
801                    run_state: Default::default(),
802                    children_time_stats: Default::default(),
803                    personality: parent
804                        .as_ref()
805                        .map(|p| p.personality)
806                        .unwrap_or(Default::default()),
807                    allowed_ptracers: PtraceAllowedPtracers::None,
808                    exit_notifier: None,
809                    notifier: None,
810                }
811                .into(),
812                log_syscalls_as_info: AtomicBool::new(false),
813            };
814
815            if let Some(mut parent) = parent {
816                thread_group.next_seccomp_filter_id.reset(parent.base.next_seccomp_filter_id.get());
817                parent.children.insert(leader, weak_self.clone());
818                process_group.insert(&thread_group);
819            };
820            thread_group
821        })
822    }
823
824    state_accessor!(ThreadGroup, mutable_state);
825
826    pub fn load_stopped(&self) -> StopState {
827        self.stop_state.load(Ordering::Relaxed)
828    }
829
830    /// Causes the thread group to exit.
831    ///
832    /// This marks the thread group as exiting and sends [`SIGKILL`] to its tasks to initiate
833    /// teardown. The thread group will not exist until the last task exits.
834    ///
835    /// If this is being called from a task that is part of the current thread group, the caller
836    /// should pass `current_task`. If ownership issues prevent passing `current_task`, then callers
837    /// should use [`CurrentTask::kill_thread_group()`] instead.
838    pub fn kill(&self, exit_status: ExitStatus, mut current_task: Option<&mut CurrentTask>) {
839        if let Some(ref mut current_task) = current_task {
840            current_task
841                .ptrace_event(PtraceOptions::TRACEEXIT, exit_status.signal_info_status() as u64);
842        }
843        let mut pids = self.kernel.pids.write();
844        let mut state = self.write();
845        if !state.is_running() {
846            return;
847        }
848
849        state.run_state = ThreadGroupRunState::Exiting(exit_status.clone());
850
851        // Detach from any ptraced zombie tasks.
852        let zombie_notifications = state.zombie_ptracees.detach_all(&mut pids);
853
854        // Interrupt each task. Unlock the group because send_signal will lock the group in order
855        // to call set_stopped.
856        let tasks = state.tasks();
857        drop(state);
858
859        for notification in zombie_notifications {
860            notification.deliver(&mut pids);
861        }
862        self.detach_ptracees(&mut pids);
863
864        for task in tasks {
865            task.write().set_exit_status(exit_status.clone());
866            send_standard_signal(&task, SignalInfo::kernel(SIGKILL));
867        }
868    }
869
870    pub fn add(&self, task: Arc<Task>) -> Result<(), Errno> {
871        let mut state = self.write();
872        if !state.is_running() {
873            if state.tasks_count() == 0 {
874                log_warn!(
875                    "Task {} with leader {} not running while adding its first task, \
876                not sending creation notification",
877                    task.tid,
878                    self.leader
879                );
880            }
881            return error!(EINVAL);
882        }
883        if task.tid == self.leader {
884            let _ = self.leader_task.set(Arc::downgrade(&task));
885        }
886        state.tasks.insert(task.tid, (&task).into());
887
888        Ok(())
889    }
890
891    /// Remove the task from the children of this ThreadGroup.
892    ///
893    /// It is important that the task is taken as an `Arc`. It ensures the tasks of the
894    /// ThreadGroup are always valid as they are still valid when removed.
895    pub fn remove(&self, mut pids: RwLockWriteGuard<'_, PidTable>, task: &Arc<Task>) {
896        task.set_ptrace_zombie(&mut pids);
897        pids.remove_task(task.tid);
898
899        let mut state = self.write();
900
901        let persistent_info: TaskPersistentInfo =
902            if let Some(container) = state.tasks.remove(&task.tid) {
903                container.into()
904            } else {
905                // The task has never been added. The only expected case is that this thread group
906                // is not running.
907                debug_assert!(!state.is_running());
908                return;
909            };
910
911        if state.tasks.is_empty() {
912            let exit_status = if let ThreadGroupRunState::Exiting(exit_status) = &state.run_state {
913                exit_status.clone()
914            } else {
915                let exit_status = task.exit_status().unwrap_or_else(|| {
916                    log_error!("Exiting without an exit code.");
917                    ExitStatus::Exit(u8::MAX)
918                });
919                state.set_exiting(exit_status.clone());
920                exit_status
921            };
922
923            // Detach from any ptraced zombie tasks.
924            let zombie_notifications = state.zombie_ptracees.detach_all(&mut pids);
925
926            // Replace PID table entry with a zombie.
927            let exit_info =
928                ProcessExitInfo { status: exit_status, exit_signal: state.exit_signal.clone() };
929            let zombie =
930                ZombieProcess::new(state.as_ref(), &persistent_info.real_creds(), exit_info);
931            pids.kill_process(self.leader, OwnedRef::downgrade(&zombie));
932
933            let session = state.leave_process_group(&pids);
934
935            // I have no idea if dropping the lock here is correct, and I don't want to think about
936            // it. If problems do turn up with another thread observing an intermediate state of
937            // this exit operation, the solution is to unify locks. It should be sensible and
938            // possible for there to be a single lock that protects all (or nearly all) of the
939            // data accessed by both exit and wait. In gvisor and linux this is the lock on the
940            // equivalent of the PidTable. This is made more difficult by rust locks being
941            // containers that only lock the data they contain, but see
942            // https://docs.google.com/document/d/1YHrhBqNhU1WcrsYgGAu3JwwlVmFXPlwWHTJLAbwRebY/edit
943            // for an idea.
944            std::mem::drop(state);
945
946            // `disassociate_controlling_terminal` can not be called while holding the
947            // ThreadGroup state lock.
948            session.disassociate_controlling_terminal();
949
950            for notification in zombie_notifications {
951                notification.deliver(&mut pids);
952            }
953
954            // Remove the process from the cgroup2 pid table after TG lock is dropped.
955            // This function will hold the CgroupState lock which should be before the TG lock. See
956            // more in lock_cgroup2_pid_table comments.
957            self.kernel.cgroups.lock_cgroup2_pid_table().remove_process(self.into());
958
959            self.detach_ptracees(&mut pids);
960
961            // We will need the immediate parent and the reaper. Once we have them, we can make
962            // sure to take the locks in the right order: parent before child.
963            let parent = self.read().parent.clone();
964            let reaper = self.find_reaper();
965
966            {
967                // Reparent the children.
968                if let Some(reaper) = reaper {
969                    let reaper = reaper.upgrade();
970                    {
971                        let mut reaper_state = reaper.write();
972                        // This allow_subclass is safe because we lock the reaper (an ancestor)
973                        // before locking `self` and its children. Lock ordering follows
974                        // strictly top-down traversal in the process tree, avoiding cycles.
975                        let _token = allow_subclass();
976                        let mut state = self.write();
977                        for (_pid, weak_child) in std::mem::take(&mut state.children) {
978                            if let Some(child) = weak_child.upgrade() {
979                                // This allow_subclass is safe because we lock the reaper (an
980                                // ancestor) before locking `self` and its children. Lock ordering
981                                // follows strictly top-down traversal in the process tree, avoiding
982                                // cycles.
983                                let _token = allow_subclass();
984                                let mut child_state = child.write();
985
986                                child_state.exit_signal = Some(SIGCHLD);
987                                child_state.parent =
988                                    Some(ThreadGroupParent::new(Arc::downgrade(&reaper)));
989                                reaper_state.children.insert(child.leader, weak_child.clone());
990                            }
991                        }
992                        reaper_state.zombie_children.append(&mut state.zombie_children);
993                    }
994                    ZombiePtracees::reparent(self, &reaper);
995                } else {
996                    // If we don't have a reaper then just drop the zombies.
997                    let mut state = self.write();
998                    for zombie in state.zombie_children.drain(..) {
999                        zombie.release(&mut pids);
1000                    }
1001                }
1002            }
1003
1004            // Clear the `parent` reference now that children have been re-`parent`ed.
1005            self.write().parent = None;
1006
1007            #[cfg(any(test, debug_assertions))]
1008            {
1009                let state = self.read();
1010                assert!(state.zombie_children.is_empty());
1011                assert!(state.zombie_ptracees.is_empty());
1012            }
1013
1014            if let Some(ref parent) = parent {
1015                let parent = parent.upgrade();
1016
1017                let tracer_tg = task
1018                    .read()
1019                    .ptrace
1020                    .as_ref()
1021                    .and_then(|ptrace| ptrace.core_state.thread_group.upgrade());
1022
1023                let maybe_zombie = match tracer_tg {
1024                    Some(tracer_tg) => {
1025                        tracer_tg.maybe_notify_tracer(task, &mut pids, &parent, zombie)
1026                    }
1027                    None => Some(zombie),
1028                };
1029
1030                if let Some(zombie) = maybe_zombie {
1031                    parent.do_zombie_notifications(zombie);
1032                }
1033            } else {
1034                zombie.release(&mut pids);
1035            }
1036
1037            // TODO: Set the error_code on the Zircon process object. Currently missing a way
1038            // to do this in Zircon. Might be easier in the new execution model.
1039
1040            // Once the last zircon thread stops, the zircon process will also stop executing.
1041
1042            if let Some(parent) = parent {
1043                let parent = parent.upgrade();
1044                parent.check_orphans(&pids);
1045            }
1046
1047            self.write().set_exited();
1048        }
1049    }
1050
1051    /// Detach from any ptraced tasks, killing the ones that set `PTRACE_O_EXITKILL`.
1052    fn detach_ptracees(&self, pids: &mut PidTable) {
1053        let tracee_tids = self.ptracees.lock().keys().cloned().collect_vec();
1054        for tracee_tid in tracee_tids {
1055            let Ok(tracee) = pids.get_task(tracee_tid) else {
1056                continue;
1057            };
1058
1059            let mut should_send_sigkill = false;
1060            if let Some(ptrace) = &tracee.read().ptrace {
1061                should_send_sigkill = ptrace.has_option(PtraceOptions::EXITKILL);
1062            }
1063            if should_send_sigkill {
1064                send_standard_signal(tracee.as_ref(), SignalInfo::kernel(SIGKILL));
1065            }
1066
1067            let _ = ptrace_detach(
1068                pids,
1069                PtraceTracer::Exiting(self),
1070                tracee.as_ref(),
1071                &UserAddress::NULL,
1072            );
1073        }
1074    }
1075
1076    pub fn do_zombie_notifications(&self, zombie: OwnedRef<ZombieProcess>) {
1077        let mut state = self.write();
1078
1079        state.children.remove(&zombie.pid());
1080        state
1081            .deferred_zombie_ptracers
1082            .retain(|dzp| dzp.tracee_thread_group_key != zombie.thread_group_key);
1083
1084        let exit_signal = zombie.exit_info.exit_signal;
1085        let mut signal_info = zombie.to_wait_result().as_signal_info();
1086
1087        state.zombie_children.push(zombie);
1088        state.lifecycle_waiters.notify_value(ThreadGroupLifecycleWaitValue::ChildStatus);
1089
1090        // Send signals
1091        if let Some(exit_signal) = exit_signal {
1092            signal_info.signal = exit_signal;
1093            state.send_signal(signal_info);
1094        }
1095    }
1096
1097    /// Notifies the tracer if appropriate.  Returns Some(zombie) if caller
1098    /// needs to notify the parent, None otherwise.  The caller should probably
1099    /// invoke parent.do_zombie_notifications(zombie) on the result.
1100    fn maybe_notify_tracer(
1101        &self,
1102        tracee: &Task,
1103        mut pids: &mut PidTable,
1104        parent: &ThreadGroup,
1105        zombie: OwnedRef<ZombieProcess>,
1106    ) -> Option<OwnedRef<ZombieProcess>> {
1107        let mut state = self.write();
1108        if state.zombie_ptracees.has_tracee(tracee.tid) {
1109            if self == parent {
1110                // The tracer is the parent and has not consumed the
1111                // notification.  Detach to clean up ptrace state, then
1112                // notify the parent by discarding the notification
1113                // generated by the detach and returning Some(zombie).
1114                if let Some(zombie_notification) = state.zombie_ptracees.detach(pids, tracee.tid) {
1115                    zombie_notification.discard(pids);
1116                }
1117                return Some(zombie);
1118            } else {
1119                // The tracer is not the parent and the tracer has not consumed
1120                // the notification.
1121                if !state.is_running() {
1122                    // The tracer exited concurrently. Notify the parent.
1123                    return Some(zombie);
1124                }
1125
1126                // THREAD SAFETY: Release the tracer state lock before acquiring the parent state
1127                // lock to respect parent => child lock ordering.
1128                drop(state);
1129                {
1130                    // Tell the parent to expect a notification later.
1131                    let tracee_pgid = tracee.thread_group().read().process_group.leader;
1132                    let mut parent_state = parent.write();
1133                    parent_state.deferred_zombie_ptracers.push(DeferredZombiePTracer::new(
1134                        self,
1135                        tracee,
1136                        tracee_pgid,
1137                    ));
1138                    parent_state.children.remove(&tracee.get_pid());
1139                }
1140
1141                // Tell the tracer that there is a notification pending.
1142                // THREAD SAFETY: Checking for concurrent exit with is_running(), releasing the
1143                // tracer state lock, then reacquiring the lock introduces a TOCTOU race. This
1144                // hazard is safe because exit synchronizes on the PidTable lock, which is held
1145                // continuously.
1146                let mut state = self.write();
1147                state.zombie_ptracees.set_parent_of(tracee.tid, Some(zombie), parent);
1148                tracee.write().notify_ptracers();
1149                return None;
1150            }
1151        } else if self == parent {
1152            // The tracer is the parent and has already consumed the parent
1153            // notification.  No further action required.
1154            parent.write().children.remove(&tracee.tid);
1155            zombie.release(&mut pids);
1156            return None;
1157        }
1158        // The tracer is not the parent and has already consumed the parent
1159        // notification.  Notify the parent.
1160        Some(zombie)
1161    }
1162
1163    /// Find the task which will adopt our children after we die.
1164    fn find_reaper(&self) -> Option<ThreadGroupParent> {
1165        let mut weak_parent = self.read().parent.clone()?;
1166        loop {
1167            weak_parent = {
1168                let parent = weak_parent.upgrade();
1169                let parent_state = parent.read();
1170                if parent_state.is_child_subreaper {
1171                    break;
1172                }
1173                match parent_state.parent {
1174                    Some(ref next_parent) => next_parent.clone(),
1175                    None => break,
1176                }
1177            };
1178        }
1179        Some(weak_parent)
1180    }
1181
1182    pub fn setsid(&self) -> Result<(), Errno> {
1183        let pids = self.kernel.pids.read();
1184        if pids.get_process_group(self.leader).is_some() {
1185            return error!(EPERM);
1186        }
1187        let process_group = ProcessGroup::new(self.leader, None);
1188        pids.add_process_group(process_group.clone());
1189        let session = self.write().set_process_group(process_group, &pids);
1190        session.disassociate_controlling_terminal();
1191        self.check_orphans(&pids);
1192
1193        Ok(())
1194    }
1195
1196    pub fn setpgid(
1197        &self,
1198        current_task: &CurrentTask,
1199        target: &Task,
1200        pgid: pid_t,
1201    ) -> Result<(), Errno> {
1202        let pids = self.kernel.pids.read();
1203
1204        {
1205            let current_process_group = Arc::clone(&self.read().process_group);
1206
1207            // The target process must be either the current process of a child of the current process
1208            let mut target_thread_group = target.thread_group().write();
1209            let is_target_current_process_child =
1210                target_thread_group.parent.as_ref().map(|tg| tg.upgrade().leader)
1211                    == Some(self.leader);
1212            if target_thread_group.leader() != self.leader && !is_target_current_process_child {
1213                return error!(ESRCH);
1214            }
1215
1216            // If the target process is a child of the current task, it must not have executed one of the exec
1217            // function.
1218            if is_target_current_process_child && target_thread_group.did_exec {
1219                return error!(EACCES);
1220            }
1221
1222            let new_process_group;
1223            {
1224                let target_process_group = &target_thread_group.process_group;
1225
1226                // The target process must not be a session leader and must be in the same session as the current process.
1227                if target_thread_group.leader() == target_process_group.session.leader
1228                    || current_process_group.session != target_process_group.session
1229                {
1230                    return error!(EPERM);
1231                }
1232
1233                let target_pgid = if pgid == 0 { target_thread_group.leader() } else { pgid };
1234                if target_pgid < 0 {
1235                    return error!(EINVAL);
1236                }
1237
1238                if target_pgid == target_process_group.leader {
1239                    return Ok(());
1240                }
1241
1242                // If pgid is not equal to the target process id, the associated process group must exist
1243                // and be in the same session as the target process.
1244                if target_pgid != target_thread_group.leader() {
1245                    new_process_group =
1246                        pids.get_process_group(target_pgid).ok_or_else(|| errno!(EPERM))?;
1247                    if new_process_group.session != target_process_group.session {
1248                        return error!(EPERM);
1249                    }
1250                    security::check_setpgid_access(current_task, target)?;
1251                } else {
1252                    security::check_setpgid_access(current_task, target)?;
1253                    // Create a new process group
1254                    new_process_group =
1255                        ProcessGroup::new(target_pgid, Some(target_process_group.session.clone()));
1256                    pids.add_process_group(new_process_group.clone());
1257                }
1258            }
1259
1260            let session = target_thread_group.set_process_group(new_process_group, &pids);
1261            std::mem::drop(target_thread_group);
1262            // `disassociate_controlling_terminal` can not be called while holding the
1263            // ThreadGroup state lock.
1264            session.disassociate_controlling_terminal();
1265        }
1266
1267        target.thread_group().check_orphans(&pids);
1268
1269        Ok(())
1270    }
1271
1272    fn itimer_real(&self) -> IntervalTimerHandle {
1273        self.timers.itimer_real()
1274    }
1275
1276    pub fn set_itimer(
1277        &self,
1278        current_task: &CurrentTask,
1279        which: u32,
1280        value: itimerval,
1281    ) -> Result<itimerval, Errno> {
1282        if which == ITIMER_PROF || which == ITIMER_VIRTUAL {
1283            // We don't support setting these timers.
1284            // The gvisor test suite clears ITIMER_PROF as part of its test setup logic, so we support
1285            // clearing these values.
1286            if value.it_value.tv_sec == 0 && value.it_value.tv_usec == 0 {
1287                return Ok(itimerval::default());
1288            }
1289            track_stub!(TODO("https://fxbug.dev/322874521"), "Unsupported itimer type", which);
1290            return error!(ENOTSUP);
1291        }
1292
1293        if which != ITIMER_REAL {
1294            return error!(EINVAL);
1295        }
1296        let itimer_real = self.itimer_real();
1297        let prev_remaining = itimer_real.time_remaining();
1298        if value.it_value.tv_sec != 0 || value.it_value.tv_usec != 0 {
1299            itimer_real.arm(current_task, itimerspec_from_itimerval(value), false)?;
1300        } else {
1301            itimer_real.disarm(current_task)?;
1302        }
1303        Ok(itimerval {
1304            it_value: timeval_from_duration(prev_remaining.remainder),
1305            it_interval: timeval_from_duration(prev_remaining.interval),
1306        })
1307    }
1308
1309    pub fn get_itimer(&self, which: u32) -> Result<itimerval, Errno> {
1310        if which == ITIMER_PROF || which == ITIMER_VIRTUAL {
1311            // We don't support setting these timers, so we can accurately report that these are not set.
1312            return Ok(itimerval::default());
1313        }
1314        if which != ITIMER_REAL {
1315            return error!(EINVAL);
1316        }
1317        let remaining = self.itimer_real().time_remaining();
1318        Ok(itimerval {
1319            it_value: timeval_from_duration(remaining.remainder),
1320            it_interval: timeval_from_duration(remaining.interval),
1321        })
1322    }
1323
1324    /// Check whether the stop state is compatible with `new_stopped`. If it is return it,
1325    /// otherwise, return None.
1326    fn check_stopped_state(
1327        &self,
1328        new_stopped: StopState,
1329        finalize_only: bool,
1330    ) -> Option<StopState> {
1331        let stopped = self.load_stopped();
1332        if finalize_only && !stopped.is_stopping_or_stopped() {
1333            return Some(stopped);
1334        }
1335
1336        if stopped.is_illegal_transition(new_stopped) {
1337            return Some(stopped);
1338        }
1339
1340        return None;
1341    }
1342
1343    /// Set the stop status of the process.  If you pass |siginfo| of |None|,
1344    /// does not update the signal.  If |finalize_only| is set, will check that
1345    /// the set will be a finalize (Stopping -> Stopped or Stopped -> Stopped)
1346    /// before executing it.
1347    ///
1348    /// Returns the latest stop state after any changes.
1349    pub fn set_stopped(
1350        &self,
1351        new_stopped: StopState,
1352        siginfo: Option<SignalInfo>,
1353        finalize_only: bool,
1354    ) -> StopState {
1355        // Perform an early return check to see if we can avoid taking the lock.
1356        if let Some(stopped) = self.check_stopped_state(new_stopped, finalize_only) {
1357            return stopped;
1358        }
1359
1360        self.write().set_stopped(new_stopped, siginfo, finalize_only)
1361    }
1362
1363    /// Ensures |session| is the controlling session inside of |terminal_controller|, and returns a
1364    /// reference to the |TerminalController|.
1365    fn check_terminal_controller(
1366        session: &Arc<Session>,
1367        terminal_controller: &Option<TerminalController>,
1368    ) -> Result<(), Errno> {
1369        if let Some(terminal_controller) = terminal_controller {
1370            if let Some(terminal_session) = terminal_controller.session.upgrade() {
1371                if Arc::ptr_eq(session, &terminal_session) {
1372                    return Ok(());
1373                }
1374            }
1375        }
1376        error!(ENOTTY)
1377    }
1378
1379    pub fn get_foreground_process_group(&self, terminal: &Terminal) -> Result<pid_t, Errno> {
1380        let state = self.read();
1381        let process_group = &state.process_group;
1382        let terminal_state = terminal.read();
1383
1384        // "When fd does not refer to the controlling terminal of the calling
1385        // process, -1 is returned" - tcgetpgrp(3)
1386        Self::check_terminal_controller(&process_group.session, &terminal_state.controller)?;
1387        let pid = process_group.session.read().get_foreground_process_group_leader();
1388        Ok(pid)
1389    }
1390
1391    pub fn set_foreground_process_group(
1392        &self,
1393        current_task: &CurrentTask,
1394        terminal: &Terminal,
1395        pgid: pid_t,
1396    ) -> Result<(), Errno> {
1397        let process_group;
1398        let send_ttou;
1399        {
1400            // Keep locks to ensure atomicity.
1401            let pids = self.kernel.pids.read();
1402            let state = self.read();
1403            process_group = Arc::clone(&state.process_group);
1404            let terminal_state = terminal.read();
1405            Self::check_terminal_controller(&process_group.session, &terminal_state.controller)?;
1406
1407            // pgid must be positive.
1408            if pgid < 0 {
1409                return error!(EINVAL);
1410            }
1411
1412            let new_process_group = pids.get_process_group(pgid).ok_or_else(|| errno!(ESRCH))?;
1413            if new_process_group.session != process_group.session {
1414                return error!(EPERM);
1415            }
1416
1417            let mut session_state = process_group.session.write();
1418            // If the calling process is a member of a background group and not ignoring SIGTTOU, a
1419            // SIGTTOU signal is sent to all members of this background process group.
1420            send_ttou = process_group.leader != session_state.get_foreground_process_group_leader()
1421                && !current_task.read().signal_mask().has_signal(SIGTTOU)
1422                && self.signal_actions.get(SIGTTOU).sa_handler != SIG_IGN;
1423
1424            if !send_ttou {
1425                session_state.set_foreground_process_group(&new_process_group);
1426            }
1427        }
1428
1429        // Locks must not be held when sending signals.
1430        if send_ttou {
1431            process_group.send_signals(&[SIGTTOU]);
1432            return error!(EINTR);
1433        }
1434
1435        Ok(())
1436    }
1437
1438    pub fn set_controlling_terminal(
1439        &self,
1440        current_task: &CurrentTask,
1441        terminal: &Terminal,
1442        is_main: bool,
1443        steal: bool,
1444        is_readable: bool,
1445    ) -> Result<(), Errno> {
1446        // Keep locks to ensure atomicity.
1447        let state = self.read();
1448        let process_group = &state.process_group;
1449        let mut terminal_state = terminal.write();
1450
1451        // It might be necessary to lock the existing session, to steal the terminal
1452        // for it. Because of ordering requirement, it must be locked now.
1453        let other_session = terminal_state.controller.as_ref().and_then(|cs| cs.session.upgrade());
1454        let (mut session_writer, other_session) =
1455            if let Some(other_session) = other_session.as_ref() {
1456                if *other_session == process_group.session {
1457                    (process_group.session.mutable_state.write(), None)
1458                } else {
1459                    let (session_writer, other_session_writer) = ordered_write_lock(
1460                        &process_group.session.mutable_state,
1461                        &other_session.mutable_state,
1462                    );
1463                    (session_writer, Some((other_session, other_session_writer)))
1464                }
1465            } else {
1466                (process_group.session.mutable_state.write(), None)
1467            };
1468
1469        // "The calling process must be a session leader and not have a
1470        // controlling terminal already." - tty_ioctl(4)
1471        if process_group.session.leader != self.leader {
1472            return error!(EINVAL);
1473        }
1474        if let Some(ref current_ct) = session_writer.controlling_terminal {
1475            if current_ct.matches(terminal, is_main) {
1476                return Ok(());
1477            } else {
1478                return error!(EINVAL);
1479            }
1480        }
1481
1482        let mut has_admin_capability_determined = false;
1483
1484        // "If this terminal is already the controlling terminal of a different
1485        // session group, then the ioctl fails with EPERM, unless the caller
1486        // has the CAP_SYS_ADMIN capability and arg equals 1, in which case the
1487        // terminal is stolen, and all processes that had it as controlling
1488        // terminal lose it." - tty_ioctl(4)
1489        if let Some((other_session, mut other_session_writer)) = other_session {
1490            debug_assert!(*other_session != process_group.session);
1491            if !steal {
1492                return error!(EPERM);
1493            }
1494            security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1495            has_admin_capability_determined = true;
1496
1497            // Steal the TTY away. Unlike TIOCNOTTY, don't send signals.
1498            other_session_writer.controlling_terminal = None;
1499        }
1500
1501        if !is_readable && !has_admin_capability_determined {
1502            security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1503        }
1504
1505        session_writer.controlling_terminal = Some(ControllingTerminal::new(terminal, is_main));
1506        terminal_state.controller = TerminalController::new(&process_group.session);
1507        Ok(())
1508    }
1509
1510    pub fn release_controlling_terminal(
1511        &self,
1512        _current_task: &CurrentTask,
1513        terminal: &Terminal,
1514        is_main: bool,
1515    ) -> Result<(), Errno> {
1516        let process_group;
1517        {
1518            // Keep locks to ensure atomicity.
1519            let state = self.read();
1520            process_group = Arc::clone(&state.process_group);
1521            let mut terminal_state = terminal.write();
1522            let mut session_writer = process_group.session.write();
1523
1524            // tty must be the controlling terminal.
1525            Self::check_terminal_controller(&process_group.session, &terminal_state.controller)?;
1526            if !session_writer
1527                .controlling_terminal
1528                .as_ref()
1529                .map_or(false, |ct| ct.matches(terminal, is_main))
1530            {
1531                return error!(ENOTTY);
1532            }
1533
1534            // "If the process was session leader, then send SIGHUP and SIGCONT to the foreground
1535            // process group and all processes in the current session lose their controlling terminal."
1536            // - tty_ioctl(4)
1537
1538            // Remove tty as the controlling tty for each process in the session, then
1539            // send them SIGHUP and SIGCONT.
1540
1541            session_writer.controlling_terminal = None;
1542            terminal_state.controller = None;
1543        }
1544
1545        if process_group.session.leader == self.leader {
1546            process_group.send_signals(&[SIGHUP, SIGCONT]);
1547        }
1548
1549        Ok(())
1550    }
1551
1552    fn check_orphans(&self, pids: &PidTable) {
1553        let mut thread_groups = self.read().children().collect::<Vec<_>>();
1554        let this = self.weak_self.upgrade().unwrap();
1555        thread_groups.push(this);
1556        let process_groups =
1557            thread_groups.iter().map(|tg| Arc::clone(&tg.read().process_group)).unique();
1558        for pg in process_groups {
1559            pg.check_orphaned(pids);
1560        }
1561    }
1562
1563    pub fn get_rlimit(&self, resource: Resource) -> u64 {
1564        self.limits.lock().get(resource).rlim_cur
1565    }
1566
1567    /// Adjusts the rlimits of the ThreadGroup to which `target_task` belongs to.
1568    pub fn adjust_rlimits(
1569        current_task: &CurrentTask,
1570        target_task: &Task,
1571        resource: Resource,
1572        maybe_new_limit: Option<rlimit>,
1573    ) -> Result<rlimit, Errno> {
1574        let thread_group = target_task.thread_group();
1575        let mut limit_state = thread_group.limits.lock();
1576        let old_limit = limit_state.get(resource);
1577        if let Some(new_limit) = maybe_new_limit {
1578            if new_limit.rlim_max > old_limit.rlim_max
1579                && !security::is_task_capable_noaudit(current_task, CAP_SYS_RESOURCE)
1580            {
1581                return error!(EPERM);
1582            }
1583            security::task_setrlimit(current_task, &target_task, old_limit, new_limit)?;
1584            limit_state.set(resource, new_limit)
1585        }
1586        Ok(old_limit)
1587    }
1588
1589    pub fn time_stats(&self) -> TaskTimeStats {
1590        let process: &zx::Process = if self.process.as_handle_ref().is_invalid() {
1591            // `process` must be valid for all tasks, except `kthreads`. In that case get the
1592            // stats from starnix process.
1593            assert_eq!(
1594                self as *const ThreadGroup,
1595                Arc::as_ptr(&self.kernel.kthreads.system_thread_group())
1596            );
1597            &self.kernel.kthreads.starnix_process
1598        } else {
1599            &self.process
1600        };
1601
1602        let info =
1603            zx::Task::get_runtime_info(process).expect("Failed to get starnix process stats");
1604        TaskTimeStats {
1605            user_time: zx::MonotonicDuration::from_nanos(info.cpu_time),
1606            // TODO(https://fxbug.dev/42078242): How can we calculate system time?
1607            system_time: zx::MonotonicDuration::default(),
1608        }
1609    }
1610
1611    /// For each task traced by this thread_group that matches the given
1612    /// selector, acquire its TaskMutableState and ptracees lock and execute the
1613    /// given function.
1614    pub fn get_ptracees_and(
1615        &self,
1616        selector: &ProcessSelector,
1617        pids: &PidTable,
1618        f: &mut dyn FnMut(&Task, &TaskMutableState),
1619    ) {
1620        for tracee in self
1621            .ptracees
1622            .lock()
1623            .keys()
1624            .filter(|tracee_tid| selector.match_tid(**tracee_tid, &pids))
1625            .map(|tracee_tid| pids.get_task(*tracee_tid))
1626        {
1627            if let Ok(task_ref) = tracee {
1628                let task_state = task_ref.write();
1629                if task_state.ptrace.is_some() {
1630                    f(&task_ref, &task_state);
1631                }
1632            }
1633        }
1634    }
1635
1636    /// Returns a tracee whose state has changed, so that waitpid can report on
1637    /// it. If this returns a value, and the pid is being traced, the tracer
1638    /// thread is deemed to have seen the tracee ptrace-stop for the purposes of
1639    /// PTRACE_LISTEN.
1640    pub fn get_waitable_ptracee(
1641        &self,
1642        selector: &ProcessSelector,
1643        options: &WaitingOptions,
1644        pids: &mut PidTable,
1645    ) -> Option<WaitResult> {
1646        // This checks to see if the target is a zombie ptracee.
1647        let waitable_entry = self.write().zombie_ptracees.get_waitable_entry(selector, options);
1648        match waitable_entry {
1649            None => (),
1650            Some((zombie, None)) => return Some(zombie.to_wait_result()),
1651            Some((zombie, Some((tg, z)))) => {
1652                if let Some(tg) = tg.upgrade() {
1653                    if Arc::as_ptr(&tg) != self as *const Self {
1654                        tg.do_zombie_notifications(z);
1655                    } else {
1656                        {
1657                            let mut state = tg.write();
1658                            state.children.remove(&z.pid());
1659                            state
1660                                .deferred_zombie_ptracers
1661                                .retain(|dzp| dzp.tracee_thread_group_key != z.thread_group_key);
1662                        }
1663
1664                        z.release(pids);
1665                    };
1666                }
1667                return Some(zombie.to_wait_result());
1668            }
1669        }
1670
1671        let mut tasks = vec![];
1672
1673        // This checks to see if the target is a running ptracee.
1674        self.get_ptracees_and(selector, pids, &mut |task: &Task, _| {
1675            tasks.push(task.weak_self.clone());
1676        });
1677        for task in tasks {
1678            let Some(task_ref) = task.upgrade() else {
1679                continue;
1680            };
1681
1682            let process_state = &mut task_ref.thread_group().write();
1683            let mut task_state = task_ref.write();
1684            if task_state
1685                .ptrace
1686                .as_ref()
1687                .is_some_and(|ptrace| ptrace.is_waitable(task_ref.load_stopped(), options))
1688            {
1689                // We've identified a potential target.  Need to return either
1690                // the process's information (if we are in group-stop) or the
1691                // thread's information (if we are in a different stop).
1692
1693                // The shared information:
1694                let mut pid: i32 = 0;
1695                let info = process_state.tasks.values().next().unwrap().info().clone();
1696                let uid = info.real_creds().uid;
1697                let mut exit_status = None;
1698                let exit_signal = process_state.exit_signal.clone();
1699                let time_stats =
1700                    process_state.base.time_stats() + process_state.children_time_stats;
1701                let task_stopped = task_ref.load_stopped();
1702
1703                #[derive(PartialEq)]
1704                enum ExitType {
1705                    None,
1706                    Cont,
1707                    Stop,
1708                    Kill,
1709                }
1710                if process_state.is_waitable() {
1711                    let ptrace = &mut task_state.ptrace;
1712                    // The information for processes, if we were in group stop.
1713                    let process_stopped = process_state.base.load_stopped();
1714                    let mut fn_type = ExitType::None;
1715                    if process_stopped == StopState::Awake && options.wait_for_continued {
1716                        fn_type = ExitType::Cont;
1717                    }
1718                    let mut event = ptrace
1719                        .as_ref()
1720                        .map_or(PtraceEvent::None, |ptrace| {
1721                            ptrace.event_data.as_ref().map_or(PtraceEvent::None, |data| data.event)
1722                        })
1723                        .clone();
1724                    // Tasks that are ptrace'd always get stop notifications.
1725                    if process_stopped == StopState::GroupStopped
1726                        && (options.wait_for_stopped || ptrace.is_some())
1727                    {
1728                        fn_type = ExitType::Stop;
1729                    }
1730                    if fn_type != ExitType::None {
1731                        let siginfo = if options.keep_waitable_state {
1732                            process_state.last_signal.clone()
1733                        } else {
1734                            process_state.last_signal.take()
1735                        };
1736                        if let Some(mut siginfo) = siginfo {
1737                            if task_ref.thread_group().load_stopped() == StopState::GroupStopped
1738                                && ptrace.as_ref().is_some_and(|ptrace| ptrace.is_seized())
1739                            {
1740                                if event == PtraceEvent::None {
1741                                    event = PtraceEvent::Stop;
1742                                }
1743                                siginfo.code |= (PtraceEvent::Stop as i32) << 8;
1744                            }
1745                            if siginfo.signal == SIGKILL {
1746                                fn_type = ExitType::Kill;
1747                            }
1748                            exit_status = match fn_type {
1749                                ExitType::Stop => Some(ExitStatus::Stop(siginfo, event)),
1750                                ExitType::Cont => Some(ExitStatus::Continue(siginfo, event)),
1751                                ExitType::Kill => Some(ExitStatus::Kill(siginfo)),
1752                                _ => None,
1753                            };
1754                        }
1755                        // Clear the wait status of the ptrace, because we're
1756                        // using the tg status instead.
1757                        ptrace
1758                            .as_mut()
1759                            .map(|ptrace| ptrace.get_last_signal(options.keep_waitable_state));
1760                    }
1761                    pid = process_state.base.leader;
1762                }
1763                if exit_status == None {
1764                    if let Some(ptrace) = task_state.ptrace.as_mut() {
1765                        // The information for the task, if we were in a non-group stop.
1766                        let mut fn_type = ExitType::None;
1767                        let event = ptrace
1768                            .event_data
1769                            .as_ref()
1770                            .map_or(PtraceEvent::None, |event| event.event);
1771                        if task_stopped == StopState::Awake {
1772                            fn_type = ExitType::Cont;
1773                        }
1774                        if task_stopped.is_stopping_or_stopped()
1775                            || ptrace.stop_status == PtraceStatus::Listening
1776                        {
1777                            fn_type = ExitType::Stop;
1778                        }
1779                        if fn_type != ExitType::None {
1780                            if let Some(siginfo) =
1781                                ptrace.get_last_signal(options.keep_waitable_state)
1782                            {
1783                                if siginfo.signal == SIGKILL {
1784                                    fn_type = ExitType::Kill;
1785                                }
1786                                exit_status = match fn_type {
1787                                    ExitType::Stop => Some(ExitStatus::Stop(siginfo, event)),
1788                                    ExitType::Cont => Some(ExitStatus::Continue(siginfo, event)),
1789                                    ExitType::Kill => Some(ExitStatus::Kill(siginfo)),
1790                                    _ => None,
1791                                };
1792                            }
1793                        }
1794                        pid = task_ref.get_tid();
1795                    }
1796                }
1797                if let Some(exit_status) = exit_status {
1798                    return Some(WaitResult {
1799                        pid,
1800                        uid,
1801                        exit_info: ProcessExitInfo { status: exit_status, exit_signal },
1802                        time_stats,
1803                    });
1804                }
1805            }
1806        }
1807        None
1808    }
1809
1810    /// Attempts to send an unchecked signal to this thread group.
1811    ///
1812    /// - `current_task`: The task that is sending the signal.
1813    /// - `unchecked_signal`: The signal that is to be sent. Unchecked, since `0` is a sentinel value
1814    /// where rights are to be checked but no signal is actually sent.
1815    ///
1816    /// # Returns
1817    /// Returns Ok(()) if the signal was sent, or the permission checks passed with a 0 signal, otherwise
1818    /// the error that was encountered.
1819    pub fn send_signal_unchecked(
1820        &self,
1821        current_task: &CurrentTask,
1822        unchecked_signal: UncheckedSignal,
1823    ) -> Result<(), Errno> {
1824        if let Some(signal) = self.check_signal_access(current_task, unchecked_signal)? {
1825            let signal_info = SignalInfo::with_detail(
1826                signal,
1827                SI_USER as i32,
1828                SignalDetail::Kill {
1829                    pid: current_task.thread_group().leader,
1830                    uid: current_task.current_creds().uid,
1831                },
1832            );
1833
1834            self.write().send_signal(signal_info);
1835        }
1836
1837        Ok(())
1838    }
1839
1840    /// Sends a signal to this thread_group without performing any access checks.
1841    ///
1842    /// # Safety
1843    /// This is unsafe, because it should only be called by tools and tests.
1844    pub unsafe fn send_signal_unchecked_debug(
1845        &self,
1846        current_task: &CurrentTask,
1847        unchecked_signal: UncheckedSignal,
1848    ) -> Result<(), Errno> {
1849        let signal = Signal::try_from(unchecked_signal)?;
1850        let signal_info = SignalInfo::with_detail(
1851            signal,
1852            SI_USER as i32,
1853            SignalDetail::Kill {
1854                pid: current_task.thread_group().leader,
1855                uid: current_task.current_creds().uid,
1856            },
1857        );
1858
1859        self.write().send_signal(signal_info);
1860        Ok(())
1861    }
1862
1863    /// Attempts to send an unchecked signal to this thread group, with info read from
1864    /// `siginfo_ref`.
1865    ///
1866    /// - `current_task`: The task that is sending the signal.
1867    /// - `unchecked_signal`: The signal that is to be sent. Unchecked, since `0` is a sentinel value
1868    /// where rights are to be checked but no signal is actually sent.
1869    /// - `siginfo_ref`: The siginfo that will be enqueued.
1870    /// - `options`: Options for how to convert the siginfo into a signal info.
1871    ///
1872    /// # Returns
1873    /// Returns Ok(()) if the signal was sent, or the permission checks passed with a 0 signal, otherwise
1874    /// the error that was encountered.
1875    #[track_caller]
1876    pub fn send_signal_unchecked_with_info(
1877        &self,
1878        current_task: &CurrentTask,
1879        unchecked_signal: UncheckedSignal,
1880        siginfo_ref: UserAddress,
1881        options: IntoSignalInfoOptions,
1882    ) -> Result<(), Errno> {
1883        if let Some(signal) = self.check_signal_access(current_task, unchecked_signal)? {
1884            let siginfo = UncheckedSignalInfo::read_from_siginfo(current_task, siginfo_ref)?;
1885            if self.leader != current_task.get_pid()
1886                && (siginfo.code() >= 0 || siginfo.code() == SI_TKILL)
1887            {
1888                return error!(EPERM);
1889            }
1890
1891            self.write().send_signal(siginfo.into_signal_info(signal, options)?);
1892        }
1893
1894        Ok(())
1895    }
1896
1897    /// Checks whether or not `current_task` can signal this thread group with `unchecked_signal`.
1898    ///
1899    /// Returns:
1900    ///   - `Ok(Some(Signal))` if the signal passed checks and should be sent.
1901    ///   - `Ok(None)` if the signal passed checks, but should not be sent. This is used by
1902    ///   userspace for permission checks.
1903    ///   - `Err(_)` if the permission checks failed.
1904    fn check_signal_access(
1905        &self,
1906        current_task: &CurrentTask,
1907        unchecked_signal: UncheckedSignal,
1908    ) -> Result<Option<Signal>, Errno> {
1909        // Pick an arbitrary task in thread_group to check permissions.
1910        //
1911        // Tasks can technically have different credentials, but in practice they are kept in sync.
1912        let target_task = self.read().get_any_task()?;
1913        current_task.can_signal(&target_task, unchecked_signal)?;
1914
1915        // 0 is a sentinel value used to do permission checks.
1916        if unchecked_signal.is_zero() {
1917            return Ok(None);
1918        }
1919
1920        let signal = Signal::try_from(unchecked_signal)?;
1921        security::check_signal_access(current_task, &target_task, signal)?;
1922
1923        Ok(Some(signal))
1924    }
1925
1926    pub fn has_signal_queued(&self, signal: Signal) -> bool {
1927        self.pending_signals.lock().has_queued(signal)
1928    }
1929
1930    pub fn num_signals_queued(&self) -> usize {
1931        self.pending_signals.lock().num_queued()
1932    }
1933
1934    pub fn get_pending_signals(&self) -> SigSet {
1935        self.pending_signals.lock().pending()
1936    }
1937
1938    pub fn is_any_signal_allowed_by_mask(&self, mask: SigSet) -> bool {
1939        self.pending_signals.lock().is_any_allowed_by_mask(mask)
1940    }
1941
1942    pub fn take_next_signal_where<F>(&self, predicate: F) -> Option<SignalInfo>
1943    where
1944        F: Fn(&SignalInfo) -> bool,
1945    {
1946        let mut signals = self.pending_signals.lock();
1947        let r = signals.take_next_where(predicate);
1948        self.has_pending_signals.store(!signals.is_empty(), Ordering::Relaxed);
1949        r
1950    }
1951
1952    /// Drive this `ThreadGroup` to exit, allowing it time to handle SIGTERM before sending SIGKILL.
1953    ///
1954    /// Returns once `ThreadGroup::exit()` has completed.
1955    ///
1956    /// Must be called from the system task.
1957    pub async fn shut_down(this: Weak<Self>) {
1958        const SHUTDOWN_SIGNAL_HANDLING_TIMEOUT: zx::MonotonicDuration =
1959            zx::MonotonicDuration::from_seconds(1);
1960
1961        // Prepare for shutting down the thread group.
1962        let (tg_name, mut on_exited) = {
1963            // Nest this upgraded access so upgraded references aren't held across await-points.
1964            let Some(this) = this.upgrade() else {
1965                return;
1966            };
1967
1968            let mut state = this.write();
1969            if state.is_exited() {
1970                // Do not set an exit notifier on an exited thread group. It will never be notified.
1971                return;
1972            }
1973
1974            // Register a channel to be notified when exit() is complete.
1975            let (on_exited_send, on_exited) = futures::channel::oneshot::channel();
1976            state.exit_notifier = Some(on_exited_send);
1977
1978            // We want to be able to log about this thread group without upgrading the `Weak`.
1979            let tg_name = format!("{this:?}");
1980
1981            (tg_name, on_exited)
1982        };
1983
1984        log_debug!(tg:% = tg_name; "shutting down thread group, sending SIGTERM");
1985        this.upgrade().map(|tg| tg.write().send_signal(SignalInfo::kernel(SIGTERM)));
1986
1987        // Give thread groups some time to handle SIGTERM, proceeding early if they exit
1988        let timeout = fuchsia_async::Timer::new(SHUTDOWN_SIGNAL_HANDLING_TIMEOUT);
1989        futures::pin_mut!(timeout);
1990
1991        // Use select_biased instead of on_timeout() so that we can await on on_exited later
1992        futures::select_biased! {
1993            _ = &mut on_exited => (),
1994            _ = timeout => {
1995                log_debug!(tg:% = tg_name; "sending SIGKILL");
1996                this.upgrade().map(|tg| tg.write().send_signal(SignalInfo::kernel(SIGKILL)));
1997            },
1998        };
1999
2000        log_debug!(tg:% = tg_name; "waiting for exit");
2001        // It doesn't matter whether ThreadGroup::exit() was called or the process exited with
2002        // a return code and dropped the sender end of the channel.
2003        on_exited.await.ok();
2004        log_debug!(tg:% = tg_name; "thread group shutdown complete");
2005    }
2006
2007    /// Returns the KOID of the process for this thread group.
2008    /// This method should be used to when mapping 32 bit linux process ids to KOIDs
2009    /// to avoid breaking the encapsulation of the zx::process within the ThreadGroup.
2010    /// This encapsulation is important since the relationship between the ThreadGroup
2011    /// and the Process may change over time. See [ThreadGroup::process] for more details.
2012    pub fn get_process_koid(&self) -> Result<Koid, Status> {
2013        self.process.koid()
2014    }
2015}
2016
2017pub enum WaitableChildResult {
2018    ReadyNow(Box<WaitResult>),
2019    ShouldWait,
2020    NoneFound,
2021}
2022
2023#[apply(state_implementation!)]
2024impl ThreadGroupMutableState<Base = ThreadGroup> {
2025    pub fn leader(&self) -> pid_t {
2026        self.base.leader
2027    }
2028
2029    pub fn leader_command(&self) -> TaskCommand {
2030        self.get_task(self.leader())
2031            .map(|l| l.command())
2032            .unwrap_or_else(|| TaskCommand::new(b"<leader exited>"))
2033    }
2034
2035    pub fn is_running(&self) -> bool {
2036        matches!(self.run_state, ThreadGroupRunState::Running)
2037    }
2038
2039    pub fn is_exited(&self) -> bool {
2040        matches!(self.run_state, ThreadGroupRunState::Exited(_))
2041    }
2042
2043    fn set_exiting(&mut self, exit_status: ExitStatus) {
2044        self.run_state = ThreadGroupRunState::Exiting(exit_status);
2045    }
2046
2047    fn set_exited(&mut self) {
2048        let ThreadGroupRunState::Exiting(exit_status) = std::mem::take(&mut self.run_state) else {
2049            panic!("Must transition from Exiting to Exited");
2050        };
2051        self.run_state = ThreadGroupRunState::Exited(exit_status);
2052
2053        if let Some(notifier) = self.exit_notifier.take() {
2054            let _ = notifier.send(());
2055        }
2056    }
2057
2058    pub fn children(&self) -> impl Iterator<Item = Arc<ThreadGroup>> + '_ {
2059        self.children.values().map(|v| {
2060            v.upgrade().expect("Weak references to processes in ThreadGroup must always be valid")
2061        })
2062    }
2063
2064    pub fn tasks(&self) -> Vec<Arc<Task>> {
2065        self.tasks.values().flat_map(|t| t.upgrade()).collect()
2066    }
2067
2068    pub fn task_ids(&self) -> impl Iterator<Item = &tid_t> {
2069        self.tasks.keys()
2070    }
2071
2072    pub fn contains_task(&self, tid: tid_t) -> bool {
2073        self.tasks.contains_key(&tid)
2074    }
2075
2076    pub fn get_task(&self, tid: tid_t) -> Option<Arc<Task>> {
2077        self.tasks.get(&tid).and_then(|t| t.upgrade())
2078    }
2079
2080    pub fn tasks_count(&self) -> usize {
2081        self.tasks.len()
2082    }
2083
2084    pub fn get_ppid(&self) -> pid_t {
2085        match &self.parent {
2086            Some(parent) => parent.upgrade().leader,
2087            None => 0,
2088        }
2089    }
2090
2091    /// Changes the process group of the thread group.
2092    ///
2093    /// Returns a `SessionDisassociation`, which the caller must use to explicitly
2094    /// disassociate the controlling terminal if the thread group was previously a session
2095    /// leader.
2096    /// This must be done after the ThreadGroup state lock is released to avoid lock order
2097    /// violations.
2098    fn set_process_group(
2099        &mut self,
2100        process_group: Arc<ProcessGroup>,
2101        pids: &PidTable,
2102    ) -> SessionDisassociation {
2103        if self.process_group == process_group {
2104            return SessionDisassociation::new(None);
2105        }
2106        let session = self.leave_process_group(pids);
2107        self.process_group = process_group;
2108        self.process_group.insert(self.base);
2109        session
2110    }
2111
2112    /// Removes the thread group from its current process group.
2113    ///
2114    /// Returns a `SessionDisassociation`, which the caller must use to explicitly
2115    /// disassociate the controlling terminal if the thread group was previously a session
2116    /// leader.
2117    /// This must be done after the ThreadGroup state lock is released to avoid lock order
2118    /// violations.
2119    fn leave_process_group(&mut self, pids: &PidTable) -> SessionDisassociation {
2120        let (is_empty, disassociation) = self.process_group.remove(self.base);
2121        if is_empty {
2122            self.process_group.session.write().remove(self.process_group.leader);
2123            pids.remove_process_group(self.process_group.leader);
2124        }
2125        disassociation
2126    }
2127
2128    /// Indicates whether the thread group is waitable via waitid and waitpid for
2129    /// either WSTOPPED or WCONTINUED.
2130    pub fn is_waitable(&self) -> bool {
2131        return self.last_signal.is_some() && !self.base.load_stopped().is_in_progress();
2132    }
2133
2134    pub fn get_waitable_zombie(
2135        &mut self,
2136        zombie_list: &dyn Fn(&mut ThreadGroupMutableState) -> &mut Vec<OwnedRef<ZombieProcess>>,
2137        selector: &ProcessSelector,
2138        options: &WaitingOptions,
2139        pids: &mut PidTable,
2140    ) -> Option<WaitResult> {
2141        // We look for the last zombie in the vector that matches pid selector and waiting options
2142        let selected_zombie_position = zombie_list(self)
2143            .iter()
2144            .rev()
2145            .position(|zombie| zombie.matches_selector_and_waiting_option(selector, options))
2146            .map(|position_starting_from_the_back| {
2147                zombie_list(self).len() - 1 - position_starting_from_the_back
2148            });
2149
2150        selected_zombie_position.map(|position| {
2151            if options.keep_waitable_state {
2152                zombie_list(self)[position].to_wait_result()
2153            } else {
2154                let zombie = zombie_list(self).remove(position);
2155                self.children_time_stats += zombie.time_stats;
2156                let result = zombie.to_wait_result();
2157                zombie.release(pids);
2158                result
2159            }
2160        })
2161    }
2162
2163    pub fn is_correct_exit_signal(for_clone: bool, exit_code: Option<Signal>) -> bool {
2164        for_clone == (exit_code != Some(SIGCHLD))
2165    }
2166
2167    fn get_waitable_running_children(
2168        &self,
2169        selector: &ProcessSelector,
2170        options: &WaitingOptions,
2171        pids: &PidTable,
2172    ) -> WaitableChildResult {
2173        // The children whose pid matches the pid selector queried.
2174        let filter_children_by_pid_selector = |child: &ThreadGroup| match *selector {
2175            ProcessSelector::Any => true,
2176            ProcessSelector::Pid(pid) => child.leader == pid,
2177            ProcessSelector::Pgid(pgid) => {
2178                // This allow_subclass is safe because the lock is being acquired
2179                // in a strictly top-down traversal of the ThreadGroup tree (from parent
2180                // to child), so no lock ordering cycles can be formed.
2181                let _token = allow_subclass();
2182                pids.get_process_group(pgid).as_ref() == Some(&child.read().process_group)
2183            }
2184            ProcessSelector::Process(ref key) => *key == ThreadGroupKey::from(child),
2185        };
2186
2187        // The children whose exit signal matches the waiting options queried.
2188        let filter_children_by_waiting_options = |child: &ThreadGroup| {
2189            if options.wait_for_all {
2190                return true;
2191            }
2192            // This allow_subclass is safe because the lock is being acquired
2193            // in a strictly top-down traversal of the ThreadGroup tree (from parent
2194            // to child), so no lock ordering cycles can be formed.
2195            let _token = allow_subclass();
2196            Self::is_correct_exit_signal(options.wait_for_clone, child.read().exit_signal)
2197        };
2198
2199        // If wait_for_exited flag is disabled or no exited children were found we look for running
2200        // children.
2201        let mut selected_children = self
2202            .children
2203            .values()
2204            .map(|t| t.upgrade().unwrap())
2205            .filter(|tg| filter_children_by_pid_selector(&tg))
2206            .filter(|tg| filter_children_by_waiting_options(&tg))
2207            .peekable();
2208        if selected_children.peek().is_none() {
2209            // There still might be a process that ptrace hasn't looked at yet.
2210            if self.deferred_zombie_ptracers.iter().any(|dzp| match *selector {
2211                ProcessSelector::Any => true,
2212                ProcessSelector::Pid(pid) => dzp.tracee_thread_group_key.pid() == pid,
2213                ProcessSelector::Pgid(pgid) => pgid == dzp.tracee_pgid,
2214                ProcessSelector::Process(ref key) => *key == dzp.tracee_thread_group_key,
2215            }) {
2216                return WaitableChildResult::ShouldWait;
2217            }
2218
2219            return WaitableChildResult::NoneFound;
2220        }
2221        for child in selected_children {
2222            // This allow_subclass is safe because the lock is being acquired
2223            // in a strictly top-down traversal of the ThreadGroup tree (from parent
2224            // to child), so no lock ordering cycles can be formed.
2225            let _token = allow_subclass();
2226            let child = child.write();
2227            if child.last_signal.is_some() {
2228                let build_wait_result = |mut child: ThreadGroupWriteGuard<'_>,
2229                                         exit_status: &dyn Fn(SignalInfo) -> ExitStatus|
2230                 -> WaitResult {
2231                    let siginfo = if options.keep_waitable_state {
2232                        child.last_signal.clone().unwrap()
2233                    } else {
2234                        child.last_signal.take().unwrap()
2235                    };
2236                    let exit_status = if siginfo.signal == SIGKILL {
2237                        // This overrides the stop/continue choice.
2238                        ExitStatus::Kill(siginfo)
2239                    } else {
2240                        exit_status(siginfo)
2241                    };
2242                    let info = child.tasks.values().next().unwrap().info();
2243                    let uid = info.real_creds().uid;
2244                    WaitResult {
2245                        pid: child.base.leader,
2246                        uid,
2247                        exit_info: ProcessExitInfo {
2248                            status: exit_status,
2249                            exit_signal: child.exit_signal,
2250                        },
2251                        time_stats: child.base.time_stats() + child.children_time_stats,
2252                    }
2253                };
2254                let child_stopped = child.base.load_stopped();
2255                if child_stopped == StopState::Awake && options.wait_for_continued {
2256                    return WaitableChildResult::ReadyNow(Box::new(build_wait_result(
2257                        child,
2258                        &|siginfo| ExitStatus::Continue(siginfo, PtraceEvent::None),
2259                    )));
2260                }
2261                if child_stopped == StopState::GroupStopped && options.wait_for_stopped {
2262                    return WaitableChildResult::ReadyNow(Box::new(build_wait_result(
2263                        child,
2264                        &|siginfo| ExitStatus::Stop(siginfo, PtraceEvent::None),
2265                    )));
2266                }
2267            }
2268        }
2269
2270        WaitableChildResult::ShouldWait
2271    }
2272
2273    /// Returns any waitable child matching the given `selector` and `options`. Returns None if no
2274    /// child matching the selector is waitable. Returns ECHILD if no child matches the selector at
2275    /// all.
2276    ///
2277    /// Will remove the waitable status from the child depending on `options`.
2278    pub fn get_waitable_child(
2279        &mut self,
2280        selector: &ProcessSelector,
2281        options: &WaitingOptions,
2282        pids: &mut PidTable,
2283    ) -> WaitableChildResult {
2284        if options.wait_for_exited {
2285            if let Some(waitable_zombie) = self.get_waitable_zombie(
2286                &|state: &mut ThreadGroupMutableState| &mut state.zombie_children,
2287                selector,
2288                options,
2289                pids,
2290            ) {
2291                return WaitableChildResult::ReadyNow(Box::new(waitable_zombie));
2292            }
2293        }
2294
2295        self.get_waitable_running_children(selector, options, pids)
2296    }
2297
2298    /// Returns a running task in the current thread group.
2299    pub fn get_running_task(&self) -> Result<Arc<Task>, Errno> {
2300        self.tasks
2301            .iter()
2302            .find_map(|container| container.1.upgrade().filter(|task| task.is_running()))
2303            .ok_or_else(|| errno!(ESRCH))
2304    }
2305
2306    /// Returns a task representative of the [`ThreadGroup`].
2307    ///
2308    /// If the task list contains at least one running task, an arbitrary running task is returned.
2309    /// Otherwise, if the task list is empty, the process must be a zombie. In this case, the exited
2310    /// leader task is returned.
2311    pub fn get_any_task(&self) -> Result<Arc<Task>, Errno> {
2312        self.get_running_task()
2313            .ok()
2314            .or_else(|| self.base.leader_task.get().and_then(|t| t.upgrade()))
2315            .ok_or_else(|| errno!(ESRCH))
2316    }
2317
2318    /// Set the stop status of the process.  If you pass |siginfo| of |None|,
2319    /// does not update the signal.  If |finalize_only| is set, will check that
2320    /// the set will be a finalize (Stopping -> Stopped or Stopped -> Stopped)
2321    /// before executing it.
2322    ///
2323    /// Returns the latest stop state after any changes.
2324    pub fn set_stopped(
2325        mut self,
2326        new_stopped: StopState,
2327        siginfo: Option<SignalInfo>,
2328        finalize_only: bool,
2329    ) -> StopState {
2330        if let Some(stopped) = self.base.check_stopped_state(new_stopped, finalize_only) {
2331            return stopped;
2332        }
2333
2334        // Thread groups don't transition to group stop if they are waking, because waking
2335        // means something told it to wake up (like a SIGCONT) but hasn't finished yet.
2336        if self.base.load_stopped() == StopState::Waking
2337            && (new_stopped == StopState::GroupStopping || new_stopped == StopState::GroupStopped)
2338        {
2339            return self.base.load_stopped();
2340        }
2341
2342        // TODO(https://g-issues.fuchsia.dev/issues/306438676): When thread
2343        // group can be stopped inside user code, tasks/thread groups will
2344        // need to be either restarted or stopped here.
2345        self.store_stopped(new_stopped);
2346        if let Some(signal) = &siginfo {
2347            // We don't want waiters to think the process was unstopped
2348            // because of a sigkill.  They will get woken when the
2349            // process dies.
2350            if signal.signal != SIGKILL {
2351                self.last_signal = siginfo;
2352            }
2353        }
2354        if new_stopped == StopState::Waking || new_stopped == StopState::ForceWaking {
2355            self.lifecycle_waiters.notify_value(ThreadGroupLifecycleWaitValue::Stopped);
2356        };
2357
2358        let parent = (!new_stopped.is_in_progress()).then(|| self.parent.clone()).flatten();
2359
2360        // Drop the lock before locking the parent.
2361        std::mem::drop(self);
2362        if let Some(parent) = parent {
2363            let parent = parent.upgrade();
2364            parent
2365                .write()
2366                .lifecycle_waiters
2367                .notify_value(ThreadGroupLifecycleWaitValue::ChildStatus);
2368        }
2369
2370        new_stopped
2371    }
2372
2373    fn store_stopped(&mut self, state: StopState) {
2374        // We don't actually use the guard but we require it to enforce that the
2375        // caller holds the thread group's mutable state lock (identified by
2376        // mutable access to the thread group's mutable state).
2377
2378        self.base.stop_state.store(state, Ordering::Relaxed)
2379    }
2380
2381    /// Sends the signal `signal_info` to this thread group.
2382    #[allow(unused_mut, reason = "needed for some but not all macro outputs")]
2383    pub fn send_signal(mut self, signal_info: SignalInfo) {
2384        let sigaction = self.base.signal_actions.get(signal_info.signal);
2385        let action = action_for_signal(&signal_info, sigaction);
2386
2387        {
2388            let mut pending_signals = self.base.pending_signals.lock();
2389            pending_signals.enqueue(signal_info.clone());
2390            self.base.has_pending_signals.store(true, Ordering::Relaxed);
2391        }
2392        let tasks: Vec<Weak<Task>> = self.tasks.values().map(|t| t.weak_clone()).collect();
2393
2394        // Set state to waking before interrupting any tasks.
2395        if signal_info.signal == SIGKILL {
2396            self.set_stopped(StopState::ForceWaking, Some(signal_info.clone()), false);
2397        } else if signal_info.signal == SIGCONT {
2398            self.set_stopped(StopState::Waking, Some(signal_info.clone()), false);
2399        }
2400
2401        let mut has_interrupted_task = false;
2402        for task in tasks.iter().flat_map(|t| t.upgrade()) {
2403            let mut task_state = task.write();
2404
2405            if signal_info.signal == SIGKILL {
2406                task_state.thaw();
2407                task_state.set_stopped(StopState::ForceWaking, None, None, None);
2408            } else if signal_info.signal == SIGCONT {
2409                task_state.set_stopped(StopState::Waking, None, None, None);
2410            }
2411
2412            let is_masked = task_state.is_signal_masked(signal_info.signal);
2413            let was_masked = task_state.is_signal_masked_by_saved_mask(signal_info.signal);
2414
2415            let is_queued = action != DeliveryAction::Ignore
2416                || is_masked
2417                || was_masked
2418                || task_state.is_ptraced();
2419
2420            if is_queued {
2421                task_state.notify_signal_waiters(&signal_info.signal);
2422
2423                if !is_masked && action.must_interrupt(Some(sigaction)) && !has_interrupted_task {
2424                    // Only interrupt one task, and only interrupt if the signal was actually queued
2425                    // and the action must interrupt.
2426                    drop(task_state);
2427                    task.interrupt();
2428                    has_interrupted_task = true;
2429                }
2430            }
2431        }
2432    }
2433}
2434
2435/// Container around a weak task and a strong `TaskPersistentInfo`. It is needed to keep the
2436/// information even when the task is not upgradable, because when the task is dropped, there is a
2437/// moment where the task is not yet released, yet the weak pointer is not upgradeable anymore.
2438/// During this time, it is still necessary to access the persistent info to compute the state of
2439/// the thread for the different wait syscalls.
2440pub struct TaskContainer(Weak<Task>, TaskPersistentInfo);
2441
2442impl From<&Arc<Task>> for TaskContainer {
2443    fn from(task: &Arc<Task>) -> Self {
2444        Self(Arc::downgrade(task), task.persistent_info.clone())
2445    }
2446}
2447
2448impl From<TaskContainer> for TaskPersistentInfo {
2449    fn from(container: TaskContainer) -> TaskPersistentInfo {
2450        container.1
2451    }
2452}
2453
2454impl TaskContainer {
2455    fn upgrade(&self) -> Option<Arc<Task>> {
2456        self.0.upgrade()
2457    }
2458
2459    fn weak_clone(&self) -> Weak<Task> {
2460        self.0.clone()
2461    }
2462
2463    fn info(&self) -> &TaskPersistentInfo {
2464        &self.1
2465    }
2466}
2467
2468#[cfg(test)]
2469mod test {
2470    use super::*;
2471    use crate::testing::*;
2472
2473    #[::fuchsia::test]
2474    async fn test_setsid() {
2475        spawn_kernel_and_run(async |current_task| {
2476            fn get_process_group(task: &Task) -> Arc<ProcessGroup> {
2477                Arc::clone(&task.thread_group().read().process_group)
2478            }
2479            assert_eq!(current_task.thread_group().setsid(), error!(EPERM));
2480
2481            let child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2482            assert_eq!(get_process_group(&current_task), get_process_group(&child_task));
2483
2484            let old_process_group = child_task.thread_group().read().process_group.clone();
2485            assert_eq!(child_task.thread_group().setsid(), Ok(()));
2486            assert_eq!(
2487                child_task.thread_group().read().process_group.session.leader,
2488                child_task.get_pid()
2489            );
2490            assert!(!old_process_group.read().thread_groups().contains(child_task.thread_group()));
2491        })
2492        .await;
2493    }
2494
2495    #[::fuchsia::test]
2496    async fn test_exit_status() {
2497        spawn_kernel_and_run(async |current_task| {
2498            let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
2499            child.thread_group().kill(ExitStatus::Exit(42), None);
2500            std::mem::drop(child);
2501            assert_eq!(
2502                current_task.thread_group().read().zombie_children[0].exit_info.status,
2503                ExitStatus::Exit(42)
2504            );
2505        })
2506        .await;
2507    }
2508
2509    #[::fuchsia::test]
2510    async fn test_setgpid() {
2511        spawn_kernel_and_run(async |current_task| {
2512            assert_eq!(current_task.thread_group().setsid(), error!(EPERM));
2513
2514            let child_task1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2515            let child_task2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2516            let execd_child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2517            execd_child_task.thread_group().write().did_exec = true;
2518            let other_session_child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2519            assert_eq!(other_session_child_task.thread_group().setsid(), Ok(()));
2520
2521            assert_eq!(
2522                child_task1.thread_group().setpgid(&current_task, &current_task, 0),
2523                error!(ESRCH)
2524            );
2525            assert_eq!(
2526                current_task.thread_group().setpgid(&current_task, &execd_child_task, 0),
2527                error!(EACCES)
2528            );
2529            assert_eq!(
2530                current_task.thread_group().setpgid(&current_task, &current_task, 0),
2531                error!(EPERM)
2532            );
2533            assert_eq!(
2534                current_task.thread_group().setpgid(&current_task, &other_session_child_task, 0),
2535                error!(EPERM)
2536            );
2537            assert_eq!(
2538                current_task.thread_group().setpgid(&current_task, &child_task1, -1),
2539                error!(EINVAL)
2540            );
2541            assert_eq!(
2542                current_task.thread_group().setpgid(&current_task, &child_task1, 255),
2543                error!(EPERM)
2544            );
2545            assert_eq!(
2546                current_task.thread_group().setpgid(
2547                    &current_task,
2548                    &child_task1,
2549                    other_session_child_task.tid
2550                ),
2551                error!(EPERM)
2552            );
2553
2554            assert_eq!(child_task1.thread_group().setpgid(&current_task, &child_task1, 0), Ok(()));
2555            assert_eq!(
2556                child_task1.thread_group().read().process_group.session.leader,
2557                current_task.tid
2558            );
2559            assert_eq!(child_task1.thread_group().read().process_group.leader, child_task1.tid);
2560
2561            let old_process_group = child_task2.thread_group().read().process_group.clone();
2562            assert_eq!(
2563                current_task.thread_group().setpgid(&current_task, &child_task2, child_task1.tid),
2564                Ok(())
2565            );
2566            assert_eq!(child_task2.thread_group().read().process_group.leader, child_task1.tid);
2567            assert!(!old_process_group.read().thread_groups().contains(child_task2.thread_group()));
2568        })
2569        .await;
2570    }
2571
2572    #[::fuchsia::test]
2573    async fn test_adopt_children() {
2574        spawn_kernel_and_run(async |current_task| {
2575            let task1 = current_task.clone_task_for_test(0, None);
2576            let task2 = task1.clone_task_for_test(0, None);
2577            let task3 = task2.clone_task_for_test(0, None);
2578
2579            assert_eq!(task3.thread_group().read().get_ppid(), task2.tid);
2580
2581            task2.thread_group().kill(ExitStatus::Exit(0), None);
2582            std::mem::drop(task2);
2583
2584            // Task3 parent should be current_task.
2585            assert_eq!(task3.thread_group().read().get_ppid(), current_task.tid);
2586        })
2587        .await;
2588    }
2589
2590    #[::fuchsia::test]
2591    async fn test_getppid_after_self_and_parent_exit() {
2592        spawn_kernel_and_run(async |current_task| {
2593            let task1 = current_task.clone_task_for_test(0, None);
2594            let task2 = task1.clone_task_for_test(0, None);
2595
2596            // Take strong references to the ThreadGroups.
2597            let tg1 = task1.thread_group().clone();
2598            let tg2 = task2.thread_group().clone();
2599
2600            assert_eq!(tg1.read().get_ppid(), current_task.tid);
2601            assert_eq!(tg2.read().get_ppid(), task1.tid);
2602
2603            // Exit `task2` first, so that when `task1` exits, it will not be reparented to init.
2604            tg2.kill(ExitStatus::Exit(0), None);
2605            std::mem::drop(task2);
2606
2607            // Exit `task1`, and drop the task and ThreadGroup.
2608            tg1.kill(ExitStatus::Exit(0), None);
2609            std::mem::drop(task1);
2610            std::mem::drop(tg1);
2611
2612            // It should still be valid to call `get_ppid()` on `tg2`, though is parent ThreadGroup
2613            // no longer exists.
2614            let _ = tg2.read().get_ppid();
2615        })
2616        .await;
2617    }
2618}