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::{ControllingSession, ControllingTerminal, Terminal, TerminalSide};
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};
17#[cfg(doc)]
18use crate::task::TaskFlags;
19use crate::task::memory_attribution::MemoryAttributionLifecycleEvent;
20use crate::task::{
21    CurrentTask, ExitStatus, Kernel, Pid, PidTableGuard, Task, TaskMutableState,
22    TaskPersistentInfo, TypedWaitQueue, WaitResult, ZombieProcess, ZombieState,
23};
24use crate::time::{IntervalTimerHandle, TimerTable};
25use fuchsia_rcu::subtle::RcuPtr;
26use fuchsia_rcu::{RcuDroppable, RcuReadScope};
27use fuchsia_rcu_collections::rcu_intrusive_list::{Link, RcuListAdapter, rcu_list_adapter};
28use itertools::Itertools;
29use macro_rules_attribute::apply;
30use starnix_lifecycle::{AtomicCounter, DropNotifier};
31use starnix_logging::{log_debug, log_error, log_info, log_warn, track_stub};
32use starnix_sync::{
33    LockDepMutex, LockDepRwLock, ThreadGroupLimits, ThreadGroupMutableStateLock,
34    ThreadGroupPendingSignalsLock, ThreadGroupPtraceesLock, allow_subclass,
35};
36use starnix_task_command::TaskCommand;
37use starnix_types::ownership::{OwnedRef, Releasable};
38use starnix_types::stats::TaskTimeStats;
39use starnix_types::time::{itimerspec_from_itimerval, timeval_from_duration};
40use starnix_uapi::auth::{CAP_SYS_ADMIN, CAP_SYS_RESOURCE};
41use starnix_uapi::errors::Errno;
42use starnix_uapi::personality::PersonalityFlags;
43use starnix_uapi::resource_limits::{Resource, ResourceLimits};
44use starnix_uapi::signals::{
45    SIGCHLD, SIGCONT, SIGHUP, SIGKILL, SIGTERM, SIGTTOU, SigSet, Signal, UncheckedSignal,
46};
47use starnix_uapi::user_address::UserAddress;
48use starnix_uapi::{
49    ITIMER_PROF, ITIMER_REAL, ITIMER_VIRTUAL, SA_NOCLDWAIT, SI_TKILL, SI_USER, SIG_IGN, errno,
50    error, itimerval, pid_t, rlimit, tid_t,
51};
52use std::collections::HashSet;
53use std::fmt;
54use std::sync::atomic::{AtomicBool, Ordering};
55use std::sync::{Arc, Weak};
56use zx::{Koid, Status};
57
58#[derive(Debug)]
59pub struct ZirconProcess {
60    process: zx::Process,
61    koid: Result<Koid, Status>,
62}
63
64impl ZirconProcess {
65    pub fn new(process: zx::Process) -> Self {
66        let koid = process.koid();
67        Self { process, koid }
68    }
69
70    pub fn koid(&self) -> Result<Koid, Status> {
71        self.koid
72    }
73}
74
75impl std::ops::Deref for ZirconProcess {
76    type Target = zx::Process;
77    fn deref(&self) -> &Self::Target {
78        &self.process
79    }
80}
81
82/// Values used for waiting on the [ThreadGroup] lifecycle wait queue.
83#[repr(u64)]
84pub enum ThreadGroupLifecycleWaitValue {
85    /// Wait for updates to the WaitResults of tasks in the group.
86    ChildStatus,
87    /// Wait for updates to `stopped`.
88    Stopped,
89}
90
91impl Into<u64> for ThreadGroupLifecycleWaitValue {
92    fn into(self) -> u64 {
93        self as u64
94    }
95}
96
97/// Child process that have exited, but the zombie ptrace needs to be consumed
98/// before they can be waited for.
99#[derive(Clone, Debug)]
100pub struct DeferredZombiePTracer {
101    /// Original tracer
102    pub tracer_pid: Pid,
103    /// Tracee tid
104    pub tracee_tid: Pid,
105    /// Tracee pgid
106    pub tracee_pgid: Pid,
107    /// Tracee thread group
108    pub tracee_pid: Pid,
109}
110
111impl DeferredZombiePTracer {
112    fn new(tracer: &ThreadGroup, tracee: &Task, tracee_pgid: Pid) -> Self {
113        Self {
114            tracer_pid: tracer.leader.clone(),
115            tracee_tid: tracee.tid.clone(),
116            tracee_pgid,
117            tracee_pid: tracee.pid.clone(),
118        }
119    }
120}
121
122/// The mutable state of the ThreadGroup.
123pub struct ThreadGroupMutableState {
124    /// The parent thread group.
125    ///
126    /// The value needs to be writable so that it can be re-parent to the correct subreaper if the
127    /// parent ends before the child.
128    pub parent: Option<ThreadGroupParent>,
129
130    /// The signal this process generates on exit.
131    pub exit_signal: Option<Signal>,
132
133    /// Parent death [`Signal`] of this process, set by `prctl(PR_SET_PDEATHSIG)`.
134    ///
135    /// Cleared on `fork(2)`, upon changes to effective or filesystem UID/GID, and
136    /// during `execve(2)` if the executed file is set-user-ID, set-group-ID, or
137    /// has associated capabilities.
138    parent_death_signal: Option<Signal>,
139
140    /// The tasks in the thread group.
141    ///
142    /// The references to Task is weak to prevent cycles as Task have a Arc reference to their
143    /// thread group.
144    /// It is still expected that these weak references are always valid, as tasks must unregister
145    /// themselves before they are deleted.
146    tasks: HashSet<TaskPersistentInfo>,
147
148    /// The children of this thread group.
149    ///
150    /// Holds `Pid` references to prevent cycles. Child thread groups must unregister
151    /// themselves before they are deleted.
152    pub children: HashSet<Pid>,
153
154    /// Child tasks that have exited, but not yet been waited for.
155    pub zombie_children: Vec<OwnedRef<ZombieProcess>>,
156
157    /// ptracees of this process that have exited, but not yet been waited for.
158    pub zombie_ptracees: ZombiePtracees,
159
160    /// Child processes that have exited, but the zombie ptrace needs to be consumed
161    /// before they can be waited for.
162    pub deferred_zombie_ptracers: Vec<DeferredZombiePTracer>,
163
164    /// Unified [WaitQueue] for all waited ThreadGroup events.
165    pub lifecycle_waiters: TypedWaitQueue<ThreadGroupLifecycleWaitValue>,
166
167    /// Whether this thread group will inherit from children of dying processes in its descendant
168    /// tree.
169    pub is_child_subreaper: bool,
170
171    /// The process group leader PID.
172    pub process_group: Pid,
173
174    /// The session leader PID.
175    pub session: Pid,
176
177    /// The controlling terminal of the thread group.
178    pub controlling_terminal: Option<ControllingTerminal>,
179
180    pub did_exec: bool,
181
182    /// A signal that indicates whether the process is going to become waitable
183    /// via waitid and waitpid for either WSTOPPED or WCONTINUED, depending on
184    /// the value of `stopped`. If not None, contains the SignalInfo to return.
185    pub last_signal: Option<SignalInfo>,
186
187    /// Whether the `ThreadGroup` is running or not.
188    ///
189    /// For exited thread groups, this contains the exit status.
190    run_state: ThreadGroupRunState,
191
192    /// Time statistics accumulated from the children.
193    pub children_time_stats: TaskTimeStats,
194
195    /// Personality flags set with `sys_personality()`.
196    pub personality: PersonalityFlags,
197
198    /// Thread groups allowed to trace tasks in this this thread group.
199    pub allowed_ptracers: PtraceAllowedPtracers,
200
201    /// Channel to message when this thread group exits.
202    exit_notifier: Option<futures::channel::oneshot::Sender<()>>,
203
204    /// Notifier for name changes.
205    pub notifier: Option<std::sync::mpsc::Sender<MemoryAttributionLifecycleEvent>>,
206}
207
208/// A collection of `Task` objects that roughly correspond to a "process".
209///
210/// Userspace programmers often think about "threads" and "process", but those concepts have no
211/// clear analogs inside the kernel because tasks are typically created using `clone(2)`, which
212/// takes a complex set of flags that describes how much state is shared between the original task
213/// and the new task.
214///
215/// If a new task is created with the `CLONE_THREAD` flag, the new task will be placed in the same
216/// `ThreadGroup` as the original task. Userspace typically uses this flag in conjunction with the
217/// `CLONE_FILES`, `CLONE_VM`, and `CLONE_FS`, which corresponds to the userspace notion of a
218/// "thread". For example, that's how `pthread_create` behaves. In that sense, a `ThreadGroup`
219/// normally corresponds to the set of "threads" in a "process". However, this pattern is purely a
220/// userspace convention, and nothing stops userspace from using `CLONE_THREAD` without
221/// `CLONE_FILES`, for example.
222///
223/// In Starnix, a `ThreadGroup` corresponds to a Zircon process, which means we do not support the
224/// `CLONE_THREAD` flag without the `CLONE_VM` flag. If we run into problems with this limitation,
225/// we might need to revise this correspondence.
226///
227/// Each `Task` in a `ThreadGroup` has the same thread group ID (`tgid`). The task with the same
228/// `pid` as the `tgid` is called the thread group leader.
229///
230/// Thread groups are destroyed when the last task in the group exits.
231pub struct ThreadGroup {
232    /// Weak reference to the `OwnedRef` of this `ThreadGroup`. This allows to retrieve the
233    /// `TempRef` from a raw `ThreadGroup`.
234    pub weak_self: Weak<ThreadGroup>,
235
236    /// The kernel to which this thread group belongs.
237    pub kernel: Arc<Kernel>,
238
239    /// A handle to the underlying Zircon process object.
240    ///
241    /// Currently, we have a 1-to-1 mapping between thread groups and zx::process
242    /// objects. This approach might break down if/when we implement CLONE_VM
243    /// without CLONE_THREAD because that creates a situation where two thread
244    /// groups share an address space. To implement that situation, we might
245    /// need to break the 1-to-1 mapping between thread groups and zx::process
246    /// or teach zx::process to share address spaces.
247    pub process: ZirconProcess,
248
249    /// A handle to the restricted address space for the Zircon process object.
250    pub root_vmar: zx::Vmar,
251
252    /// The lead task of this thread group.
253    ///
254    /// The lead task is typically the initial thread created in the thread group.
255    pub leader: Pid,
256
257    /// Current node in `PidEntry::pgid_thread_groups`. Protected by `PidTableLock`.
258    pub(super) pgrp_node: RcuPtr<ThreadGroupLinkNode>,
259
260    /// Current node in `PidEntry::sid_thread_groups`. Protected by `PidTableLock`.
261    pub(super) session_node: RcuPtr<ThreadGroupLinkNode>,
262
263    /// The signal actions that are registered for this process.
264    pub signal_actions: Arc<SignalActions>,
265
266    /// The timers for this thread group (from timer_create(), etc.).
267    pub timers: TimerTable,
268
269    /// A mechanism to be notified when this `ThreadGroup` is destroyed.
270    pub drop_notifier: DropNotifier,
271
272    /// Whether the process is currently stopped.
273    ///
274    /// Must only be set when the `mutable_state` write lock is held.
275    stop_state: AtomicStopState,
276
277    /// The mutable state of the ThreadGroup.
278    mutable_state: LockDepRwLock<ThreadGroupMutableState, ThreadGroupMutableStateLock>,
279
280    /// The resource limits for this thread group.  This is outside mutable_state
281    /// to avoid deadlocks where the thread_group lock is held when acquiring
282    /// the task lock, and vice versa.
283    pub limits: LockDepMutex<ResourceLimits, ThreadGroupLimits>,
284
285    /// The next unique identifier for a seccomp filter.  These are required to be
286    /// able to distinguish identical seccomp filters, which are treated differently
287    /// for the purposes of SECCOMP_FILTER_FLAG_TSYNC.  Inherited across clone because
288    /// seccomp filters are also inherited across clone.
289    pub next_seccomp_filter_id: AtomicCounter<u64>,
290
291    /// Tasks ptraced by this process
292    pub ptracees: LockDepMutex<HashSet<TaskPersistentInfo>, ThreadGroupPtraceesLock>,
293
294    /// The signals that are currently pending for this thread group.
295    pub pending_signals: LockDepMutex<QueuedSignals, ThreadGroupPendingSignalsLock>,
296
297    /// Whether or not there are any pending signals available for tasks in this thread group.
298    /// Used to avoid having to acquire the signal state lock in hot paths.
299    pub has_pending_signals: AtomicBool,
300
301    /// The monotonic time at which the thread group started.
302    pub start_time: zx::MonotonicInstant,
303
304    /// Whether to log syscalls at INFO level for this thread group.
305    log_syscalls_as_info: AtomicBool,
306}
307
308impl fmt::Debug for ThreadGroup {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        write!(
311            f,
312            "{}({})",
313            self.process.get_name().unwrap_or(zx::Name::new_lossy("<unknown>")),
314            self.leader
315        )
316    }
317}
318
319impl ThreadGroup {
320    pub fn to_owned(&self) -> Arc<Self> {
321        self.weak_self.upgrade().expect("ThreadGroup is alive")
322    }
323
324    pub fn sync_syscall_log_level(&self) {
325        let command = self.read().leader_command();
326        let filters = self.kernel.syscall_log_filters.lock();
327        let should_log = filters.iter().any(|f| f.matches(&command));
328        let prev_should_log = self.log_syscalls_as_info.swap(should_log, Ordering::Relaxed);
329        let change_str = match (should_log, prev_should_log) {
330            (true, false) => Some("Enabled"),
331            (false, true) => Some("Disabled"),
332            _ => None,
333        };
334        if let Some(change_str) = change_str {
335            log_info!(
336                "{change_str} info syscall logs for thread group {} (command: {command})",
337                self.leader
338            );
339        }
340    }
341
342    #[inline]
343    pub fn syscall_log_level(&self) -> starnix_logging::Level {
344        if self.log_syscalls_as_info.load(Ordering::Relaxed) {
345            starnix_logging::Level::Info
346        } else {
347            starnix_logging::Level::Trace
348        }
349    }
350}
351
352impl PartialEq for ThreadGroup {
353    fn eq(&self, other: &Self) -> bool {
354        self.leader == other.leader
355    }
356}
357
358/// Non-reusable list node linking a [`ThreadGroup`] into a process group or session list.
359///
360/// Allocated per list attachment (`attach_pgid` / `attach_sid`) and retired via `rcu_drop`
361/// upon detachment so moving a process between process groups or sessions never clobbers
362/// `link.next` for concurrent lock-free RCU readers.
363#[derive(Debug, RcuDroppable)]
364pub(super) struct ThreadGroupLinkNode {
365    pub(super) thread_group: Weak<ThreadGroup>,
366    pub(super) link: Link,
367}
368
369impl ThreadGroupLinkNode {
370    pub(super) fn new(thread_group: Weak<ThreadGroup>) -> Self {
371        Self { thread_group, link: Link::default() }
372    }
373}
374
375/// RCU intrusive list adapter for [`ThreadGroupLinkNode`].
376#[derive(Debug, RcuDroppable)]
377pub(super) struct ThreadGroupLinkAdapter;
378
379impl RcuListAdapter<ThreadGroupLinkNode> for ThreadGroupLinkAdapter {
380    rcu_list_adapter!(ThreadGroupLinkNode, link);
381}
382
383impl Drop for ThreadGroup {
384    fn drop(&mut self) {
385        let state = self.mutable_state.get_mut();
386        assert!(state.tasks.is_empty());
387        assert!(state.children.is_empty());
388        assert!(state.zombie_children.is_empty());
389        assert!(state.zombie_ptracees.is_empty());
390        #[cfg(any(test, debug_assertions))]
391        {
392            let scope = fuchsia_rcu::RcuReadScope::new();
393            assert!(self.pgrp_node.read(&scope).is_null());
394            assert!(self.session_node.read(&scope).is_null());
395            assert!(
396                state
397                    .parent
398                    .as_ref()
399                    .and_then(|p| p
400                        .thread_group
401                        .upgrade()
402                        .map(|p| !p.read().children.contains(&self.leader)))
403                    .unwrap_or(true)
404            );
405        }
406    }
407}
408
409/// Parent process reference for a [`ThreadGroup`], tracking both the parent
410/// [`ThreadGroup`] and, prior to reparenting, the specific [`Task`] within that
411/// parent that created the child process.
412#[derive(Clone)]
413pub struct ThreadGroupParent {
414    /// Weak reference to the current parent [`ThreadGroup`].
415    thread_group: Weak<ThreadGroup>,
416
417    /// Specific [`Task`] within the original parent [`ThreadGroup`] that created this child
418    /// process, or `None` once the child has been reparented to an ancestor reaper.
419    creator_task: Option<Weak<Task>>,
420}
421
422impl ThreadGroupParent {
423    /// Creates a [`ThreadGroupParent`] referencing `thread_group`, defaulting `creator_task`
424    /// to the leader [`Task`] of `thread_group`.
425    ///
426    /// When a child process is created by a specific thread via `clone(2)` / `fork(2)`, callers
427    /// override `creator_task` via [`Self::set_creator_task`] with the actual creating [`Task`]
428    /// (or the inherited `creator_task` when `CLONE_PARENT` is specified).
429    pub fn new(thread_group: Weak<ThreadGroup>) -> Self {
430        let upgraded = thread_group.upgrade();
431        debug_assert!(upgraded.is_some());
432        let creator_task =
433            upgraded.and_then(|tg| tg.leader.get_task().ok()).map(|t| t.weak_self.clone());
434        Self { thread_group, creator_task }
435    }
436
437    /// Creates a [`ThreadGroupParent`] for a child reparented to `thread_group` after its
438    /// previous parent process terminated.
439    ///
440    /// Sets `creator_task` to `None` so that subsequent `PR_SET_PDEATHSIG` delivery is triggered
441    /// only if `thread_group` is a subreaper and only when `thread_group` itself terminates as a
442    /// process, rather than when individual threads within the reaper exit.
443    pub(super) fn from_reaper(thread_group: Weak<ThreadGroup>) -> Self {
444        debug_assert!(thread_group.upgrade().is_some());
445        Self { thread_group, creator_task: None }
446    }
447
448    /// Upgrades the parent reference to an [`Arc<ThreadGroup>`].
449    pub fn upgrade(&self) -> Arc<ThreadGroup> {
450        self.thread_group.upgrade().expect("ThreadGroupParent references must always be valid")
451    }
452
453    /// Returns the specific [`Task`] in the original parent [`ThreadGroup`] that created this
454    /// process, or `None` if this process has been reparented.
455    pub(super) fn creator_task(&self) -> Option<&Weak<Task>> {
456        self.creator_task.as_ref()
457    }
458
459    /// Sets the [`Task`] in the parent [`ThreadGroup`] whose termination triggers
460    /// `PR_SET_PDEATHSIG` delivery for this child process.
461    pub(super) fn set_creator_task(&mut self, creator_task: Option<Weak<Task>>) {
462        self.creator_task = creator_task;
463    }
464}
465
466/// A selector that can match a process. Works as a representation of the pid argument to syscalls
467/// like wait and kill.
468#[derive(Debug, Clone)]
469pub enum ProcessSelector {
470    /// Matches any process at all.
471    Any,
472    /// Matches only the process with the specified pid
473    Pid(Pid),
474    /// Matches all the processes in the given process group
475    Pgid(Pid),
476}
477
478impl ProcessSelector {
479    pub fn match_tid(&self, tid: &Pid) -> bool {
480        match self {
481            ProcessSelector::Pid(pid) => {
482                if pid == tid {
483                    true
484                } else if let Ok(task_ref) = tid.get_task() {
485                    &task_ref.pid == pid
486                } else {
487                    false
488                }
489            }
490            ProcessSelector::Any => true,
491            ProcessSelector::Pgid(pgid) => {
492                if let Ok(task_ref) = tid.get_task() {
493                    &task_ref.thread_group().read().process_group == pgid
494                } else {
495                    false
496                }
497            }
498        }
499    }
500}
501
502#[derive(Clone, Debug, Default, PartialEq, Eq)]
503enum ThreadGroupRunState {
504    #[default]
505    Running,
506    Exiting(ExitStatus),
507    Exited(ExitStatus),
508}
509
510impl ThreadGroup {
511    /// Creates a ThreadGroup for a regular userspace process.
512    pub fn new(
513        kernel: Arc<Kernel>,
514        process: zx::Process,
515        root_vmar: zx::Vmar,
516        parent: Option<ThreadGroupWriteGuard<'_>>,
517        leader: Pid,
518        exit_signal: Option<Signal>,
519        signal_actions: Arc<SignalActions>,
520    ) -> Arc<ThreadGroup> {
521        debug_assert!(!process.is_invalid());
522        debug_assert!(!root_vmar.is_invalid());
523        Self::new_internal(kernel, process, root_vmar, parent, leader, exit_signal, signal_actions)
524    }
525
526    /// Creates a ThreadGroup for a kernel system task (e.g., kthreadd).
527    pub fn for_system(kernel: Arc<Kernel>, leader: Pid) -> Arc<ThreadGroup> {
528        Self::new_internal(
529            kernel,
530            zx::Process::invalid(),
531            zx::Vmar::invalid(),
532            None,
533            leader,
534            Some(SIGCHLD),
535            SignalActions::default(),
536        )
537    }
538
539    /// Creates a ThreadGroup suitable for use in tests.
540    ///
541    /// This function performs the minimal setup necessary to produce a valid `ThreadGroup`
542    /// instance. It uses an invalid handle for the root VMAR, sets no parent, and uses
543    /// default signal actions with `SIGCHLD` as the exit signal.
544    ///
545    /// This should only be used in tests where a full process environment is not required.
546    pub fn for_test(
547        kernel: Arc<Kernel>,
548        process: zx::Process,
549        parent: ThreadGroupWriteGuard<'_>,
550        leader: Pid,
551    ) -> Arc<ThreadGroup> {
552        Self::new_internal(
553            kernel,
554            process,
555            zx::Vmar::invalid(),
556            Some(parent),
557            leader,
558            Some(SIGCHLD),
559            SignalActions::default(),
560        )
561    }
562
563    fn new_internal(
564        kernel: Arc<Kernel>,
565        process: zx::Process,
566        root_vmar: zx::Vmar,
567        parent: Option<ThreadGroupWriteGuard<'_>>,
568        leader: Pid,
569        exit_signal: Option<Signal>,
570        signal_actions: Arc<SignalActions>,
571    ) -> Arc<ThreadGroup> {
572        Arc::new_cyclic(|weak_self| {
573            let process = ZirconProcess::new(process);
574            let process_group =
575                parent.as_ref().map(|p| p.process_group.clone()).unwrap_or_else(|| leader.clone());
576            let session =
577                parent.as_ref().map(|p| p.session.clone()).unwrap_or_else(|| leader.clone());
578            let mut thread_group = ThreadGroup {
579                weak_self: weak_self.clone(),
580                kernel,
581                process,
582                root_vmar,
583                leader,
584                pgrp_node: RcuPtr::null(),
585                session_node: RcuPtr::null(),
586                signal_actions,
587                timers: Default::default(),
588                drop_notifier: Default::default(),
589                // A child process created via fork(2) inherits its parent's
590                // resource limits.  Resource limits are preserved across execve(2).
591                // Root tasks without a parent (such as `init` and `kthreadd`) start
592                // with the system default resource limits, with `RLIMIT_NPROC` and
593                // `RLIMIT_SIGPENDING` scaled from the system's total physical memory.
594                limits: LockDepMutex::new(
595                    parent.as_ref().map(|p| p.base.limits.lock().clone()).unwrap_or_else(|| {
596                        ResourceLimits::from_total_ram(zx::system_get_physmem())
597                    }),
598                ),
599                next_seccomp_filter_id: Default::default(),
600                ptracees: Default::default(),
601                stop_state: AtomicStopState::new(StopState::Awake),
602                pending_signals: Default::default(),
603                has_pending_signals: Default::default(),
604                start_time: zx::MonotonicInstant::get(),
605                mutable_state: ThreadGroupMutableState {
606                    parent: parent
607                        .as_ref()
608                        .map(|p| ThreadGroupParent::new(p.base.weak_self.clone())),
609                    exit_signal,
610                    // From <https://man7.org/linux/man-pages/man2/PR_SET_PDEATHSIG.2const.html>:
611                    //
612                    //   The parent-death signal setting is cleared for the child of a
613                    //   fork(2).
614                    parent_death_signal: None,
615                    tasks: HashSet::new(),
616                    children: HashSet::new(),
617                    zombie_children: vec![],
618                    zombie_ptracees: ZombiePtracees::new(),
619                    deferred_zombie_ptracers: vec![],
620                    lifecycle_waiters: TypedWaitQueue::<ThreadGroupLifecycleWaitValue>::default(),
621                    is_child_subreaper: false,
622                    process_group,
623                    session,
624                    controlling_terminal: None,
625                    did_exec: false,
626                    last_signal: None,
627                    run_state: Default::default(),
628                    children_time_stats: Default::default(),
629                    personality: parent
630                        .as_ref()
631                        .map(|p| p.personality)
632                        .unwrap_or(Default::default()),
633                    allowed_ptracers: PtraceAllowedPtracers::None,
634                    exit_notifier: None,
635                    notifier: None,
636                }
637                .into(),
638                log_syscalls_as_info: AtomicBool::new(false),
639            };
640
641            if let Some(mut parent) = parent {
642                thread_group.next_seccomp_filter_id.reset(parent.base.next_seccomp_filter_id.get());
643                parent.children.insert(thread_group.leader.clone());
644            };
645            thread_group
646        })
647    }
648
649    state_accessor!(ThreadGroup, mutable_state);
650
651    pub fn load_stopped(&self) -> StopState {
652        self.stop_state.load(Ordering::Relaxed)
653    }
654
655    /// Causes the thread group to exit.
656    ///
657    /// Transitions the thread group to exiting, marks and interrupts sibling tasks to initiate
658    /// teardown, and delivers any `PTRACE_EVENT_EXIT` stop for `current_task`.
659    ///
660    /// If this is being called from a task that is part of the current thread group, the caller
661    /// should pass `current_task` (or call [`CurrentTask::kill_thread_group()`]).
662    pub fn kill(&self, exit_status: ExitStatus, mut current_task: Option<&mut CurrentTask>) {
663        let state = self.write();
664        if !state.is_running() {
665            if let Some(current_task) = current_task {
666                current_task.write().set_exit_status_if_not_already(exit_status);
667            }
668            return;
669        }
670
671        // Mark and wake all sibling tasks while holding the thread-group write lock so no
672        // sibling thread can execute in userspace once the thread group transitions to `Exiting`.
673        // Keep `current_task` unexited (`!TaskFlags::EXITED`) until after `ptrace_event` so it can
674        // block in `PTRACE_EVENT_EXIT`.
675        let current_tid = current_task.as_ref().map(|task| task.tid.clone());
676        state.terminate_tasks(
677            exit_status.clone(),
678            SignalInfo::forced(SIGKILL),
679            current_tid.as_ref(),
680        );
681
682        // From <https://man7.org/linux/man-pages/man2/ptrace.2.html>:
683        //
684        //   PTRACE_O_TRACEEXIT (since Linux 2.5.60)
685        //     The tracee is stopped early during process exit, when registers
686        //     are still available, allowing the tracer to see where the exit
687        //     occurred, whereas the normal exit notification is done after the
688        //     process is finished exiting. Even though context is available,
689        //     the tracer cannot prevent the exit from happening at this point.
690        //
691        // Because the thread group is already `Exiting` and all sibling threads
692        // have been marked `TaskFlags::EXITED` and interrupted, `process_mrelease(2)`
693        // can safely release memory while `current_task` is stopped in
694        // `PTRACE_EVENT_EXIT`.
695        if let Some(ref mut current_task) = current_task {
696            current_task
697                .ptrace_event(PtraceOptions::TRACEEXIT, exit_status.signal_info_status() as u64);
698            let mut task_state = current_task.write();
699            task_state.set_exit_status(exit_status);
700            task_state.force_wake_for_exit();
701        }
702    }
703
704    pub fn add(&self, task: Arc<Task>) -> Result<(), Errno> {
705        let mut state = self.write();
706        if !state.is_running() {
707            if state.tasks_count() == 0 {
708                log_warn!(
709                    "Task {} with leader {} not running while adding its first task, \
710                not sending creation notification",
711                    task.tid,
712                    self.leader
713                );
714            }
715            return error!(EINVAL);
716        }
717        state.tasks.insert(task.persistent_info.clone());
718
719        Ok(())
720    }
721
722    /// Remove the task from the children of this ThreadGroup.
723    ///
724    /// It is important that the task is taken as an `Arc`. It ensures the tasks of the
725    /// ThreadGroup are always valid as they are still valid when removed.
726    pub fn remove(&self, mut pids: PidTableGuard<'_>, task: &Arc<Task>) {
727        task.set_ptrace_zombie(&mut pids);
728
729        let mut state = self.write();
730
731        if !state.tasks.remove(&task.persistent_info) {
732            // The task has never been added. The only expected case is that this thread group
733            // is not running.
734            debug_assert!(!state.is_running());
735            pids.remove_task(&task.tid);
736            return;
737        }
738
739        if state.tasks.is_empty() {
740            let pgid = Arc::clone(&state.process_group);
741            let sid = Arc::clone(&state.session);
742            pids.detach_pgid(&pgid, self);
743            pids.detach_sid(&sid, self);
744
745            let exit_status = if let ThreadGroupRunState::Exiting(exit_status) = &state.run_state {
746                exit_status.clone()
747            } else {
748                let exit_status = task.exit_status().unwrap_or_else(|| {
749                    log_error!("Exiting without an exit code.");
750                    ExitStatus::Exit(u8::MAX)
751                });
752                state.set_exiting(exit_status.clone());
753                exit_status
754            };
755
756            // Detach from any ptraced zombie tasks.
757            let zombie_notifications = state.zombie_ptracees.detach_all(&mut pids);
758
759            // Replace PID table entry with a zombie before clearing `task.tid` so that
760            // lock-free RCU readers calling `PidEntry::get_process_task()` never observe
761            // an empty `ThreadGroup` before `ProcessEntry::Zombie` is published.
762            let zombie = ZombieProcess::new(
763                task.clone(),
764                state.as_ref(),
765                exit_status,
766                state.exit_signal.clone(),
767            );
768            pids.kill_process(&self.leader, Arc::downgrade(task));
769            pids.remove_task(&task.tid);
770
771            let disassociated_controlling_terminal =
772                if state.session == self.leader { state.controlling_terminal.take() } else { None };
773
774            // I have no idea if dropping the lock here is correct, and I don't want to think about
775            // it. If problems do turn up with another thread observing an intermediate state of
776            // this exit operation, the solution is to unify locks. It should be sensible and
777            // possible for there to be a single lock that protects all (or nearly all) of the
778            // data accessed by both exit and wait. In gvisor and linux this is the lock on the
779            // equivalent of the PidTable. This is made more difficult by rust locks being
780            // containers that only lock the data they contain, but see
781            // https://docs.google.com/document/d/1YHrhBqNhU1WcrsYgGAu3JwwlVmFXPlwWHTJLAbwRebY/edit
782            // for an idea.
783            std::mem::drop(state);
784
785            // Disassociate the controlling terminal when the session leader exits.
786            // This cannot be called while holding the ThreadGroup state lock.
787            if let Some(controlling_terminal) = disassociated_controlling_terminal {
788                controlling_terminal.terminal.disassociate_controlling_session_if(
789                    Some(&sid),
790                    Some(controlling_terminal.side),
791                );
792            }
793
794            for notification in zombie_notifications {
795                notification.deliver(&mut pids);
796            }
797
798            // Remove the process from the cgroup2 pid table after TG lock is dropped.
799            // This function will hold the CgroupState lock which should be before the TG lock. See
800            // more in lock_cgroup2_pid_table comments.
801            self.kernel.cgroups.lock_cgroup2_pid_table().remove_process(&self.leader);
802
803            self.detach_ptracees(&mut pids);
804
805            // We will need the immediate parent and the reaper. Once we have them, we can make
806            // sure to take the locks in the right order: parent before child.
807            let parent = self.read().parent.clone();
808            let reaper = self.find_reaper();
809
810            // From <https://man7.org/linux/man-pages/man2/PR_SET_PDEATHSIG.2const.html>:
811            //
812            //   The parent-death signal is sent upon subsequent termination of the
813            //   parent thread and also upon termination of each subreaper process
814            //   (see PR_SET_CHILD_SUBREAPER(2const)) to which the caller is
815            //   subsequently reparented.  If the parent thread and all ancestor
816            //   subreapers have already terminated by the time of the
817            //   PR_SET_PDEATHSIG operation, then no parent-death signal is sent to
818            //   the caller.
819            //
820            //   The parent-death signal is process-directed (see signal(7)) and, if
821            //   the child installs a handler using the sigaction(2) SA_SIGINFO flag,
822            //   the si_pid field of the siginfo_t argument of the handler contains
823            //   the PID of the terminating parent process.
824            let mut children = Vec::new();
825            {
826                // Reparent the children.
827                if let Some(reaper) = reaper {
828                    let reaper = reaper.upgrade();
829                    {
830                        let mut reaper_state = reaper.write();
831                        // Locking the reaper (an ancestor) before `self` and its children follows
832                        // strictly top-down traversal in the process tree, avoiding cycles.
833                        let _token = allow_subclass();
834                        let mut state = self.write();
835                        for child_pid in std::mem::take(&mut state.children) {
836                            if let Ok(child) = child_pid.get_thread_group() {
837                                let signal = {
838                                    // Locking the reaper (an ancestor) before `self` and its
839                                    // children follows strictly top-down traversal in the process
840                                    // tree, avoiding cycles.
841                                    let _token = allow_subclass();
842                                    let mut child_state = child.write();
843
844                                    let signal = child_state.parent_death_signal_for_exiting_task(
845                                        task,
846                                        state.is_child_subreaper,
847                                    );
848                                    child_state.exit_signal = Some(SIGCHLD);
849                                    child_state.parent = Some(ThreadGroupParent::from_reaper(
850                                        Arc::downgrade(&reaper),
851                                    ));
852                                    signal
853                                };
854                                reaper_state.children.insert(child_pid);
855                                children.push((child, signal));
856                            }
857                        }
858                        reaper_state.zombie_children.append(&mut state.zombie_children);
859                    }
860                    ZombiePtracees::reparent(self, &reaper);
861
862                    for (child, _) in &children {
863                        child.kill_orphaned_pgrp(&pids, Some(self), None);
864                    }
865                } else {
866                    // Without a reaper, drop the zombies directly.
867                    let mut state = self.write();
868                    for child_pid in std::mem::take(&mut state.children) {
869                        if let Ok(child) = child_pid.get_thread_group() {
870                            let signal = {
871                                // Locking `self` (the parent) before its children follows strictly
872                                // top-down traversal in the process tree, avoiding cycles.
873                                let _token = allow_subclass();
874                                let mut child_state = child.write();
875                                let signal = child_state.parent_death_signal_for_exiting_task(
876                                    task,
877                                    state.is_child_subreaper,
878                                );
879                                child_state.parent = None;
880                                signal
881                            };
882                            children.push((child, signal));
883                        }
884                    }
885                    for zombie in state.zombie_children.drain(..) {
886                        zombie.release(&mut pids);
887                    }
888                }
889            }
890
891            self.send_parent_death_signals(
892                task,
893                children.into_iter().filter_map(|(child, signal)| Some((child, signal?))),
894            );
895
896            // Clear the `parent` reference now that children have been re-`parent`ed.
897            self.write().parent = None;
898
899            #[cfg(any(test, debug_assertions))]
900            {
901                let state = self.read();
902                assert!(state.zombie_children.is_empty());
903                assert!(state.zombie_ptracees.is_empty());
904            }
905
906            if let Some(ref parent) = parent {
907                let parent = parent.upgrade();
908
909                let tracer_tg = task
910                    .read()
911                    .ptrace
912                    .as_ref()
913                    .and_then(|ptrace| ptrace.core_state.thread_group.upgrade());
914
915                let maybe_zombie = match tracer_tg {
916                    Some(tracer_tg) => {
917                        tracer_tg.maybe_notify_tracer(task, &mut pids, &parent, zombie)
918                    }
919                    None => Some(zombie),
920                };
921
922                if let Some(zombie) = maybe_zombie {
923                    parent.do_zombie_notifications(zombie, &mut pids);
924                }
925            } else {
926                zombie.release(&mut pids);
927            }
928
929            // TODO: Set the error_code on the Zircon process object. Currently missing a way
930            // to do this in Zircon. Might be easier in the new execution model.
931
932            // Once the last zircon thread stops, the zircon process will also stop executing.
933
934            let parent_tg = parent.as_ref().map(|p| p.upgrade());
935            self.kill_orphaned_pgrp(&pids, parent_tg.as_deref(), Some(self));
936
937            self.write().set_exited();
938        } else {
939            pids.remove_task(&task.tid);
940            // From <https://man7.org/linux/man-pages/man2/PR_SET_PDEATHSIG.2const.html>:
941            //
942            //   The "parent" in this case is considered to be the thread that created
943            //   this process.  In other words, the signal will be sent when that thread
944            //   terminates (via, for example, pthread_exit(3)), rather than after all of
945            //   the threads in the parent process terminate.
946            let mut children_to_signal = Vec::new();
947            for child in state.children() {
948                // This allow_subclass is safe because we lock `self` (the parent) before
949                // locking its children.
950                let _token = allow_subclass();
951                let child_state = child.read();
952                if let Some(signal) = child_state.parent_death_signal_for_exiting_task(
953                    task, /* parent_is_subreaper = */ false,
954                ) {
955                    children_to_signal.push((child.clone(), signal));
956                }
957            }
958            std::mem::drop(state);
959            self.send_parent_death_signals(task, children_to_signal);
960        }
961    }
962
963    fn send_parent_death_signals(
964        &self,
965        task: &Arc<Task>,
966        children_to_signal: impl IntoIterator<Item = (Arc<ThreadGroup>, Signal)>,
967    ) {
968        let uid = task.real_creds().uid;
969        for (child, signal) in children_to_signal {
970            // `PR_SET_PDEATHSIG(2const)` requires `si_pid` in `siginfo_t` to contain the PID
971            // of the terminating parent process. In `sigaction(2)`, `si_pid` and `si_uid`
972            // reside in the `_kill` union variant (`SignalDetail::Kill`), which is decoded
973            // when `si_code` is `SI_USER` (0), matching the `si_code` observed on Linux.
974            child.write().send_signal(SignalInfo::with_detail(
975                signal,
976                SI_USER as i32,
977                SignalDetail::Kill { pid: self.leader.clone(), uid },
978            ));
979        }
980    }
981
982    /// Detach from any ptraced tasks, killing the ones that set `PTRACE_O_EXITKILL`.
983    fn detach_ptracees(&self, pids: &mut PidTableGuard<'_>) {
984        let tracee_tids = self.ptracees.lock().iter().map(|info| info.tid.clone()).collect_vec();
985        for tracee_tid in tracee_tids {
986            let Ok(tracee) = tracee_tid.get_task() else {
987                continue;
988            };
989
990            let mut should_send_sigkill = false;
991            if let Some(ptrace) = &tracee.read().ptrace {
992                should_send_sigkill = ptrace.has_option(PtraceOptions::EXITKILL);
993            }
994            if should_send_sigkill {
995                send_standard_signal(tracee.as_ref(), SignalInfo::kernel(SIGKILL));
996            }
997
998            let _ = ptrace_detach(
999                pids,
1000                PtraceTracer::Exiting(self),
1001                tracee.as_ref(),
1002                &UserAddress::NULL,
1003            );
1004        }
1005    }
1006
1007    pub fn do_zombie_notifications(
1008        &self,
1009        zombie: OwnedRef<ZombieProcess>,
1010        pids: &mut PidTableGuard<'_>,
1011    ) {
1012        let mut state = self.write();
1013
1014        state.children.remove(&zombie.task.pid);
1015        state.deferred_zombie_ptracers.retain(|dzp| dzp.tracee_pid != zombie.task.pid);
1016
1017        let exit_signal = zombie.exit_signal;
1018        let mut signal_info = zombie.to_wait_result().as_signal_info();
1019
1020        // From https://man7.org/linux/man-pages/man2/sigaction.2.html
1021        //
1022        // > SA_NOCLDWAIT (since Linux 2.6)
1023        // >
1024        // >     If signum is SIGCHLD, do not transform children into
1025        // >     zombies when they terminate.  See also waitpid(2).  This
1026        // >     flag is meaningful only when establishing a handler for
1027        // >     SIGCHLD, or when setting that signal's disposition to
1028        // >     SIG_DFL.
1029        let should_make_zombie = if exit_signal == Some(SIGCHLD) {
1030            let action = self.signal_actions.get(SIGCHLD);
1031            action.sa_handler != SIG_IGN && (action.sa_flags & SA_NOCLDWAIT as u64) == 0
1032        } else {
1033            true
1034        };
1035        if should_make_zombie {
1036            state.zombie_children.push(zombie);
1037        } else {
1038            state.reap_zombie(zombie, pids);
1039        }
1040
1041        state.lifecycle_waiters.notify_value(ThreadGroupLifecycleWaitValue::ChildStatus);
1042
1043        // Send signals
1044        if let Some(exit_signal) = exit_signal {
1045            signal_info.signal = exit_signal;
1046            state.send_signal(signal_info);
1047        }
1048    }
1049
1050    /// Notifies the tracer if appropriate.  Returns Some(zombie) if caller
1051    /// needs to notify the parent, None otherwise.  The caller should probably
1052    /// invoke parent.do_zombie_notifications(zombie) on the result.
1053    fn maybe_notify_tracer(
1054        &self,
1055        tracee: &Task,
1056        pids: &mut PidTableGuard<'_>,
1057        parent: &ThreadGroup,
1058        zombie: OwnedRef<ZombieProcess>,
1059    ) -> Option<OwnedRef<ZombieProcess>> {
1060        let mut state = self.write();
1061        if state.zombie_ptracees.has_tracee(&tracee.tid) {
1062            if self == parent {
1063                // The tracer is the parent and has not consumed the
1064                // notification.  Detach to clean up ptrace state, then
1065                // notify the parent by discarding the notification
1066                // generated by the detach and returning Some(zombie).
1067                if let Some(zombie_notification) = state.zombie_ptracees.detach(pids, &tracee.tid) {
1068                    zombie_notification.discard(pids);
1069                }
1070                return Some(zombie);
1071            } else {
1072                // The tracer is not the parent and the tracer has not consumed
1073                // the notification.
1074                if state.is_exited() {
1075                    // The tracer exited concurrently. Notify the parent.
1076                    return Some(zombie);
1077                }
1078
1079                // THREAD SAFETY: Release the tracer state lock before acquiring the parent state
1080                // lock to respect parent => child lock ordering.
1081                drop(state);
1082                {
1083                    // Tell the parent to expect a notification later.
1084                    let tracee_pgid = tracee.thread_group().read().process_group.clone();
1085                    let mut parent_state = parent.write();
1086                    parent_state.deferred_zombie_ptracers.push(DeferredZombiePTracer::new(
1087                        self,
1088                        tracee,
1089                        tracee_pgid,
1090                    ));
1091                    parent_state.children.remove(&tracee.pid);
1092                }
1093
1094                // Tell the tracer that there is a notification pending.
1095                // THREAD SAFETY: Checking for concurrent exit with is_exited(), releasing the
1096                // tracer state lock, then reacquiring the lock introduces a TOCTOU race. This
1097                // hazard is safe because exit synchronizes on the PidTable lock, which is held
1098                // continuously.
1099                let mut state = self.write();
1100                state.zombie_ptracees.set_parent_of(&tracee.tid, Some(zombie), parent);
1101                tracee.write().notify_ptracers();
1102                return None;
1103            }
1104        } else if self == parent {
1105            // The tracer is the parent and has already consumed the parent
1106            // notification.  No further action required.
1107            state.children.remove(&tracee.pid);
1108            zombie.release(pids);
1109            return None;
1110        }
1111        // The tracer is not the parent and has already consumed the parent
1112        // notification.  Notify the parent.
1113        Some(zombie)
1114    }
1115
1116    /// Find the task which will adopt our children after we die.
1117    fn find_reaper(&self) -> Option<ThreadGroupParent> {
1118        let mut weak_parent = self.read().parent.clone()?;
1119        loop {
1120            weak_parent = {
1121                let parent = weak_parent.upgrade();
1122                let parent_state = parent.read();
1123                if parent_state.is_child_subreaper {
1124                    break;
1125                }
1126                match parent_state.parent {
1127                    Some(ref next_parent) => next_parent.clone(),
1128                    None => break,
1129                }
1130            };
1131        }
1132        Some(weak_parent)
1133    }
1134
1135    pub fn setsid(&self) -> Result<(), Errno> {
1136        let mut pids = self.kernel.pids.lock();
1137        let pid = self.leader.clone();
1138
1139        // Calling thread group must not already be a process group or session leader.
1140        let scope = RcuReadScope::new();
1141        if self.leader.is_process_group(&scope) || self.leader.is_session(&scope) {
1142            return error!(EPERM);
1143        }
1144
1145        let self_arc = self.to_owned();
1146        let (old_process_group, check_old_pgrp, child_pgrps) = {
1147            let mut state = self.write();
1148            if !state.is_running() {
1149                return error!(ESRCH);
1150            }
1151            if state.session == self.leader || state.process_group == self.leader {
1152                return error!(EPERM);
1153            }
1154            let old_process_group = std::mem::replace(&mut state.process_group, pid.clone());
1155            let old_session = std::mem::replace(&mut state.session, pid.clone());
1156            state.controlling_terminal = None;
1157
1158            pids.detach_pgid(&old_process_group, self);
1159            pids.detach_sid(&old_session, self);
1160            pids.attach_pgid(&pid, &self_arc);
1161            pids.attach_sid(&pid, &self_arc);
1162
1163            let parent_tg = state.parent.as_ref().map(|p| p.upgrade());
1164            let mut child_pgrps = HashSet::new();
1165            for child in state.children() {
1166                let _token = allow_subclass();
1167                let child_state = child.read();
1168                if child_state.session == old_session
1169                    && child_state.process_group != old_process_group
1170                {
1171                    child_pgrps.insert(Arc::clone(&child_state.process_group));
1172                }
1173            }
1174            std::mem::drop(state);
1175
1176            let check_old_pgrp = parent_tg.is_some_and(|parent_tg| {
1177                let parent_state = parent_tg.read();
1178                parent_state.session == old_session
1179                    && parent_state.process_group != old_process_group
1180            });
1181
1182            (old_process_group, check_old_pgrp, child_pgrps)
1183        };
1184        std::mem::drop(scope);
1185
1186        // Evaluate the vacated process group and any child process groups in the old session
1187        // that may have lost their job-controlling parent.
1188        if check_old_pgrp {
1189            Self::check_orphaned_pgrp(&pids, &old_process_group, None);
1190        }
1191        for child_pgrp in child_pgrps {
1192            Self::check_orphaned_pgrp(&pids, &child_pgrp, None);
1193        }
1194
1195        Ok(())
1196    }
1197
1198    pub fn setpgid(
1199        &self,
1200        current_task: &CurrentTask,
1201        target: &Task,
1202        pgid: &Pid,
1203    ) -> Result<(), Errno> {
1204        let mut pids = self.kernel.pids.lock();
1205
1206        let (old_process_group, check_old_pgrp, check_new_pgrp) = {
1207            let current_session = Arc::clone(&self.read().session);
1208
1209            // The target process must be either the current process or a child of the current process.
1210            let mut target_thread_group = target.thread_group().write();
1211            if !target_thread_group.is_running() {
1212                return error!(ESRCH);
1213            }
1214            let parent_tg = target_thread_group.parent.as_ref().map(|p| p.upgrade());
1215            let is_target_current_process_child =
1216                parent_tg.as_ref().is_some_and(|tg| tg.leader == self.leader);
1217            if target_thread_group.base.leader != self.leader && !is_target_current_process_child {
1218                return error!(ESRCH);
1219            }
1220
1221            // If the target process is a child of the current task, it must not have executed an exec function.
1222            if is_target_current_process_child && target_thread_group.did_exec {
1223                return error!(EACCES);
1224            }
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.base.leader == target_thread_group.session
1228                || current_session != target_thread_group.session
1229            {
1230                return error!(EPERM);
1231            }
1232
1233            if *pgid == target_thread_group.process_group {
1234                return Ok(());
1235            }
1236
1237            // If joining an existing process group, it must be non-empty and belong to the same session.
1238            if *pgid != target_thread_group.base.leader {
1239                let scope = RcuReadScope::new();
1240                if !pgid.is_process_group_in_session(&target_thread_group.session, &scope) {
1241                    return error!(EPERM);
1242                }
1243            }
1244            security::check_setpgid_access(current_task, target)?;
1245
1246            let old_process_group =
1247                std::mem::replace(&mut target_thread_group.process_group, pgid.clone());
1248            pids.detach_pgid(&old_process_group, target.thread_group());
1249            pids.attach_pgid(pgid, target.thread_group());
1250
1251            let check_new_pgrp = target_thread_group.children().any(|child| {
1252                let _token = allow_subclass();
1253                child.read().process_group == *pgid
1254            });
1255            std::mem::drop(target_thread_group);
1256
1257            let check_old_pgrp = parent_tg.is_some_and(|parent_tg| {
1258                let parent_state = parent_tg.read();
1259                parent_state.session == current_session
1260                    && parent_state.process_group != old_process_group
1261            });
1262
1263            (old_process_group, check_old_pgrp, check_new_pgrp)
1264        };
1265
1266        // Evaluate the vacated and destination process groups for orphanhood if the move
1267        // removed an external job-controlling parent link.
1268        if check_old_pgrp {
1269            Self::check_orphaned_pgrp(&pids, &old_process_group, None);
1270        }
1271        if check_new_pgrp {
1272            Self::check_orphaned_pgrp(&pids, pgid, None);
1273        }
1274
1275        Ok(())
1276    }
1277
1278    fn itimer_real(&self) -> IntervalTimerHandle {
1279        self.timers.itimer_real()
1280    }
1281
1282    pub fn set_itimer(
1283        &self,
1284        current_task: &CurrentTask,
1285        which: u32,
1286        value: itimerval,
1287    ) -> Result<itimerval, Errno> {
1288        if which == ITIMER_PROF || which == ITIMER_VIRTUAL {
1289            // We don't support setting these timers.
1290            // The gvisor test suite clears ITIMER_PROF as part of its test setup logic, so we support
1291            // clearing these values.
1292            if value.it_value.tv_sec == 0 && value.it_value.tv_usec == 0 {
1293                return Ok(itimerval::default());
1294            }
1295            track_stub!(TODO("https://fxbug.dev/322874521"), "Unsupported itimer type", which);
1296            return error!(ENOTSUP);
1297        }
1298
1299        if which != ITIMER_REAL {
1300            return error!(EINVAL);
1301        }
1302        let itimer_real = self.itimer_real();
1303        let prev_remaining = itimer_real.time_remaining();
1304        if value.it_value.tv_sec != 0 || value.it_value.tv_usec != 0 {
1305            itimer_real.arm(current_task, itimerspec_from_itimerval(value), false)?;
1306        } else {
1307            itimer_real.disarm(current_task)?;
1308        }
1309        Ok(itimerval {
1310            it_value: timeval_from_duration(prev_remaining.remainder),
1311            it_interval: timeval_from_duration(prev_remaining.interval),
1312        })
1313    }
1314
1315    pub fn get_itimer(&self, which: u32) -> Result<itimerval, Errno> {
1316        if which == ITIMER_PROF || which == ITIMER_VIRTUAL {
1317            // We don't support setting these timers, so we can accurately report that these are not set.
1318            return Ok(itimerval::default());
1319        }
1320        if which != ITIMER_REAL {
1321            return error!(EINVAL);
1322        }
1323        let remaining = self.itimer_real().time_remaining();
1324        Ok(itimerval {
1325            it_value: timeval_from_duration(remaining.remainder),
1326            it_interval: timeval_from_duration(remaining.interval),
1327        })
1328    }
1329
1330    /// Check whether the stop state is compatible with `new_stopped`. If it is return it,
1331    /// otherwise, return None.
1332    fn check_stopped_state(
1333        &self,
1334        new_stopped: StopState,
1335        finalize_only: bool,
1336    ) -> Option<StopState> {
1337        let stopped = self.load_stopped();
1338        if finalize_only && !stopped.is_stopping_or_stopped() {
1339            return Some(stopped);
1340        }
1341
1342        if stopped.is_illegal_transition(new_stopped) {
1343            return Some(stopped);
1344        }
1345
1346        return None;
1347    }
1348
1349    /// Set the stop status of the process.  If you pass |siginfo| of |None|,
1350    /// does not update the signal.  If |finalize_only| is set, will check that
1351    /// the set will be a finalize (Stopping -> Stopped or Stopped -> Stopped)
1352    /// before executing it.
1353    ///
1354    /// Returns the latest stop state after any changes.
1355    pub fn set_stopped(
1356        &self,
1357        new_stopped: StopState,
1358        siginfo: Option<SignalInfo>,
1359        finalize_only: bool,
1360    ) -> StopState {
1361        // Perform an early return check to see if we can avoid taking the lock.
1362        if let Some(stopped) = self.check_stopped_state(new_stopped, finalize_only) {
1363            return stopped;
1364        }
1365
1366        self.write().set_stopped(new_stopped, siginfo, finalize_only)
1367    }
1368
1369    /// Returns the controlling terminal of this thread group, if any.
1370    pub fn controlling_terminal(&self) -> Option<ControllingTerminal> {
1371        self.read().controlling_terminal.clone()
1372    }
1373
1374    /// Ensures |session| is the controlling session inside of |controlling_session|.
1375    fn check_controlling_session(
1376        session: &Pid,
1377        controlling_session: &Option<ControllingSession>,
1378    ) -> Result<(), Errno> {
1379        if controlling_session.as_ref().is_some_and(|cs| Arc::ptr_eq(session, &cs.session)) {
1380            Ok(())
1381        } else {
1382            error!(ENOTTY)
1383        }
1384    }
1385
1386    pub fn get_foreground_process_group(&self, terminal: &Terminal) -> Result<pid_t, Errno> {
1387        let state = self.read();
1388        let terminal_state = terminal.read();
1389
1390        // "When fd does not refer to the controlling terminal of the calling
1391        // process, -1 is returned" - tcgetpgrp(3)
1392        Self::check_controlling_session(&state.session, &terminal_state.controlling_session)?;
1393        if !state
1394            .controlling_terminal
1395            .as_ref()
1396            .is_some_and(|ct| std::ptr::eq(terminal, Arc::as_ptr(&ct.terminal)))
1397        {
1398            return error!(ENOTTY);
1399        }
1400        let controlling_session =
1401            terminal_state.controlling_session.as_ref().ok_or_else(|| errno!(ENOTTY))?;
1402        Ok(controlling_session.foreground_pgid())
1403    }
1404
1405    pub fn set_foreground_process_group(
1406        &self,
1407        current_task: &CurrentTask,
1408        terminal: &Terminal,
1409        pgid: &Pid,
1410    ) -> Result<(), Errno> {
1411        let process_group;
1412        let send_ttou;
1413        {
1414            // Keep locks to ensure atomicity.
1415            let state = self.read();
1416            process_group = Arc::clone(&state.process_group);
1417            let mut terminal_state = terminal.write();
1418            Self::check_controlling_session(&state.session, &terminal_state.controlling_session)?;
1419            if !state
1420                .controlling_terminal
1421                .as_ref()
1422                .is_some_and(|ct| std::ptr::eq(terminal, Arc::as_ptr(&ct.terminal)))
1423            {
1424                return error!(ENOTTY);
1425            }
1426
1427            // Verify session membership lock-free under RcuReadScope.
1428            {
1429                let scope = RcuReadScope::new();
1430                if !pgid.is_process_group_in_session(&state.session, &scope) {
1431                    return error!(EPERM);
1432                }
1433            }
1434
1435            let controlling_session =
1436                terminal_state.controlling_session.as_mut().ok_or_else(|| errno!(ENOTTY))?;
1437
1438            // If the calling process is a member of a background group and not ignoring SIGTTOU, a
1439            // SIGTTOU signal is sent to all members of this background process group.
1440            send_ttou = process_group.id != controlling_session.foreground_pgid()
1441                && !current_task.read().signal_mask().has_signal(SIGTTOU)
1442                && self.signal_actions.get(SIGTTOU).sa_handler != SIG_IGN;
1443
1444            if !send_ttou {
1445                controlling_session.set_foreground_process_group(pgid.clone());
1446            }
1447        }
1448
1449        // Locks must not be held when sending signals.
1450        if send_ttou {
1451            process_group.send_signals_to_pgid(&[SIGTTOU]);
1452            return error!(EINTR);
1453        }
1454
1455        Ok(())
1456    }
1457
1458    pub fn set_controlling_terminal(
1459        &self,
1460        current_task: &CurrentTask,
1461        terminal: &Terminal,
1462        side: TerminalSide,
1463        steal: bool,
1464        is_readable: bool,
1465    ) -> Result<(), Errno> {
1466        let steal_from_session;
1467        let new_controlling_terminal = ControllingTerminal::new(terminal, side);
1468        {
1469            // Keep locks to ensure atomicity.
1470            let mut state = self.write();
1471            let mut terminal_state = terminal.write();
1472
1473            // "The calling process must be a session leader and not have a
1474            // controlling terminal already." - tty_ioctl(4)
1475            if state.session != self.leader {
1476                return error!(EPERM);
1477            }
1478
1479            if let Some(ref current_ct) = state.controlling_terminal {
1480                if current_ct.matches(terminal, side) {
1481                    return Ok(());
1482                } else {
1483                    return error!(EPERM);
1484                }
1485            }
1486
1487            let session_pid = state.session.clone();
1488            let other_session_pid = terminal_state.controlling_session().cloned();
1489            let mut has_admin_capability_determined = false;
1490            let mut stolen_session = None;
1491
1492            if let Some(other_sid) = other_session_pid {
1493                if other_sid != session_pid {
1494                    // "If this terminal is already the controlling terminal of a different
1495                    // session group, then the ioctl fails with EPERM, unless the caller
1496                    // has the CAP_SYS_ADMIN capability and arg equals 1, in which case the
1497                    // terminal is stolen, and all processes that had it as controlling
1498                    // terminal lose it." - tty_ioctl(4)
1499                    if !steal {
1500                        return error!(EPERM);
1501                    }
1502                    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1503                    has_admin_capability_determined = true;
1504                    stolen_session = Some(other_sid);
1505                }
1506            }
1507
1508            if !is_readable && !has_admin_capability_determined {
1509                security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1510            }
1511
1512            state.controlling_terminal = Some(new_controlling_terminal);
1513            terminal_state.controlling_session =
1514                Some(ControllingSession::new(&session_pid, &state.process_group));
1515            steal_from_session = stolen_session;
1516        }
1517
1518        // Steal the TTY away from the previous session's processes. Unlike TIOCNOTTY, don't send signals.
1519        if let Some(other_sid) = steal_from_session {
1520            other_sid.clear_controlling_terminal(Some((terminal, None)));
1521        }
1522
1523        Ok(())
1524    }
1525
1526    pub fn release_controlling_terminal(
1527        &self,
1528        _current_task: &CurrentTask,
1529        terminal: &Terminal,
1530        side: TerminalSide,
1531    ) -> Result<(), Errno> {
1532        let controlling_session = {
1533            // Keep locks to ensure atomicity.
1534            let mut state = self.write();
1535            let is_session_leader = state.session == self.leader;
1536            let mut terminal_state = terminal.write();
1537
1538            // tty must be the controlling terminal.
1539            Self::check_controlling_session(&state.session, &terminal_state.controlling_session)?;
1540            if !state.controlling_terminal.as_ref().is_some_and(|ct| ct.matches(terminal, side)) {
1541                return error!(ENOTTY);
1542            }
1543
1544            state.controlling_terminal = None;
1545            is_session_leader.then(|| terminal_state.controlling_session.take()).flatten()
1546        };
1547
1548        if let Some(controlling_session) = controlling_session {
1549            controlling_session.disassociate(terminal, Some(side));
1550        }
1551
1552        Ok(())
1553    }
1554
1555    /// Checks whether `pgrp` is orphaned and has stopped jobs, sending `SIGHUP` and `SIGCONT` if
1556    /// so.
1557    fn check_orphaned_pgrp(
1558        _pids: &PidTableGuard<'_>,
1559        pgrp: &Pid,
1560        ignored_tg: Option<&ThreadGroup>,
1561    ) {
1562        let scope = RcuReadScope::new();
1563        if pgrp.will_become_orphaned_pgrp(&scope, ignored_tg) && pgrp.has_stopped_jobs(&scope) {
1564            pgrp.send_signals_to_pgid(&[SIGHUP, SIGCONT]);
1565        }
1566    }
1567
1568    /// Evaluates whether this thread group's process group became newly orphaned
1569    /// due to process termination or reparenting, and sends SIGHUP + SIGCONT if stopped jobs exist.
1570    ///
1571    /// - `parent`: The parent thread group that was outside this group.
1572    /// - `ignored_tg`: An optional thread group to ignore during orphan checks.
1573    pub fn kill_orphaned_pgrp(
1574        &self,
1575        pids: &PidTableGuard<'_>,
1576        parent: Option<&ThreadGroup>,
1577        ignored_tg: Option<&ThreadGroup>,
1578    ) {
1579        let Some(parent_tg) = parent else {
1580            return;
1581        };
1582
1583        let (pgrp, my_sid) = {
1584            let state = self.read();
1585            (Arc::clone(&state.process_group), state.session.id)
1586        };
1587
1588        let (parent_pgid, parent_sid) = {
1589            let parent_state = parent_tg.read();
1590            (parent_state.process_group.id, parent_state.session.id)
1591        };
1592
1593        // Filter: Did the exiting parent provide an external link in the same session?
1594        if parent_pgid == pgrp.id || parent_sid != my_sid {
1595            return;
1596        }
1597
1598        Self::check_orphaned_pgrp(pids, &pgrp, ignored_tg);
1599    }
1600
1601    pub fn get_rlimit(&self, resource: Resource) -> u64 {
1602        self.limits.lock().get(resource).rlim_cur
1603    }
1604
1605    /// Adjusts the rlimits of the ThreadGroup to which `target_task` belongs to.
1606    pub fn adjust_rlimits(
1607        current_task: &CurrentTask,
1608        target_task: &Task,
1609        resource: Resource,
1610        maybe_new_limit: Option<rlimit>,
1611    ) -> Result<rlimit, Errno> {
1612        let thread_group = target_task.thread_group();
1613        let mut limit_state = thread_group.limits.lock();
1614        let old_limit = limit_state.get(resource);
1615        if let Some(new_limit) = maybe_new_limit {
1616            if new_limit.rlim_max > old_limit.rlim_max
1617                && !security::is_task_capable_noaudit(current_task, CAP_SYS_RESOURCE)
1618            {
1619                return error!(EPERM);
1620            }
1621            security::task_setrlimit(current_task, &target_task, old_limit, new_limit)?;
1622            limit_state.set(resource, new_limit)
1623        }
1624        Ok(old_limit)
1625    }
1626
1627    pub fn time_stats(&self) -> TaskTimeStats {
1628        let process: &zx::Process = if self.process.as_handle_ref().is_invalid() {
1629            // `process` must be valid for all tasks, except `kthreads`. In that case get the
1630            // stats from starnix process.
1631            assert_eq!(
1632                self as *const ThreadGroup,
1633                Arc::as_ptr(&self.kernel.kthreads.system_thread_group())
1634            );
1635            &self.kernel.kthreads.starnix_process
1636        } else {
1637            &self.process
1638        };
1639
1640        let info =
1641            zx::Task::get_runtime_info(process).expect("Failed to get starnix process stats");
1642        TaskTimeStats {
1643            user_time: zx::MonotonicDuration::from_nanos(info.cpu_time),
1644            // TODO(https://fxbug.dev/42078242): How can we calculate system time?
1645            system_time: zx::MonotonicDuration::default(),
1646        }
1647    }
1648
1649    /// For each task traced by this thread_group that matches the given
1650    /// selector, acquire its TaskMutableState and ptracees lock and execute the
1651    /// given function.
1652    pub fn get_ptracees_and(
1653        &self,
1654        selector: &ProcessSelector,
1655        f: &mut dyn FnMut(&Task, &TaskMutableState),
1656    ) {
1657        for task_ref in self
1658            .ptracees
1659            .lock()
1660            .iter()
1661            .filter(|info| selector.match_tid(&info.tid))
1662            .filter_map(|info| info.tid.get_task().ok())
1663        {
1664            let task_state = task_ref.write();
1665            if task_state.ptrace.is_some() {
1666                f(&task_ref, &task_state);
1667            }
1668        }
1669    }
1670
1671    /// Returns a tracee whose state has changed, so that waitpid can report on
1672    /// it. If this returns a value, and the pid is being traced, the tracer
1673    /// thread is deemed to have seen the tracee ptrace-stop for the purposes of
1674    /// PTRACE_LISTEN.
1675    pub fn get_waitable_ptracee(
1676        &self,
1677        selector: &ProcessSelector,
1678        options: &WaitingOptions,
1679        pids: &mut PidTableGuard<'_>,
1680    ) -> Option<WaitResult> {
1681        // This checks to see if the target is a zombie ptracee.
1682        let waitable_entry = self.write().zombie_ptracees.get_waitable_entry(selector, options);
1683        match waitable_entry {
1684            None => (),
1685            Some((zombie, None)) => return Some(zombie.to_wait_result()),
1686            Some((zombie, Some((tg, z)))) => {
1687                if let Some(tg) = tg.upgrade() {
1688                    if Arc::as_ptr(&tg) != self as *const Self {
1689                        tg.do_zombie_notifications(z, pids);
1690                    } else {
1691                        {
1692                            let mut state = tg.write();
1693                            state.children.remove(&z.task.pid);
1694                            state
1695                                .deferred_zombie_ptracers
1696                                .retain(|dzp| dzp.tracee_pid != z.task.pid);
1697                        }
1698
1699                        z.release(pids);
1700                    };
1701                }
1702                return Some(zombie.to_wait_result());
1703            }
1704        }
1705
1706        let mut tasks = vec![];
1707
1708        // This checks to see if the target is a running ptracee.
1709        self.get_ptracees_and(selector, &mut |task: &Task, _| {
1710            tasks.push(task.weak_self.clone());
1711        });
1712        for task in tasks {
1713            let Some(task_ref) = task.upgrade() else {
1714                continue;
1715            };
1716
1717            let process_state = &mut task_ref.thread_group().write();
1718            let mut task_state = task_ref.write();
1719            if task_state
1720                .ptrace
1721                .as_ref()
1722                .is_some_and(|ptrace| ptrace.is_waitable(task_ref.load_stopped(), options))
1723            {
1724                // We've identified a potential target.  Need to return either
1725                // the process's information (if we are in group-stop) or the
1726                // thread's information (if we are in a different stop).
1727
1728                // The shared information:
1729                let info = process_state.tasks.iter().next().unwrap().clone();
1730                let uid = info.real_creds().uid;
1731                let mut exit_status = None;
1732                let exit_signal = process_state.exit_signal.clone();
1733                let time_stats =
1734                    process_state.base.time_stats() + process_state.children_time_stats;
1735                let task_stopped = task_ref.load_stopped();
1736
1737                #[derive(PartialEq)]
1738                enum ExitType {
1739                    None,
1740                    Cont,
1741                    Stop,
1742                    Kill,
1743                }
1744                if process_state.is_waitable() {
1745                    let ptrace = &mut task_state.ptrace;
1746                    // The information for processes, if we were in group stop.
1747                    let process_stopped = process_state.base.load_stopped();
1748                    let mut fn_type = ExitType::None;
1749                    if process_stopped == StopState::Awake && options.wait_for_continued {
1750                        fn_type = ExitType::Cont;
1751                    }
1752                    let mut event = ptrace
1753                        .as_ref()
1754                        .map_or(PtraceEvent::None, |ptrace| {
1755                            ptrace.event_data.as_ref().map_or(PtraceEvent::None, |data| data.event)
1756                        })
1757                        .clone();
1758                    // Tasks that are ptrace'd always get stop notifications.
1759                    if process_stopped == StopState::GroupStopped
1760                        && (options.wait_for_stopped || ptrace.is_some())
1761                    {
1762                        fn_type = ExitType::Stop;
1763                    }
1764                    if fn_type != ExitType::None {
1765                        let siginfo = if options.keep_waitable_state {
1766                            process_state.last_signal.clone()
1767                        } else {
1768                            process_state.last_signal.take()
1769                        };
1770                        if let Some(mut siginfo) = siginfo {
1771                            if task_ref.thread_group().load_stopped() == StopState::GroupStopped
1772                                && ptrace.as_ref().is_some_and(|ptrace| ptrace.is_seized())
1773                            {
1774                                if event == PtraceEvent::None {
1775                                    event = PtraceEvent::Stop;
1776                                }
1777                                siginfo.code |= (PtraceEvent::Stop as i32) << 8;
1778                            }
1779                            if siginfo.signal == SIGKILL {
1780                                fn_type = ExitType::Kill;
1781                            }
1782                            exit_status = match fn_type {
1783                                ExitType::Stop => Some((
1784                                    ExitStatus::Stop(siginfo, event),
1785                                    process_state.base.leader.clone(),
1786                                )),
1787                                ExitType::Cont => Some((
1788                                    ExitStatus::Continue(siginfo, event),
1789                                    process_state.base.leader.clone(),
1790                                )),
1791                                ExitType::Kill => Some((
1792                                    ExitStatus::Kill(siginfo),
1793                                    process_state.base.leader.clone(),
1794                                )),
1795                                _ => None,
1796                            };
1797                        }
1798                        // Clear the wait status of the ptrace, because we're
1799                        // using the tg status instead.
1800                        ptrace
1801                            .as_mut()
1802                            .map(|ptrace| ptrace.get_last_signal(options.keep_waitable_state));
1803                    }
1804                }
1805                if exit_status.is_none() {
1806                    if let Some(ptrace) = task_state.ptrace.as_mut() {
1807                        // The information for the task, if we were in a non-group stop.
1808                        let mut fn_type = ExitType::None;
1809                        let event = ptrace
1810                            .event_data
1811                            .as_ref()
1812                            .map_or(PtraceEvent::None, |event| event.event);
1813                        if task_stopped == StopState::Awake {
1814                            fn_type = ExitType::Cont;
1815                        }
1816                        if task_stopped.is_stopping_or_stopped()
1817                            || ptrace.stop_status == PtraceStatus::Listening
1818                        {
1819                            fn_type = ExitType::Stop;
1820                        }
1821                        if fn_type != ExitType::None {
1822                            if let Some(siginfo) =
1823                                ptrace.get_last_signal(options.keep_waitable_state)
1824                            {
1825                                if siginfo.signal == SIGKILL {
1826                                    fn_type = ExitType::Kill;
1827                                }
1828                                exit_status = match fn_type {
1829                                    ExitType::Stop => Some((
1830                                        ExitStatus::Stop(siginfo, event),
1831                                        task_ref.tid.clone(),
1832                                    )),
1833                                    ExitType::Cont => Some((
1834                                        ExitStatus::Continue(siginfo, event),
1835                                        task_ref.tid.clone(),
1836                                    )),
1837                                    ExitType::Kill => {
1838                                        Some((ExitStatus::Kill(siginfo), task_ref.tid.clone()))
1839                                    }
1840                                    _ => None,
1841                                };
1842                            }
1843                        }
1844                    }
1845                }
1846                if let Some((exit_status, pid)) = exit_status {
1847                    return Some(WaitResult {
1848                        pid,
1849                        uid,
1850                        zombie_state: ZombieState { exit_status, time_stats },
1851                        exit_signal,
1852                    });
1853                }
1854            }
1855        }
1856        None
1857    }
1858
1859    /// Attempts to send an unchecked signal to this thread group.
1860    ///
1861    /// - `current_task`: The task that is sending the signal.
1862    /// - `unchecked_signal`: The signal that is to be sent. Unchecked, since `0` is a sentinel value
1863    /// where rights are to be checked but no signal is actually sent.
1864    ///
1865    /// # Returns
1866    /// Returns Ok(()) if the signal was sent, or the permission checks passed with a 0 signal, otherwise
1867    /// the error that was encountered.
1868    pub fn send_signal_unchecked(
1869        &self,
1870        current_task: &CurrentTask,
1871        unchecked_signal: UncheckedSignal,
1872    ) -> Result<(), Errno> {
1873        if let Some(signal) = self.check_signal_access(current_task, unchecked_signal)? {
1874            let signal_info = SignalInfo::with_detail(
1875                signal,
1876                SI_USER as i32,
1877                SignalDetail::Kill {
1878                    pid: current_task.pid.clone(),
1879                    uid: current_task.current_creds().uid,
1880                },
1881            );
1882
1883            self.write().send_signal(signal_info);
1884        }
1885
1886        Ok(())
1887    }
1888
1889    /// Sends a signal to this thread_group without performing any access checks.
1890    ///
1891    /// # Safety
1892    /// This is unsafe, because it should only be called by tools and tests.
1893    pub unsafe fn send_signal_unchecked_debug(
1894        &self,
1895        current_task: &CurrentTask,
1896        unchecked_signal: UncheckedSignal,
1897    ) -> Result<(), Errno> {
1898        let signal = Signal::try_from(unchecked_signal)?;
1899        let signal_info = SignalInfo::with_detail(
1900            signal,
1901            SI_USER as i32,
1902            SignalDetail::Kill {
1903                pid: current_task.pid.clone(),
1904                uid: current_task.current_creds().uid,
1905            },
1906        );
1907
1908        self.write().send_signal(signal_info);
1909        Ok(())
1910    }
1911
1912    /// Attempts to send an unchecked signal to this thread group, with info read from
1913    /// `siginfo_ref`.
1914    ///
1915    /// - `current_task`: The task that is sending the signal.
1916    /// - `unchecked_signal`: The signal that is to be sent. Unchecked, since `0` is a sentinel value
1917    /// where rights are to be checked but no signal is actually sent.
1918    /// - `siginfo_ref`: The siginfo that will be enqueued.
1919    /// - `options`: Options for how to convert the siginfo into a signal info.
1920    ///
1921    /// # Returns
1922    /// Returns Ok(()) if the signal was sent, or the permission checks passed with a 0 signal, otherwise
1923    /// the error that was encountered.
1924    #[track_caller]
1925    pub fn send_signal_unchecked_with_info(
1926        &self,
1927        current_task: &CurrentTask,
1928        unchecked_signal: UncheckedSignal,
1929        siginfo_ref: UserAddress,
1930        options: IntoSignalInfoOptions,
1931    ) -> Result<(), Errno> {
1932        let siginfo = UncheckedSignalInfo::read_from_siginfo(current_task, siginfo_ref)?;
1933        if self.leader.id != current_task.get_pid()
1934            && (siginfo.code() >= 0 || siginfo.code() == SI_TKILL)
1935        {
1936            return error!(EPERM);
1937        }
1938
1939        if matches!(options, IntoSignalInfoOptions::CheckSigno)
1940            && siginfo.signo() as u64 != unchecked_signal.raw()
1941        {
1942            return error!(EINVAL);
1943        }
1944
1945        if let Some(signal) = self.check_signal_access(current_task, unchecked_signal)? {
1946            self.write().send_signal(siginfo.into_signal_info(signal, options)?);
1947        }
1948
1949        Ok(())
1950    }
1951
1952    /// Checks whether or not `current_task` can signal this thread group with `unchecked_signal`.
1953    ///
1954    /// Returns:
1955    ///   - `Ok(Some(Signal))` if the signal passed checks and should be sent.
1956    ///   - `Ok(None)` if the signal passed checks, but should not be sent. This is used by
1957    ///   userspace for permission checks.
1958    ///   - `Err(_)` if the permission checks failed.
1959    fn check_signal_access(
1960        &self,
1961        current_task: &CurrentTask,
1962        unchecked_signal: UncheckedSignal,
1963    ) -> Result<Option<Signal>, Errno> {
1964        // Pick an arbitrary task in thread_group to check permissions.
1965        //
1966        // Tasks can technically have different credentials, but in practice they are kept in sync.
1967        let Some(target_task) = self.read().get_signalable_task() else {
1968            // If there are no signalable tasks in the thread group, all tasks have exited and the
1969            // process is terminating or a zombie. A signal sent to a terminating or zombie process
1970            // is ignored, matching the behavior for Zombie processes in sys_kill.
1971            return Ok(None);
1972        };
1973        current_task.can_signal(&target_task, unchecked_signal)?;
1974
1975        // 0 is a sentinel value used to do permission checks.
1976        if unchecked_signal.is_zero() {
1977            return Ok(None);
1978        }
1979
1980        let signal = Signal::try_from(unchecked_signal)?;
1981        security::check_signal_access(current_task, &target_task, signal)?;
1982
1983        Ok(Some(signal))
1984    }
1985
1986    pub fn has_signal_queued(&self, signal: Signal) -> bool {
1987        self.pending_signals.lock().has_queued(signal)
1988    }
1989
1990    pub fn num_signals_queued(&self) -> usize {
1991        self.pending_signals.lock().num_queued()
1992    }
1993
1994    pub fn get_pending_signals(&self) -> SigSet {
1995        self.pending_signals.lock().pending()
1996    }
1997
1998    pub fn is_any_signal_allowed_by_mask(&self, mask: SigSet) -> bool {
1999        self.pending_signals.lock().is_any_allowed_by_mask(mask)
2000    }
2001
2002    pub fn take_next_signal_where<F>(&self, predicate: F) -> Option<SignalInfo>
2003    where
2004        F: Fn(&SignalInfo) -> bool,
2005    {
2006        let mut signals = self.pending_signals.lock();
2007        let r = signals.take_next_where(predicate);
2008        self.has_pending_signals.store(!signals.is_empty(), Ordering::Relaxed);
2009        r
2010    }
2011
2012    /// Drive this `ThreadGroup` to exit, allowing it time to handle SIGTERM before sending SIGKILL.
2013    ///
2014    /// Returns once `ThreadGroup::exit()` has completed.
2015    ///
2016    /// Must be called from the system task.
2017    pub async fn shut_down(this: Weak<Self>) {
2018        const SHUTDOWN_SIGNAL_HANDLING_TIMEOUT: zx::MonotonicDuration =
2019            zx::MonotonicDuration::from_seconds(1);
2020
2021        // Prepare for shutting down the thread group.
2022        let (tg_name, mut on_exited) = {
2023            // Nest this upgraded access so upgraded references aren't held across await-points.
2024            let Some(this) = this.upgrade() else {
2025                return;
2026            };
2027
2028            let mut state = this.write();
2029            if state.is_exited() {
2030                // Do not set an exit notifier on an exited thread group. It will never be notified.
2031                return;
2032            }
2033
2034            // Register a channel to be notified when exit() is complete.
2035            let (on_exited_send, on_exited) = futures::channel::oneshot::channel();
2036            state.exit_notifier = Some(on_exited_send);
2037
2038            // We want to be able to log about this thread group without upgrading the `Weak`.
2039            let tg_name = format!("{this:?}");
2040
2041            (tg_name, on_exited)
2042        };
2043
2044        log_debug!(tg:% = tg_name; "shutting down thread group, sending SIGTERM");
2045        this.upgrade().map(|tg| tg.write().send_signal(SignalInfo::kernel(SIGTERM)));
2046
2047        // Give thread groups some time to handle SIGTERM, proceeding early if they exit
2048        let timeout = fuchsia_async::Timer::new(SHUTDOWN_SIGNAL_HANDLING_TIMEOUT);
2049        futures::pin_mut!(timeout);
2050
2051        // Use select_biased instead of on_timeout() so that we can await on on_exited later
2052        futures::select_biased! {
2053            _ = &mut on_exited => (),
2054            _ = timeout => {
2055                log_debug!(tg:% = tg_name; "sending SIGKILL");
2056                this.upgrade().map(|tg| tg.write().send_signal(SignalInfo::kernel(SIGKILL)));
2057            },
2058        };
2059
2060        log_debug!(tg:% = tg_name; "waiting for exit");
2061        // It doesn't matter whether ThreadGroup::exit() was called or the process exited with
2062        // a return code and dropped the sender end of the channel.
2063        on_exited.await.ok();
2064        log_debug!(tg:% = tg_name; "thread group shutdown complete");
2065    }
2066
2067    /// Returns the KOID of the process for this thread group.
2068    /// This method should be used to when mapping 32 bit linux process ids to KOIDs
2069    /// to avoid breaking the encapsulation of the zx::process within the ThreadGroup.
2070    /// This encapsulation is important since the relationship between the ThreadGroup
2071    /// and the Process may change over time. See [ThreadGroup::process] for more details.
2072    pub fn get_process_koid(&self) -> Result<Koid, Status> {
2073        self.process.koid()
2074    }
2075}
2076
2077pub enum WaitableChildResult {
2078    ReadyNow(Box<WaitResult>),
2079    ShouldWait,
2080    NoneFound,
2081}
2082
2083#[apply(state_implementation!)]
2084impl ThreadGroupMutableState<Base = ThreadGroup> {
2085    /// Returns the parent-death [`Signal`] configured for this process via `PR_SET_PDEATHSIG`.
2086    pub fn parent_death_signal(&self) -> Option<Signal> {
2087        self.parent_death_signal
2088    }
2089
2090    /// Sets or clears the parent-death [`Signal`] for this process.
2091    pub fn set_parent_death_signal(&mut self, signal: Option<Signal>) {
2092        self.parent_death_signal = signal;
2093    }
2094
2095    /// Returns the parent-death [`Signal`] to deliver to this child when `exiting_task` in its
2096    /// parent [`ThreadGroup`] terminates, or `None` if no signal should be delivered.
2097    ///
2098    /// Prior to reparenting (`creator_task` is `Some`), the signal is delivered only when the
2099    /// specific creating [`Task`] exits. Once reparented to an ancestor reaper (`creator_task` is
2100    /// `None`), the signal is delivered only if the terminating parent is a subreaper
2101    /// (`parent_is_subreaper` is `true` on final process exit).
2102    fn parent_death_signal_for_exiting_task(
2103        &self,
2104        exiting_task: &Arc<Task>,
2105        parent_is_subreaper: bool,
2106    ) -> Option<Signal> {
2107        let should_signal =
2108            self.parent.as_ref().is_some_and(|parent| match parent.creator_task() {
2109                Some(creator_task) => Weak::ptr_eq(creator_task, &exiting_task.weak_self),
2110                None => parent_is_subreaper,
2111            });
2112        should_signal.then_some(self.parent_death_signal).flatten()
2113    }
2114
2115    pub fn leader(&self) -> pid_t {
2116        self.base.leader.id
2117    }
2118
2119    pub fn leader_command(&self) -> TaskCommand {
2120        self.get_task(self.leader())
2121            .map(|l| l.command())
2122            .unwrap_or_else(|| TaskCommand::new(b"<leader exited>"))
2123    }
2124
2125    pub fn is_running(&self) -> bool {
2126        matches!(self.run_state, ThreadGroupRunState::Running)
2127    }
2128
2129    pub fn is_exited(&self) -> bool {
2130        matches!(self.run_state, ThreadGroupRunState::Exited(_))
2131    }
2132
2133    /// Transitions this [`ThreadGroup`] to [`ThreadGroupRunState::Exiting`].
2134    fn set_exiting(&mut self, exit_status: ExitStatus) {
2135        self.run_state = ThreadGroupRunState::Exiting(exit_status);
2136    }
2137
2138    fn set_exited(&mut self) {
2139        let ThreadGroupRunState::Exiting(exit_status) = std::mem::take(&mut self.run_state) else {
2140            panic!("Must transition from Exiting to Exited");
2141        };
2142        self.run_state = ThreadGroupRunState::Exited(exit_status);
2143
2144        if let Some(notifier) = self.exit_notifier.take() {
2145            let _ = notifier.send(());
2146        }
2147    }
2148
2149    /// Returns the running child thread groups.
2150    ///
2151    /// A `Pid` in `self.children` may not resolve to a `ThreadGroup` while a child is in the
2152    /// middle of task creation (before `PidTableGuard::add_task`) or exit (between
2153    /// `PidTableGuard::kill_process` and removing itself from `parent.children`).
2154    pub fn children(&self) -> impl Iterator<Item = Arc<ThreadGroup>> + '_ {
2155        self.children.iter().flat_map(|pid| pid.get_thread_group().ok())
2156    }
2157
2158    pub fn tasks(&self) -> Vec<Arc<Task>> {
2159        self.tasks.iter().flat_map(|info| info.tid.get_task().ok()).collect()
2160    }
2161
2162    pub fn first_task(&self) -> Option<Arc<Task>> {
2163        self.tasks.iter().find_map(|info| info.tid.get_task().ok())
2164    }
2165
2166    pub fn task_ids(&self) -> impl Iterator<Item = tid_t> + '_ {
2167        self.tasks.iter().map(|info| info.tid.id)
2168    }
2169
2170    pub fn contains_task(&self, tid: tid_t) -> bool {
2171        self.tasks.iter().any(|info| info.tid.id == tid)
2172    }
2173
2174    pub fn get_task(&self, tid: tid_t) -> Option<Arc<Task>> {
2175        self.tasks.iter().find(|info| info.tid.id == tid).and_then(|info| info.tid.get_task().ok())
2176    }
2177
2178    pub fn tasks_count(&self) -> usize {
2179        self.tasks.len()
2180    }
2181
2182    pub fn get_ppid(&self) -> pid_t {
2183        match &self.parent {
2184            Some(parent) => parent.upgrade().leader.id,
2185            None => 0,
2186        }
2187    }
2188
2189    /// Reaps the given zombie, making its PID available for reuse.
2190    fn reap_zombie(&mut self, zombie: OwnedRef<ZombieProcess>, pids: &mut PidTableGuard<'_>) {
2191        self.children_time_stats += zombie.state.time_stats;
2192        zombie.release(pids);
2193    }
2194
2195    /// Indicates whether the thread group is waitable via waitid and waitpid for
2196    /// either WSTOPPED or WCONTINUED.
2197    pub fn is_waitable(&self) -> bool {
2198        return self.last_signal.is_some() && !self.base.load_stopped().is_in_progress();
2199    }
2200
2201    pub fn get_waitable_zombie(
2202        &mut self,
2203        zombie_list: &dyn Fn(&mut ThreadGroupMutableState) -> &mut Vec<OwnedRef<ZombieProcess>>,
2204        selector: &ProcessSelector,
2205        options: &WaitingOptions,
2206        pids: &mut PidTableGuard<'_>,
2207    ) -> Option<WaitResult> {
2208        // We look for the last zombie in the vector that matches pid selector and waiting options
2209        let selected_zombie_position = zombie_list(self)
2210            .iter()
2211            .rev()
2212            .position(|zombie| zombie.matches_selector_and_waiting_option(selector, options))
2213            .map(|position_starting_from_the_back| {
2214                zombie_list(self).len() - 1 - position_starting_from_the_back
2215            });
2216
2217        selected_zombie_position.map(|position| {
2218            if options.keep_waitable_state {
2219                zombie_list(self)[position].to_wait_result()
2220            } else {
2221                let zombie = zombie_list(self).remove(position);
2222                let result = zombie.to_wait_result();
2223                self.reap_zombie(zombie, pids);
2224                result
2225            }
2226        })
2227    }
2228
2229    pub fn is_correct_exit_signal(for_clone: bool, exit_code: Option<Signal>) -> bool {
2230        for_clone == (exit_code != Some(SIGCHLD))
2231    }
2232
2233    fn get_waitable_running_children(
2234        &self,
2235        selector: &ProcessSelector,
2236        options: &WaitingOptions,
2237    ) -> WaitableChildResult {
2238        // The children whose pid matches the pid selector queried.
2239        let filter_children_by_pid_selector = |child: &ThreadGroup| match selector {
2240            ProcessSelector::Any => true,
2241            ProcessSelector::Pid(pid) => &child.leader == pid,
2242            ProcessSelector::Pgid(pgid) => {
2243                // This allow_subclass is safe because the lock is being acquired
2244                // in a strictly top-down traversal of the ThreadGroup tree (from parent
2245                // to child), so no lock ordering cycles can be formed.
2246                let _token = allow_subclass();
2247                &child.read().process_group == pgid
2248            }
2249        };
2250
2251        // The children whose exit signal matches the waiting options queried.
2252        let filter_children_by_waiting_options = |child: &ThreadGroup| {
2253            if options.wait_for_all {
2254                return true;
2255            }
2256            // This allow_subclass is safe because the lock is being acquired
2257            // in a strictly top-down traversal of the ThreadGroup tree (from parent
2258            // to child), so no lock ordering cycles can be formed.
2259            let _token = allow_subclass();
2260            Self::is_correct_exit_signal(options.wait_for_clone, child.read().exit_signal)
2261        };
2262
2263        // If wait_for_exited flag is disabled or no exited children were found we look for running
2264        // children.
2265        let mut selected_children = self
2266            .children()
2267            .filter(|tg| filter_children_by_pid_selector(&tg))
2268            .filter(|tg| filter_children_by_waiting_options(&tg))
2269            .peekable();
2270        if selected_children.peek().is_none() {
2271            // There still might be a process that ptrace hasn't looked at yet.
2272            if self.deferred_zombie_ptracers.iter().any(|dzp| match selector {
2273                ProcessSelector::Any => true,
2274                ProcessSelector::Pid(pid) => &dzp.tracee_pid == pid,
2275                ProcessSelector::Pgid(pgid) => &dzp.tracee_pgid == pgid,
2276            }) {
2277                return WaitableChildResult::ShouldWait;
2278            }
2279
2280            return WaitableChildResult::NoneFound;
2281        }
2282        for child in selected_children {
2283            // This allow_subclass is safe because the lock is being acquired
2284            // in a strictly top-down traversal of the ThreadGroup tree (from parent
2285            // to child), so no lock ordering cycles can be formed.
2286            let _token = allow_subclass();
2287            let child = child.write();
2288            if child.last_signal.is_some() {
2289                let build_wait_result = |mut child: ThreadGroupWriteGuard<'_>,
2290                                         exit_status: &dyn Fn(SignalInfo) -> ExitStatus|
2291                 -> WaitResult {
2292                    let siginfo = if options.keep_waitable_state {
2293                        child.last_signal.clone().unwrap()
2294                    } else {
2295                        child.last_signal.take().unwrap()
2296                    };
2297                    let exit_status = if siginfo.signal == SIGKILL {
2298                        // This overrides the stop/continue choice.
2299                        ExitStatus::Kill(siginfo)
2300                    } else {
2301                        exit_status(siginfo)
2302                    };
2303                    let info = child.tasks.iter().next().unwrap();
2304                    let uid = info.real_creds().uid;
2305                    WaitResult {
2306                        pid: child.base.leader.clone(),
2307                        uid,
2308                        zombie_state: ZombieState {
2309                            exit_status,
2310                            time_stats: child.base.time_stats() + child.children_time_stats,
2311                        },
2312                        exit_signal: child.exit_signal,
2313                    }
2314                };
2315                let child_stopped = child.base.load_stopped();
2316                if child_stopped == StopState::Awake && options.wait_for_continued {
2317                    return WaitableChildResult::ReadyNow(Box::new(build_wait_result(
2318                        child,
2319                        &|siginfo| ExitStatus::Continue(siginfo, PtraceEvent::None),
2320                    )));
2321                }
2322                if child_stopped == StopState::GroupStopped && options.wait_for_stopped {
2323                    return WaitableChildResult::ReadyNow(Box::new(build_wait_result(
2324                        child,
2325                        &|siginfo| ExitStatus::Stop(siginfo, PtraceEvent::None),
2326                    )));
2327                }
2328            }
2329        }
2330
2331        WaitableChildResult::ShouldWait
2332    }
2333
2334    /// Returns any waitable child matching the given `selector` and `options`. Returns None if no
2335    /// child matching the selector is waitable. Returns ECHILD if no child matches the selector at
2336    /// all.
2337    ///
2338    /// Will remove the waitable status from the child depending on `options`.
2339    pub fn get_waitable_child(
2340        &mut self,
2341        selector: &ProcessSelector,
2342        options: &WaitingOptions,
2343        pids: &mut PidTableGuard<'_>,
2344    ) -> WaitableChildResult {
2345        if options.wait_for_exited {
2346            if let Some(waitable_zombie) = self.get_waitable_zombie(
2347                &|state: &mut ThreadGroupMutableState| &mut state.zombie_children,
2348                selector,
2349                options,
2350                pids,
2351            ) {
2352                return WaitableChildResult::ReadyNow(Box::new(waitable_zombie));
2353            }
2354        }
2355
2356        self.get_waitable_running_children(selector, options)
2357    }
2358
2359    /// Returns a running task in the current thread group.
2360    pub fn get_running_task(&self) -> Result<Arc<Task>, Errno> {
2361        self.tasks
2362            .iter()
2363            .find_map(|info| info.tid.get_task().ok().filter(|task| task.is_running()))
2364            .ok_or_else(|| errno!(ESRCH))
2365    }
2366
2367    /// Returns a running task with an intact file table in the current thread group, preferring
2368    /// the thread group leader.
2369    pub fn get_running_task_with_files(&self) -> Result<Arc<Task>, Errno> {
2370        if let Ok(task) = self.base.leader.get_task() {
2371            if task.is_running() && task.files().is_ok() {
2372                return Ok(task);
2373            }
2374        }
2375        self.tasks
2376            .iter()
2377            .find_map(|info| {
2378                info.tid.get_task().ok().filter(|task| task.is_running() && task.files().is_ok())
2379            })
2380            .ok_or_else(|| errno!(ESRCH))
2381    }
2382
2383    /// Returns a task representative of the [`ThreadGroup`] for signal access checks.
2384    ///
2385    /// Prefers a running task, but falls back to the first available non-running task.
2386    /// Returns `None` if the task list is empty or no tasks can be upgraded.
2387    fn get_signalable_task(&self) -> Option<Arc<Task>> {
2388        let mut non_running = if let Ok(task) = self.base.leader.get_task() {
2389            if task.is_running() {
2390                return Some(task);
2391            }
2392            Some(task)
2393        } else {
2394            None
2395        };
2396        for container in &self.tasks {
2397            if let Ok(task) = container.tid.get_task() {
2398                if task.is_running() {
2399                    return Some(task);
2400                }
2401                if non_running.is_none() {
2402                    non_running = Some(task);
2403                }
2404            }
2405        }
2406        non_running
2407    }
2408
2409    /// Set the stop status of the process.  If you pass |siginfo| of |None|,
2410    /// does not update the signal.  If |finalize_only| is set, will check that
2411    /// the set will be a finalize (Stopping -> Stopped or Stopped -> Stopped)
2412    /// before executing it.
2413    ///
2414    /// Returns the latest stop state after any changes.
2415    pub fn set_stopped(
2416        mut self,
2417        new_stopped: StopState,
2418        siginfo: Option<SignalInfo>,
2419        finalize_only: bool,
2420    ) -> StopState {
2421        if let Some(stopped) = self.base.check_stopped_state(new_stopped, finalize_only) {
2422            return stopped;
2423        }
2424
2425        // Thread groups don't transition to group stop if they are waking, because waking
2426        // means something told it to wake up (like a SIGCONT) but hasn't finished yet.
2427        if self.base.load_stopped() == StopState::Waking
2428            && (new_stopped == StopState::GroupStopping || new_stopped == StopState::GroupStopped)
2429        {
2430            return self.base.load_stopped();
2431        }
2432
2433        // TODO(https://g-issues.fuchsia.dev/issues/306438676): When thread
2434        // group can be stopped inside user code, tasks/thread groups will
2435        // need to be either restarted or stopped here.
2436        self.store_stopped(new_stopped);
2437        if let Some(signal) = &siginfo {
2438            // We don't want waiters to think the process was unstopped
2439            // because of a sigkill.  They will get woken when the
2440            // process dies.
2441            if signal.signal != SIGKILL {
2442                self.last_signal = siginfo;
2443            }
2444        }
2445        if new_stopped == StopState::Waking || new_stopped == StopState::ForceWaking {
2446            self.lifecycle_waiters.notify_value(ThreadGroupLifecycleWaitValue::Stopped);
2447        };
2448
2449        let parent = (!new_stopped.is_in_progress()).then(|| self.parent.clone()).flatten();
2450
2451        // Drop the lock before locking the parent.
2452        std::mem::drop(self);
2453        if let Some(parent) = parent {
2454            let parent = parent.upgrade();
2455            parent
2456                .write()
2457                .lifecycle_waiters
2458                .notify_value(ThreadGroupLifecycleWaitValue::ChildStatus);
2459        }
2460
2461        new_stopped
2462    }
2463
2464    fn store_stopped(&mut self, state: StopState) {
2465        // We don't actually use the guard but we require it to enforce that the
2466        // caller holds the thread group's mutable state lock (identified by
2467        // mutable access to the thread group's mutable state).
2468
2469        self.base.stop_state.store(state, Ordering::Relaxed)
2470    }
2471
2472    /// Synchronously transitions this [`ThreadGroup`] to [`ThreadGroupRunState::Exiting`] and marks
2473    /// all running member [`Task`]s (except `excluded_tid`, if specified) as
2474    /// [`TaskFlags::EXITED`] before waking and interrupting them.
2475    ///
2476    /// All targeted member tasks must have [`TaskFlags::EXITED`] set while the
2477    /// [`ThreadGroupWriteGuard`] is still held, before [`Self::set_stopped`] notifies
2478    /// `lifecycle_waiters` and drops the lock. This guarantees that stopped sibling threads cannot
2479    /// wake and return to userspace, and that `process_mrelease(2)` cannot observe a non-running
2480    /// [`ThreadGroup`] before all member tasks are marked [`TaskFlags::EXITED`].
2481    #[allow(unused_mut, reason = "needed for some but not all macro outputs")]
2482    fn terminate_tasks(
2483        mut self,
2484        exit_status: ExitStatus,
2485        wake_signal: SignalInfo,
2486        excluded_tid: Option<&Pid>,
2487    ) {
2488        if self.is_running() {
2489            self.set_exiting(exit_status.clone());
2490        }
2491
2492        let tasks: Vec<Arc<Task>> = self
2493            .tasks()
2494            .into_iter()
2495            .filter(|task| task.is_running() && excluded_tid != Some(&task.tid))
2496            .collect();
2497        for task in &tasks {
2498            let mut task_state = task.write();
2499            task_state.set_exit_status_if_not_already(exit_status.clone());
2500            task_state.force_wake_for_exit();
2501        }
2502
2503        self.set_stopped(
2504            StopState::ForceWaking,
2505            Some(wake_signal),
2506            /* finalize_only = */ false,
2507        );
2508
2509        for task in tasks {
2510            task.interrupt();
2511        }
2512    }
2513
2514    #[allow(unused_mut, reason = "needed for some but not all macro outputs")]
2515    pub(crate) fn force_exit_for_sigkill(mut self, signal_info: SignalInfo) {
2516        let exit_status = ExitStatus::Kill(signal_info.clone());
2517        self.terminate_tasks(exit_status, signal_info, /* excluded_tid = */ None);
2518    }
2519
2520    /// Sends the signal `signal_info` to this thread group.
2521    #[allow(unused_mut, reason = "needed for some but not all macro outputs")]
2522    pub fn send_signal(mut self, signal_info: SignalInfo) {
2523        // TODO(https://fxbug.dev/573607334): Immediately transition the thread group to Exiting
2524        // for other unblocked default-fatal signals (DeliveryAction::Terminate when not ptraced).
2525        if signal_info.signal == SIGKILL {
2526            self.force_exit_for_sigkill(signal_info);
2527            return;
2528        }
2529
2530        let sigaction = self.base.signal_actions.get(signal_info.signal);
2531        let action = action_for_signal(&signal_info, sigaction);
2532
2533        // Like `send_signal_prio` does for a single task, discard an ignored signal instead of
2534        // queueing it: no task would ever act on it, but it would still make interruptible syscalls
2535        // fail with EINTR. It must still be queued when a task blocks it with its current or its
2536        // saved mask, as it can be accepted later (see the `SigtimedwaitTest.IgnoredUnmaskedSignal`
2537        // gvisor test), or when a task is ptraced, for the signal-delivery-stop.
2538        let queue_on_group = action != DeliveryAction::Ignore
2539            || self
2540                .tasks
2541                .iter()
2542                .filter_map(|info| info.tid.get_task().ok())
2543                .filter(|task| task.is_running())
2544                .any(|task| {
2545                    let task_state = task.read();
2546                    task_state.is_signal_masked(signal_info.signal)
2547                        || task_state.is_signal_masked_by_saved_mask(signal_info.signal)
2548                        || task_state.is_ptraced()
2549                });
2550
2551        if queue_on_group {
2552            let mut pending_signals = self.base.pending_signals.lock();
2553            pending_signals.enqueue(signal_info.clone());
2554            self.base.has_pending_signals.store(true, Ordering::Relaxed);
2555        }
2556
2557        let tasks: Vec<Pid> = self.tasks.iter().map(|info| info.tid.clone()).collect();
2558
2559        // Set state to waking before interrupting any tasks.
2560        if signal_info.signal == SIGCONT {
2561            self.set_stopped(
2562                StopState::Waking,
2563                Some(signal_info.clone()),
2564                /* finalize_only = */ false,
2565            );
2566        }
2567
2568        let mut has_interrupted_task = false;
2569        for task in tasks.iter().flat_map(|pid| pid.get_task().ok()) {
2570            if !task.is_running() {
2571                continue;
2572            }
2573
2574            let mut task_state = task.write();
2575
2576            if signal_info.signal == SIGCONT {
2577                task_state.set_stopped(
2578                    StopState::Waking,
2579                    /* siginfo = */ None,
2580                    /* current_task = */ None,
2581                    /* event = */ None,
2582                );
2583            }
2584
2585            let is_masked = task_state.is_signal_masked(signal_info.signal);
2586            let was_masked = task_state.is_signal_masked_by_saved_mask(signal_info.signal);
2587
2588            let is_queued = action != DeliveryAction::Ignore
2589                || is_masked
2590                || was_masked
2591                || task_state.is_ptraced();
2592
2593            if is_queued {
2594                task_state.notify_signal_waiters(&signal_info.signal);
2595
2596                let is_fatal = action == DeliveryAction::Terminate && !task_state.is_ptraced();
2597
2598                if !is_masked
2599                    && action.must_interrupt(Some(sigaction))
2600                    && (!has_interrupted_task || is_fatal)
2601                {
2602                    // Interrupt every task if the action is fatal, or only one task for catchable
2603                    // signals.
2604                    drop(task_state);
2605                    task.interrupt();
2606                    has_interrupted_task = true;
2607                }
2608            }
2609        }
2610    }
2611}
2612
2613#[cfg(test)]
2614mod test {
2615    use super::*;
2616    use crate::ptrace::ptrace_traceme;
2617    use crate::signals::send_signal;
2618    use crate::testing::*;
2619    use starnix_syscalls::SUCCESS;
2620    use starnix_uapi::user_address::UserRef;
2621    use starnix_uapi::{CLONE_SIGHAND, CLONE_THREAD, CLONE_VM};
2622
2623    #[::fuchsia::test]
2624    async fn test_setsid() {
2625        spawn_kernel_and_run(async |current_task| {
2626            fn get_process_group(task: &Task) -> Pid {
2627                Arc::clone(&task.thread_group().read().process_group)
2628            }
2629            assert_eq!(current_task.thread_group().setsid(), error!(EPERM));
2630
2631            let child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2632            assert_eq!(get_process_group(&current_task), get_process_group(&child_task));
2633
2634            let old_process_group = child_task.thread_group().read().process_group.clone();
2635            assert_eq!(child_task.thread_group().setsid(), Ok(()));
2636            assert_eq!(child_task.thread_group().read().session, child_task.pid);
2637            let scope = RcuReadScope::new();
2638            assert!(
2639                !old_process_group
2640                    .pgid_thread_groups(&scope)
2641                    .any(|tg| Arc::ptr_eq(&tg, child_task.thread_group()))
2642            );
2643        })
2644        .await;
2645    }
2646
2647    #[::fuchsia::test]
2648    async fn test_exit_status() {
2649        spawn_kernel_and_run(async |current_task| {
2650            let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
2651            child.thread_group().kill(ExitStatus::Exit(42), None);
2652            assert_eq!(child.exit_status(), Some(ExitStatus::Exit(42)));
2653            std::mem::drop(child);
2654            assert_eq!(
2655                current_task.thread_group().read().zombie_children[0].state.exit_status,
2656                ExitStatus::Exit(42)
2657            );
2658        })
2659        .await;
2660    }
2661
2662    #[::fuchsia::test]
2663    async fn test_kill_thread_group_when_already_exiting() {
2664        spawn_kernel_and_run(async |current_task| {
2665            let clone_thread_flags = (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64;
2666            let mut leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
2667            let mut sibling = leader.clone_task_for_test(clone_thread_flags, None);
2668
2669            // Simulate the thread group having already transitioned to `Exiting(42)` while a
2670            // sibling thread concurrently calls `kill_thread_group` (e.g. `exit_group(99)`).
2671            leader.thread_group().write().set_exiting(ExitStatus::Exit(42));
2672            sibling.kill_thread_group(ExitStatus::Exit(99));
2673            assert!(sibling.is_exitted());
2674
2675            leader.kill_thread_group(ExitStatus::Exit(0));
2676            assert!(leader.is_exitted());
2677            std::mem::drop(sibling);
2678            std::mem::drop(leader);
2679            assert_eq!(
2680                current_task.thread_group().read().zombie_children[0].state.exit_status,
2681                ExitStatus::Exit(42)
2682            );
2683        })
2684        .await;
2685    }
2686
2687    #[::fuchsia::test]
2688    async fn test_sigkill_fast_path_exiting() {
2689        spawn_kernel_and_run(async |current_task| {
2690            let clone_thread_flags = (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64;
2691
2692            // Process-directed SIGKILL synchronously transitions the thread group and all tasks.
2693            let process_killed_leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
2694            let process_killed_thread =
2695                process_killed_leader.clone_task_for_test(clone_thread_flags, None);
2696            assert!(process_killed_leader.thread_group().read().is_running());
2697            assert!(!process_killed_leader.is_exitted());
2698            assert!(!process_killed_thread.is_exitted());
2699
2700            assert_eq!(
2701                process_killed_leader
2702                    .thread_group()
2703                    .send_signal_unchecked(current_task, SIGKILL.into()),
2704                Ok(())
2705            );
2706            assert!(!process_killed_leader.thread_group().read().is_running());
2707            assert!(process_killed_leader.is_exitted());
2708            assert!(process_killed_thread.is_exitted());
2709            std::mem::drop(process_killed_thread);
2710            std::mem::drop(process_killed_leader);
2711
2712            // Task-directed SIGKILL also synchronously transitions the thread group and all tasks.
2713            let task_killed_leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
2714            let task_killed_thread =
2715                task_killed_leader.clone_task_for_test(clone_thread_flags, None);
2716            let task_sigkill_info = SignalInfo::with_sender(
2717                SIGKILL,
2718                SI_TKILL,
2719                SignalDetail::Kill {
2720                    pid: current_task.pid.clone(),
2721                    uid: current_task.current_creds().uid,
2722                },
2723                Some(current_task.weak_self.clone()),
2724            );
2725            assert_eq!(send_signal(&task_killed_thread, task_sigkill_info), Ok(()));
2726            assert!(!task_killed_leader.thread_group().read().is_running());
2727            assert!(task_killed_leader.is_exitted());
2728            assert!(task_killed_thread.is_exitted());
2729        })
2730        .await;
2731    }
2732
2733    #[::fuchsia::test]
2734    async fn test_setgpid() {
2735        spawn_kernel_and_run(async |current_task| {
2736            assert_eq!(current_task.thread_group().setsid(), error!(EPERM));
2737
2738            let child_task1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2739            let child_task2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2740            let execd_child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2741            execd_child_task.thread_group().write().did_exec = true;
2742            let other_session_child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2743            assert_eq!(other_session_child_task.thread_group().setsid(), Ok(()));
2744
2745            assert_eq!(
2746                child_task1.thread_group().setpgid(&current_task, &current_task, &current_task.pid),
2747                error!(ESRCH)
2748            );
2749            assert_eq!(
2750                current_task.thread_group().setpgid(
2751                    &current_task,
2752                    &execd_child_task,
2753                    &execd_child_task.pid
2754                ),
2755                error!(EACCES)
2756            );
2757            assert_eq!(
2758                current_task.thread_group().setpgid(
2759                    &current_task,
2760                    &current_task,
2761                    &current_task.pid
2762                ),
2763                error!(EPERM)
2764            );
2765            assert_eq!(
2766                current_task.thread_group().setpgid(
2767                    &current_task,
2768                    &other_session_child_task,
2769                    &other_session_child_task.pid
2770                ),
2771                error!(EPERM)
2772            );
2773            assert_eq!(
2774                current_task.thread_group().setpgid(&current_task, &child_task1, &child_task2.pid),
2775                error!(EPERM)
2776            );
2777            assert_eq!(
2778                current_task.thread_group().setpgid(
2779                    &current_task,
2780                    &child_task1,
2781                    &other_session_child_task.pid
2782                ),
2783                error!(EPERM)
2784            );
2785
2786            assert_eq!(
2787                child_task1.thread_group().setpgid(&current_task, &child_task1, &child_task1.pid),
2788                Ok(())
2789            );
2790            assert_eq!(child_task1.thread_group().read().session, current_task.tid);
2791            assert_eq!(child_task1.thread_group().read().process_group, child_task1.tid);
2792
2793            let old_process_group = child_task2.thread_group().read().process_group.clone();
2794            assert_eq!(
2795                current_task.thread_group().setpgid(&current_task, &child_task2, &child_task1.pid),
2796                Ok(())
2797            );
2798            assert_eq!(child_task2.thread_group().read().process_group, child_task1.tid);
2799            let scope = RcuReadScope::new();
2800            assert!(
2801                !old_process_group
2802                    .pgid_thread_groups(&scope)
2803                    .any(|tg| Arc::ptr_eq(&tg, child_task2.thread_group()))
2804            );
2805
2806            let child_task3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2807            assert_eq!(
2808                child_task3.thread_group().setpgid(&current_task, &child_task3, &child_task3.pid),
2809                Ok(())
2810            );
2811            // Move child_task1 to child_task3's process group.
2812            assert_eq!(
2813                current_task.thread_group().setpgid(&current_task, &child_task1, &child_task3.pid),
2814                Ok(())
2815            );
2816            assert_eq!(child_task1.thread_group().read().process_group, child_task3.tid);
2817
2818            // Rejoin child_task1's original process group (which still contains child_task2).
2819            assert_eq!(
2820                child_task1.thread_group().setpgid(&current_task, &child_task1, &child_task1.pid),
2821                Ok(())
2822            );
2823            assert_eq!(child_task1.thread_group().read().process_group, child_task1.tid);
2824            let pg1 = child_task1.thread_group().read().process_group.clone();
2825            let pg2 = child_task2.thread_group().read().process_group.clone();
2826            assert_eq!(pg1, pg2);
2827
2828            assert_eq!(
2829                crate::task::syscalls::sys_setpgid(&current_task, child_task1.pid.id, -1),
2830                error!(EINVAL)
2831            );
2832            assert_eq!(
2833                crate::task::syscalls::sys_setpgid(&current_task, child_task1.pid.id, 255),
2834                error!(EPERM)
2835            );
2836        })
2837        .await;
2838    }
2839
2840    #[::fuchsia::test]
2841    async fn test_adopt_children() {
2842        spawn_kernel_and_run(async |current_task| {
2843            let task1 = current_task.clone_task_for_test(0, None);
2844            let task2 = task1.clone_task_for_test(0, None);
2845            let task3 = task2.clone_task_for_test(0, None);
2846
2847            assert_eq!(task3.thread_group().read().get_ppid(), task2.tid.id);
2848            assert!(current_task.thread_group().read().children.contains(&task1.pid));
2849            assert!(task1.thread_group().read().children.contains(&task2.pid));
2850            assert!(task2.thread_group().read().children.contains(&task3.pid));
2851
2852            task2.thread_group().kill(ExitStatus::Exit(0), None);
2853            std::mem::drop(task2);
2854
2855            // Task3 parent should be current_task.
2856            assert_eq!(task3.thread_group().read().get_ppid(), current_task.tid.id);
2857            assert!(current_task.thread_group().read().children.contains(&task3.pid));
2858            assert!(!task1.thread_group().read().children.contains(&task3.pid));
2859        })
2860        .await;
2861    }
2862
2863    #[::fuchsia::test]
2864    async fn test_thread_group_children_lifecycle() {
2865        spawn_kernel_and_run(async |current_task| {
2866            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2867            let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2868            let child1_pid = child1.pid.clone();
2869            let child2_pid = child2.pid.clone();
2870
2871            assert!(current_task.thread_group().read().children.contains(&child1_pid));
2872            assert!(current_task.thread_group().read().children.contains(&child2_pid));
2873
2874            let children_pids: HashSet<Pid> =
2875                current_task.thread_group().read().children().map(|tg| tg.leader.clone()).collect();
2876            assert!(children_pids.contains(&child1_pid));
2877            assert!(children_pids.contains(&child2_pid));
2878
2879            child1.thread_group().kill(ExitStatus::Exit(0), None);
2880            std::mem::drop(child1);
2881
2882            assert!(child1_pid.get_thread_group().is_err());
2883            assert!(!current_task.thread_group().read().children.contains(&child1_pid));
2884            assert!(current_task.thread_group().read().children.contains(&child2_pid));
2885            let children_pids: HashSet<Pid> =
2886                current_task.thread_group().read().children().map(|tg| tg.leader.clone()).collect();
2887            assert!(!children_pids.contains(&child1_pid));
2888            assert!(children_pids.contains(&child2_pid));
2889
2890            child2.thread_group().kill(ExitStatus::Exit(0), None);
2891            std::mem::drop(child2);
2892
2893            assert!(child2_pid.get_thread_group().is_err());
2894            assert!(current_task.thread_group().read().children.is_empty());
2895            assert_eq!(current_task.thread_group().read().children().count(), 0);
2896        })
2897        .await;
2898    }
2899
2900    #[::fuchsia::test]
2901    async fn test_ptrace_unspawned_child_drop() {
2902        spawn_kernel_and_run(async |current_task| {
2903            let mut child = create_task(current_task.kernel(), "tracee");
2904            let child_pid = child.pid.clone();
2905            assert!(current_task.thread_group().read().children.contains(&child_pid));
2906
2907            assert_eq!(ptrace_traceme(&mut child), Ok(SUCCESS));
2908            child.thread_group().kill(ExitStatus::Exit(0), None);
2909            std::mem::drop(child);
2910
2911            assert!(!current_task.thread_group().read().children.contains(&child_pid));
2912            assert!(current_task.thread_group().read().zombie_children.is_empty());
2913
2914            // Also verify the case where an unspawned non-leader thread in a multi-threaded child
2915            // (`tracee.tid != tracee.pid`) is traced by its parent and exits last.
2916            let child_leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
2917            let child_pid = child_leader.pid.clone();
2918            let mut child_thread: AutoReleasableTask = child_leader
2919                .clone_task(
2920                    (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64,
2921                    None,
2922                    UserRef::default(),
2923                    UserRef::default(),
2924                    UserRef::default(),
2925                )
2926                .expect("failed to create thread")
2927                .into();
2928            assert_ne!(child_thread.tid, child_pid);
2929            assert_eq!(child_thread.pid, child_pid);
2930            assert!(current_task.thread_group().read().children.contains(&child_pid));
2931
2932            // Exit the leader thread first so `child_thread` is the last task in the thread group.
2933            child_leader.write().set_exit_status(ExitStatus::Exit(0));
2934            std::mem::drop(child_leader);
2935            assert!(current_task.thread_group().read().children.contains(&child_pid));
2936
2937            assert_eq!(ptrace_traceme(&mut child_thread), Ok(SUCCESS));
2938            child_thread.thread_group().kill(ExitStatus::Exit(0), None);
2939            std::mem::drop(child_thread);
2940
2941            assert!(!current_task.thread_group().read().children.contains(&child_pid));
2942            assert!(current_task.thread_group().read().zombie_children.is_empty());
2943        })
2944        .await;
2945    }
2946
2947    #[::fuchsia::test]
2948    async fn test_getppid_after_self_and_parent_exit() {
2949        spawn_kernel_and_run(async |current_task| {
2950            let task1 = current_task.clone_task_for_test(0, None);
2951            let task2 = task1.clone_task_for_test(0, None);
2952
2953            // Take strong references to the ThreadGroups.
2954            let tg1 = task1.thread_group().clone();
2955            let tg2 = task2.thread_group().clone();
2956
2957            assert_eq!(tg1.read().get_ppid(), current_task.tid.id);
2958            assert_eq!(tg2.read().get_ppid(), task1.tid.id);
2959
2960            // Exit `task2` first, so that when `task1` exits, it will not be reparented to init.
2961            tg2.kill(ExitStatus::Exit(0), None);
2962            std::mem::drop(task2);
2963
2964            // Exit `task1`, and drop the task and ThreadGroup.
2965            tg1.kill(ExitStatus::Exit(0), None);
2966            std::mem::drop(task1);
2967            std::mem::drop(tg1);
2968
2969            // It should still be valid to call `get_ppid()` on `tg2`, though is parent ThreadGroup
2970            // no longer exists.
2971            let _ = tg2.read().get_ppid();
2972        })
2973        .await;
2974    }
2975
2976    #[::fuchsia::test]
2977    async fn test_thread_group_rcu_links_and_adapters() {
2978        spawn_kernel_and_run(async |current_task| {
2979            let scope = RcuReadScope::new();
2980            let tg = current_task.thread_group();
2981            let node_ptr = tg.pgrp_node.read(&scope);
2982            assert!(!node_ptr.is_null());
2983            let link_ptr = ThreadGroupLinkAdapter::to_link(node_ptr);
2984            assert_eq!(ThreadGroupLinkAdapter::from_link(link_ptr).as_ptr(), node_ptr.as_ptr());
2985
2986            let init_pid = current_task.pid.clone();
2987
2988            assert!(init_pid.is_process_group(&scope));
2989            assert!(init_pid.is_session(&scope));
2990            assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 1);
2991            assert_eq!(init_pid.sid_thread_groups(&scope).count(), 1);
2992
2993            let child = current_task.clone_task_for_test(0, None);
2994            assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 2);
2995            assert_eq!(init_pid.sid_thread_groups(&scope).count(), 2);
2996
2997            // Moving `child` into its own process group within the same session updates PGID
2998            // membership without detaching SID membership.
2999            child
3000                .thread_group()
3001                .setpgid(&current_task, &child, &child.pid)
3002                .expect("setpgid failed");
3003            assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 1);
3004            assert_eq!(init_pid.sid_thread_groups(&scope).count(), 2);
3005            assert!(child.pid.is_process_group(&scope));
3006            assert!(!child.pid.is_session(&scope));
3007            assert_eq!(child.pid.pgid_thread_groups(&scope).count(), 1);
3008
3009            // Move `child` back to `init_pid`'s process group so `setsid()` succeeds.
3010            child
3011                .thread_group()
3012                .setpgid(&current_task, &child, &init_pid)
3013                .expect("setpgid back to init failed");
3014            assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 2);
3015            assert_eq!(init_pid.sid_thread_groups(&scope).count(), 2);
3016
3017            child.thread_group().setsid().expect("setsid failed");
3018            assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 1);
3019            assert_eq!(init_pid.sid_thread_groups(&scope).count(), 1);
3020            assert!(child.pid.is_process_group(&scope));
3021            assert!(child.pid.is_session(&scope));
3022            assert_eq!(child.pid.pgid_thread_groups(&scope).count(), 1);
3023            assert_eq!(child.pid.sid_thread_groups(&scope).count(), 1);
3024
3025            // Clone a grandchild in `child`'s session, then exit and reap `child` (the session
3026            // leader). Calling `setpgid` on `grandchild` within that session must retain `child`'s
3027            // session `PidEntry` in `PidTable`.
3028            let grandchild = child.clone_task_for_test(0, None);
3029            let child_pid = child.pid.clone();
3030            child.thread_group().kill(ExitStatus::Exit(0), None);
3031            std::mem::drop(child);
3032            {
3033                let mut pids = current_task.kernel().pids.lock();
3034                for zombie in current_task.thread_group().write().zombie_children.drain(..) {
3035                    zombie.release(&mut pids);
3036                }
3037            }
3038            assert!(child_pid.get_process().is_none());
3039            assert!(current_task.kernel().pids.get(child_pid.id).is_ok());
3040
3041            grandchild
3042                .thread_group()
3043                .setpgid(&grandchild, &grandchild, &grandchild.pid)
3044                .expect("grandchild setpgid failed");
3045            assert!(!child_pid.is_process_group(&scope));
3046            assert!(child_pid.is_session(&scope));
3047            assert!(current_task.kernel().pids.get(child_pid.id).is_ok());
3048            assert!(grandchild.pid.is_process_group(&scope));
3049
3050            grandchild.thread_group().kill(ExitStatus::Exit(0), None);
3051            std::mem::drop(grandchild);
3052            {
3053                let mut pids = current_task.kernel().pids.lock();
3054                for zombie in current_task.thread_group().write().zombie_children.drain(..) {
3055                    zombie.release(&mut pids);
3056                }
3057            }
3058
3059            assert!(!child_pid.is_process_group(&scope));
3060            assert!(!child_pid.is_session(&scope));
3061            assert!(current_task.kernel().pids.get(child_pid.id).is_err());
3062        })
3063        .await;
3064    }
3065
3066    #[::fuchsia::test]
3067    async fn test_orphaned_pgrp_signals_stopped_jobs() {
3068        spawn_kernel_and_run(async |init| {
3069            // 1. Exiting a job-controlling parent orphans the child's process group and sends
3070            // SIGHUP + SIGCONT to stopped jobs via `kill_orphaned_pgrp`.
3071            let parent = init.clone_task_for_test(0, Some(SIGCHLD));
3072            parent.thread_group().setsid().expect("setsid");
3073            let child = parent.clone_task_for_test(0, Some(SIGCHLD));
3074            child.thread_group().setpgid(&child, &child, &child.pid).expect("setpgid");
3075
3076            child.thread_group().set_stopped(StopState::GroupStopped, None, false);
3077            assert_eq!(child.thread_group().load_stopped(), StopState::GroupStopped);
3078            assert!(!child.read().has_signal_pending(SIGHUP));
3079
3080            parent.thread_group().kill(ExitStatus::Exit(0), None);
3081            std::mem::drop(parent);
3082
3083            assert!(child.read().has_signal_pending(SIGHUP));
3084            assert_ne!(child.thread_group().load_stopped(), StopState::GroupStopped);
3085
3086            child.thread_group().kill(ExitStatus::Exit(0), None);
3087            std::mem::drop(child);
3088
3089            // 2. A parent leaving the session via `setsid` orphans its child's process group and
3090            // sends SIGHUP + SIGCONT to stopped jobs via `check_orphaned_pgrp`.
3091            let session_leader = init.clone_task_for_test(0, Some(SIGCHLD));
3092            session_leader.thread_group().setsid().expect("setsid");
3093            let middle = session_leader.clone_task_for_test(0, Some(SIGCHLD));
3094            let leaf = middle.clone_task_for_test(0, Some(SIGCHLD));
3095            leaf.thread_group().setpgid(&leaf, &leaf, &leaf.pid).expect("setpgid");
3096
3097            leaf.thread_group().set_stopped(StopState::GroupStopped, None, false);
3098            assert_eq!(leaf.thread_group().load_stopped(), StopState::GroupStopped);
3099            assert!(!leaf.read().has_signal_pending(SIGHUP));
3100
3101            middle.thread_group().setsid().expect("middle setsid");
3102
3103            assert!(leaf.read().has_signal_pending(SIGHUP));
3104            assert_ne!(leaf.thread_group().load_stopped(), StopState::GroupStopped);
3105        })
3106        .await;
3107    }
3108}