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