Skip to main content

starnix_core/task/
thread_group.rs

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