1use crate::device::terminal::{ControllingSession, ControllingTerminal, Terminal, TerminalSide};
6use crate::mutable_state::{state_accessor, state_implementation};
7use crate::ptrace::{
8 AtomicStopState, PtraceAllowedPtracers, PtraceEvent, PtraceOptions, PtraceStatus, PtraceTracer,
9 StopState, ZombiePtracees, ptrace_detach,
10};
11use crate::security;
12use crate::signals::syscalls::WaitingOptions;
13use crate::signals::{
14 DeliveryAction, IntoSignalInfoOptions, QueuedSignals, SignalActions, SignalDetail, SignalInfo,
15 UncheckedSignalInfo, action_for_signal, send_standard_signal,
16};
17use crate::task::memory_attribution::MemoryAttributionLifecycleEvent;
18use crate::task::{
19 CurrentTask, ExitStatus, Kernel, Pid, PidEntry, PidTableGuard, Task, TaskFlags,
20 TaskMutableState, TaskPersistentInfo, TypedWaitQueue, WaitResult, ZombieProcess, ZombieState,
21};
22use crate::time::{IntervalTimerHandle, TimerTable};
23use fuchsia_rcu::subtle::RcuPtr;
24use fuchsia_rcu::{RcuDroppable, RcuDroppableArc, RcuReadScope};
25use fuchsia_rcu_collections::rcu_intrusive_list::{Link, RcuListAdapter, rcu_list_adapter};
26use itertools::Itertools;
27use macro_rules_attribute::apply;
28use starnix_lifecycle::{AtomicCounter, DropNotifier};
29use starnix_logging::{log_debug, log_error, log_info, log_warn, track_stub};
30use starnix_sync::{
31 LockDepMutex, LockDepRwLock, ThreadGroupLimits, ThreadGroupMutableStateLock,
32 ThreadGroupPendingSignalsLock, ThreadGroupPtraceesLock, allow_subclass,
33};
34use starnix_task_command::TaskCommand;
35use starnix_types::ownership::{OwnedRef, Releasable};
36use starnix_types::stats::TaskTimeStats;
37use starnix_types::time::{itimerspec_from_itimerval, timeval_from_duration};
38use starnix_uapi::auth::{CAP_SYS_ADMIN, CAP_SYS_RESOURCE};
39use starnix_uapi::errors::Errno;
40use starnix_uapi::personality::PersonalityFlags;
41use starnix_uapi::resource_limits::{Resource, ResourceLimits};
42use starnix_uapi::signals::{
43 SIGCHLD, SIGCONT, SIGHUP, SIGKILL, SIGTERM, SIGTTOU, SigSet, Signal, UncheckedSignal,
44};
45use starnix_uapi::user_address::UserAddress;
46use starnix_uapi::{
47 ITIMER_PROF, ITIMER_REAL, ITIMER_VIRTUAL, SA_NOCLDWAIT, SI_USER, SIG_IGN, errno, error,
48 itimerval, pid_t, rlimit, tid_t,
49};
50use std::collections::HashSet;
51use std::fmt;
52use std::sync::atomic::{AtomicBool, Ordering};
53use std::sync::{Arc, Weak};
54use zx::{Koid, Status};
55
56const INIT_PID: pid_t = 1;
57
58#[derive(Debug)]
59pub struct ZirconProcess {
60 process: zx::Process,
61 koid: Result<Koid, Status>,
62}
63
64impl ZirconProcess {
65 pub fn new(process: zx::Process) -> Self {
66 let koid = process.koid();
67 Self { process, koid }
68 }
69
70 pub fn koid(&self) -> Result<Koid, Status> {
71 self.koid
72 }
73}
74
75impl std::ops::Deref for ZirconProcess {
76 type Target = zx::Process;
77 fn deref(&self) -> &Self::Target {
78 &self.process
79 }
80}
81
82#[repr(u64)]
84pub enum ThreadGroupLifecycleWaitValue {
85 ChildStatus,
87 Stopped,
89}
90
91impl Into<u64> for ThreadGroupLifecycleWaitValue {
92 fn into(self) -> u64 {
93 self as u64
94 }
95}
96
97#[derive(Clone, Debug)]
100pub struct DeferredZombiePTracer {
101 pub tracer_pid: Pid,
103 pub tracee_tid: Pid,
105 pub tracee_pid: Pid,
107 pub tracee_thread_group: Weak<ThreadGroup>,
109}
110
111impl DeferredZombiePTracer {
112 fn new(tracer: &ThreadGroup, tracee: &Task) -> Self {
113 Self {
114 tracer_pid: tracer.leader.clone(),
115 tracee_tid: tracee.tid.clone(),
116 tracee_pid: tracee.pid.clone(),
117 tracee_thread_group: Arc::downgrade(tracee.thread_group()),
118 }
119 }
120
121 fn tracee_pgid(&self) -> Option<Pid> {
123 self.tracee_thread_group.upgrade().map(|thread_group| thread_group.pgid())
124 }
125}
126
127pub struct ThreadGroupMutableState {
129 pub parent: Option<ThreadGroupParent>,
134
135 pub exit_signal: Option<Signal>,
137
138 parent_death_signal: Option<Signal>,
144
145 tasks: HashSet<TaskPersistentInfo>,
152
153 pub children: HashSet<Pid>,
158
159 pub zombie_children: Vec<OwnedRef<ZombieProcess>>,
161
162 pub zombie_ptracees: ZombiePtracees,
164
165 pub deferred_zombie_ptracers: Vec<DeferredZombiePTracer>,
168
169 pub lifecycle_waiters: TypedWaitQueue<ThreadGroupLifecycleWaitValue>,
171
172 pub is_child_subreaper: bool,
175
176 pub session: Pid,
178
179 pub controlling_terminal: Option<ControllingTerminal>,
181
182 pub did_exec: bool,
183
184 pub last_signal: Option<SignalInfo>,
188
189 run_state: ThreadGroupRunState,
193
194 pub children_time_stats: TaskTimeStats,
196
197 pub personality: PersonalityFlags,
199
200 pub allowed_ptracers: PtraceAllowedPtracers,
202
203 exit_notifier: Option<futures::channel::oneshot::Sender<()>>,
205
206 pub notifier: Option<std::sync::mpsc::Sender<MemoryAttributionLifecycleEvent>>,
208}
209
210#[derive(Debug)]
214struct ProcessGroup(RcuDroppableArc<PidEntry>);
215
216impl ProcessGroup {
217 fn new(process_group: Pid) -> Self {
218 Self(RcuDroppableArc::new(process_group))
219 }
220
221 fn get(&self) -> Pid {
222 self.0.to_arc()
223 }
224
225 fn update(&self, new_pgid: Pid, pids: &PidTableGuard<'_>, state: &ThreadGroupWriteGuard<'_>) {
226 debug_assert!(
227 std::ptr::eq(self, &state.base.process_group),
228 "ThreadGroupWriteGuard must belong to the ThreadGroup whose process_group is being updated"
229 );
230 debug_assert!(
231 std::ptr::eq(&**pids, &state.base.kernel.pids),
232 "PidTableGuard must belong to the Kernel of the ThreadGroup being updated"
233 );
234 self.0.update(new_pgid);
235 }
236}
237
238pub struct ThreadGroup {
262 pub weak_self: Weak<ThreadGroup>,
265
266 pub kernel: Arc<Kernel>,
268
269 pub process: ZirconProcess,
278
279 pub root_vmar: zx::Vmar,
281
282 pub leader: Pid,
286
287 process_group: ProcessGroup,
293
294 pub(super) pgrp_node: RcuPtr<ThreadGroupLinkNode>,
296
297 pub(super) session_node: RcuPtr<ThreadGroupLinkNode>,
299
300 pub signal_actions: Arc<SignalActions>,
302
303 pub timers: TimerTable,
305
306 pub drop_notifier: DropNotifier,
308
309 stop_state: AtomicStopState,
313
314 mutable_state: LockDepRwLock<ThreadGroupMutableState, ThreadGroupMutableStateLock>,
316
317 pub limits: LockDepMutex<ResourceLimits, ThreadGroupLimits>,
321
322 pub next_seccomp_filter_id: AtomicCounter<u64>,
327
328 pub ptracees: LockDepMutex<HashSet<TaskPersistentInfo>, ThreadGroupPtraceesLock>,
330
331 pub pending_signals: LockDepMutex<QueuedSignals, ThreadGroupPendingSignalsLock>,
333
334 pub has_pending_signals: AtomicBool,
337
338 pub start_time: zx::MonotonicInstant,
340
341 log_syscalls_as_info: AtomicBool,
343
344 pub created_during_shutdown: AtomicBool,
347}
348
349impl fmt::Debug for ThreadGroup {
350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351 write!(
352 f,
353 "{}({})",
354 self.process.get_name().unwrap_or(zx::Name::new_lossy("<unknown>")),
355 self.leader
356 )
357 }
358}
359
360impl ThreadGroup {
361 pub fn to_owned(&self) -> Arc<Self> {
362 self.weak_self.upgrade().expect("ThreadGroup is alive")
363 }
364
365 pub fn sync_syscall_log_level(&self) {
366 let command = self.read().leader_command();
367 let filters = self.kernel.syscall_log_filters.lock();
368 let should_log = filters.iter().any(|f| f.matches(&command));
369 let prev_should_log = self.log_syscalls_as_info.swap(should_log, Ordering::Relaxed);
370 let change_str = match (should_log, prev_should_log) {
371 (true, false) => Some("Enabled"),
372 (false, true) => Some("Disabled"),
373 _ => None,
374 };
375 if let Some(change_str) = change_str {
376 log_info!(
377 "{change_str} info syscall logs for thread group {} (command: {command})",
378 self.leader
379 );
380 }
381 }
382
383 #[inline]
384 pub fn syscall_log_level(&self) -> starnix_logging::Level {
385 if self.log_syscalls_as_info.load(Ordering::Relaxed) {
386 starnix_logging::Level::Info
387 } else {
388 starnix_logging::Level::Trace
389 }
390 }
391}
392
393impl PartialEq for ThreadGroup {
394 fn eq(&self, other: &Self) -> bool {
395 self.leader == other.leader
396 }
397}
398
399#[derive(Debug, RcuDroppable)]
405pub(super) struct ThreadGroupLinkNode {
406 pub(super) thread_group: Weak<ThreadGroup>,
407 pub(super) link: Link,
408}
409
410impl ThreadGroupLinkNode {
411 pub(super) fn new(thread_group: Weak<ThreadGroup>) -> Self {
412 Self { thread_group, link: Link::default() }
413 }
414}
415
416#[derive(Debug, RcuDroppable)]
418pub(super) struct ThreadGroupLinkAdapter;
419
420impl RcuListAdapter<ThreadGroupLinkNode> for ThreadGroupLinkAdapter {
421 rcu_list_adapter!(ThreadGroupLinkNode, link);
422}
423
424impl Drop for ThreadGroup {
425 fn drop(&mut self) {
426 let state = self.mutable_state.get_mut();
427 assert!(state.tasks.is_empty());
428 assert!(state.children.is_empty());
429 assert!(state.zombie_children.is_empty());
430 assert!(state.zombie_ptracees.is_empty());
431 #[cfg(any(test, debug_assertions))]
432 {
433 let scope = fuchsia_rcu::RcuReadScope::new();
434 assert!(self.pgrp_node.read(&scope).is_null());
435 assert!(self.session_node.read(&scope).is_null());
436 assert!(
437 state
438 .parent
439 .as_ref()
440 .and_then(|p| p
441 .thread_group
442 .upgrade()
443 .map(|p| !p.read().children.contains(&self.leader)))
444 .unwrap_or(true)
445 );
446 }
447 }
448}
449
450#[derive(Clone)]
454pub struct ThreadGroupParent {
455 thread_group: Weak<ThreadGroup>,
457
458 creator_task: Option<Weak<Task>>,
461}
462
463impl ThreadGroupParent {
464 pub fn new(thread_group: Weak<ThreadGroup>) -> Self {
471 let upgraded = thread_group.upgrade();
472 debug_assert!(upgraded.is_some());
473 let creator_task =
474 upgraded.and_then(|tg| tg.leader.get_task().ok()).map(|t| t.weak_self.clone());
475 Self { thread_group, creator_task }
476 }
477
478 pub(super) fn from_reaper(thread_group: Weak<ThreadGroup>) -> Self {
485 debug_assert!(thread_group.upgrade().is_some());
486 Self { thread_group, creator_task: None }
487 }
488
489 pub fn upgrade(&self) -> Arc<ThreadGroup> {
491 self.thread_group.upgrade().expect("ThreadGroupParent references must always be valid")
492 }
493
494 pub(super) fn creator_task(&self) -> Option<&Weak<Task>> {
497 self.creator_task.as_ref()
498 }
499
500 pub(super) fn set_creator_task(&mut self, creator_task: Option<Weak<Task>>) {
503 self.creator_task = creator_task;
504 }
505}
506
507#[derive(Debug, Clone)]
510pub enum ProcessSelector {
511 Any,
513 Pid(Pid),
515 Pgid(Pid),
517}
518
519impl ProcessSelector {
520 pub fn match_tid(&self, tid: &Pid) -> bool {
521 match self {
522 ProcessSelector::Pid(pid) => {
523 if pid == tid {
524 true
525 } else if let Ok(task_ref) = tid.get_task() {
526 &task_ref.pid == pid
527 } else {
528 false
529 }
530 }
531 ProcessSelector::Any => true,
532 ProcessSelector::Pgid(pgid) => {
533 if let Ok(task_ref) = tid.get_task() {
534 &task_ref.thread_group().pgid() == pgid
535 } else {
536 false
537 }
538 }
539 }
540 }
541}
542
543#[derive(Clone, Debug, Default, PartialEq, Eq)]
544enum ThreadGroupRunState {
545 #[default]
546 Running,
547 Exiting(ExitStatus),
548 Exited(ExitStatus),
549}
550
551impl ThreadGroup {
552 pub fn new(
554 kernel: Arc<Kernel>,
555 process: zx::Process,
556 root_vmar: zx::Vmar,
557 parent: Option<ThreadGroupWriteGuard<'_>>,
558 leader: Pid,
559 exit_signal: Option<Signal>,
560 signal_actions: Arc<SignalActions>,
561 ) -> Arc<ThreadGroup> {
562 debug_assert!(!process.is_invalid());
563 debug_assert!(!root_vmar.is_invalid());
564 Self::new_internal(kernel, process, root_vmar, parent, leader, exit_signal, signal_actions)
565 }
566
567 pub fn for_system(kernel: Arc<Kernel>, leader: Pid) -> Arc<ThreadGroup> {
569 Self::new_internal(
570 kernel,
571 zx::Process::invalid(),
572 zx::Vmar::invalid(),
573 None,
574 leader,
575 Some(SIGCHLD),
576 SignalActions::default(),
577 )
578 }
579
580 pub fn for_test(
588 kernel: Arc<Kernel>,
589 process: zx::Process,
590 parent: ThreadGroupWriteGuard<'_>,
591 leader: Pid,
592 ) -> Arc<ThreadGroup> {
593 Self::new_internal(
594 kernel,
595 process,
596 zx::Vmar::invalid(),
597 Some(parent),
598 leader,
599 Some(SIGCHLD),
600 SignalActions::default(),
601 )
602 }
603
604 fn new_internal(
605 kernel: Arc<Kernel>,
606 process: zx::Process,
607 root_vmar: zx::Vmar,
608 parent: Option<ThreadGroupWriteGuard<'_>>,
609 leader: Pid,
610 exit_signal: Option<Signal>,
611 signal_actions: Arc<SignalActions>,
612 ) -> Arc<ThreadGroup> {
613 Arc::new_cyclic(|weak_self| {
614 let process = ZirconProcess::new(process);
615 let process_group =
616 parent.as_ref().map(|p| p.base.pgid()).unwrap_or_else(|| leader.clone());
617 let session =
618 parent.as_ref().map(|p| p.session.clone()).unwrap_or_else(|| leader.clone());
619 let mut thread_group = ThreadGroup {
620 weak_self: weak_self.clone(),
621 kernel,
622 process,
623 root_vmar,
624 leader,
625 process_group: ProcessGroup::new(process_group),
626 pgrp_node: RcuPtr::null(),
627 session_node: RcuPtr::null(),
628 signal_actions,
629 timers: Default::default(),
630 drop_notifier: Default::default(),
631 limits: LockDepMutex::new(
637 parent.as_ref().map(|p| p.base.limits.lock().clone()).unwrap_or_else(|| {
638 ResourceLimits::from_total_ram(zx::system_get_physmem())
639 }),
640 ),
641 next_seccomp_filter_id: Default::default(),
642 ptracees: Default::default(),
643 stop_state: AtomicStopState::new(StopState::Awake),
644 pending_signals: Default::default(),
645 has_pending_signals: Default::default(),
646 start_time: zx::MonotonicInstant::get(),
647 mutable_state: ThreadGroupMutableState {
648 parent: parent
649 .as_ref()
650 .map(|p| ThreadGroupParent::new(p.base.weak_self.clone())),
651 exit_signal,
652 parent_death_signal: None,
657 tasks: HashSet::new(),
658 children: HashSet::new(),
659 zombie_children: vec![],
660 zombie_ptracees: ZombiePtracees::new(),
661 deferred_zombie_ptracers: vec![],
662 lifecycle_waiters: TypedWaitQueue::<ThreadGroupLifecycleWaitValue>::default(),
663 is_child_subreaper: false,
664 session,
665 controlling_terminal: None,
666 did_exec: false,
667 last_signal: None,
668 run_state: Default::default(),
669 children_time_stats: Default::default(),
670 personality: parent
671 .as_ref()
672 .map(|p| p.personality)
673 .unwrap_or(Default::default()),
674 allowed_ptracers: PtraceAllowedPtracers::None,
675 exit_notifier: None,
676 notifier: None,
677 }
678 .into(),
679 log_syscalls_as_info: AtomicBool::new(false),
680 created_during_shutdown: AtomicBool::new(false),
681 };
682
683 if let Some(mut parent) = parent {
684 thread_group.next_seccomp_filter_id.reset(parent.base.next_seccomp_filter_id.get());
685 parent.children.insert(thread_group.leader.clone());
686 };
687 thread_group
688 })
689 }
690
691 state_accessor!(ThreadGroup, mutable_state);
692
693 pub fn load_stopped(&self) -> StopState {
694 self.stop_state.load(Ordering::Relaxed)
695 }
696
697 fn is_init(&self) -> bool {
699 self.leader.id == INIT_PID
702 }
703
704 pub(crate) fn ignores_signal(&self, signal_info: &SignalInfo) -> bool {
707 signal_info.signal.is_unblockable() && self.is_init() && !signal_info.force
719 }
720
721 pub fn kill(&self, exit_status: ExitStatus, mut current_task: Option<&mut CurrentTask>) {
729 let state = self.write();
730 if !state.is_running() {
731 if let Some(current_task) = current_task {
732 current_task.write().set_exit_status_if_not_already(exit_status);
733 }
734 return;
735 }
736
737 let current_tid = current_task.as_ref().map(|task| task.tid.clone());
742 state.terminate_tasks(
743 exit_status.clone(),
744 SignalInfo::forced(SIGKILL),
745 current_tid.as_ref(),
746 );
747
748 if let Some(ref mut current_task) = current_task {
762 current_task
763 .ptrace()
764 .on_event(PtraceOptions::TRACEEXIT, exit_status.signal_info_status() as u64);
765 let mut task_state = current_task.write();
766 task_state.set_exit_status(exit_status);
767 task_state.force_wake_for_exit();
768 }
769 }
770
771 pub fn add(&self, task: Arc<Task>) -> Result<(), Errno> {
772 let mut state = self.write();
773 if !state.is_running() {
774 if state.tasks_count() == 0 {
775 log_warn!(
776 "Task {} with leader {} not running while adding its first task, \
777 not sending creation notification",
778 task.tid,
779 self.leader
780 );
781 }
782 return error!(EINVAL);
783 }
784 state.tasks.insert(task.persistent_info.clone());
785
786 Ok(())
787 }
788
789 pub fn remove(&self, mut pids: PidTableGuard<'_>, task: &Arc<Task>) {
794 task.set_ptrace_zombie(&mut pids);
795
796 let mut state = self.write();
797
798 if !state.tasks.remove(&task.persistent_info) {
799 debug_assert!(!state.is_running());
802 pids.remove_task(&task.tid);
803 return;
804 }
805
806 if state.tasks.is_empty() {
807 let pgid = self.pgid();
808 let sid = Arc::clone(&state.session);
809 pids.detach_pgid(&pgid, self);
810 pids.detach_sid(&sid, self);
811
812 let exit_status = if let ThreadGroupRunState::Exiting(exit_status) = &state.run_state {
813 exit_status.clone()
814 } else {
815 let exit_status = task.exit_status().unwrap_or_else(|| {
816 log_error!("Exiting without an exit code.");
817 ExitStatus::Exit(u8::MAX)
818 });
819 state.set_exiting(exit_status.clone());
820 exit_status
821 };
822
823 let zombie_notifications = state.zombie_ptracees.detach_all(&mut pids);
825
826 let zombie = ZombieProcess::new(
830 task.clone(),
831 state.as_ref(),
832 exit_status,
833 state.exit_signal.clone(),
834 );
835 pids.kill_process(&self.leader, Arc::downgrade(task));
836 pids.remove_task(&task.tid);
837
838 let disassociated_controlling_terminal =
839 if state.session == self.leader { state.controlling_terminal.take() } else { None };
840
841 std::mem::drop(state);
851
852 if let Some(controlling_terminal) = disassociated_controlling_terminal {
855 controlling_terminal.terminal.disassociate_controlling_session_if(
856 Some(&sid),
857 Some(controlling_terminal.side),
858 );
859 }
860
861 for notification in zombie_notifications {
862 notification.deliver(&mut pids);
863 }
864
865 self.kernel.cgroups.lock_cgroup2_pid_table().remove_process(&self.leader);
869
870 self.detach_ptracees(&mut pids);
871
872 let parent = self.read().parent.clone();
875 let reaper = self.find_reaper();
876
877 let mut children = Vec::new();
892 {
893 if let Some(reaper) = reaper {
895 let reaper = reaper.upgrade();
896 {
897 let mut reaper_state = reaper.write();
898 let _token = allow_subclass();
901 let mut state = self.write();
902 for child_pid in std::mem::take(&mut state.children) {
903 if let Ok(child) = child_pid.get_thread_group() {
904 let signal = {
905 let _token = allow_subclass();
909 let mut child_state = child.write();
910
911 let signal = child_state.parent_death_signal_for_exiting_task(
912 task,
913 state.is_child_subreaper,
914 );
915 child_state.exit_signal = Some(SIGCHLD);
916 child_state.parent = Some(ThreadGroupParent::from_reaper(
917 Arc::downgrade(&reaper),
918 ));
919 signal
920 };
921 reaper_state.children.insert(child_pid);
922 children.push((child, signal));
923 }
924 }
925 reaper_state.zombie_children.append(&mut state.zombie_children);
926 }
927 ZombiePtracees::reparent(self, &reaper);
928
929 for (child, _) in &children {
930 child.kill_orphaned_pgrp(&pids, Some(self), None);
931 }
932 } else {
933 let mut state = self.write();
935 for child_pid in std::mem::take(&mut state.children) {
936 if let Ok(child) = child_pid.get_thread_group() {
937 let signal = {
938 let _token = allow_subclass();
941 let mut child_state = child.write();
942 let signal = child_state.parent_death_signal_for_exiting_task(
943 task,
944 state.is_child_subreaper,
945 );
946 child_state.parent = None;
947 signal
948 };
949 children.push((child, signal));
950 }
951 }
952 for zombie in state.zombie_children.drain(..) {
953 zombie.release(&mut pids);
954 }
955 }
956 }
957
958 self.send_parent_death_signals(
959 task,
960 children.into_iter().filter_map(|(child, signal)| Some((child, signal?))),
961 );
962
963 self.write().parent = None;
965
966 #[cfg(any(test, debug_assertions))]
967 {
968 let state = self.read();
969 assert!(state.zombie_children.is_empty());
970 assert!(state.zombie_ptracees.is_empty());
971 }
972
973 if let Some(ref parent) = parent {
974 let parent = parent.upgrade();
975
976 let tracer_tg = task
977 .read()
978 .ptrace
979 .as_ref()
980 .and_then(|ptrace| ptrace.core_state.thread_group.upgrade());
981
982 let maybe_zombie = match tracer_tg {
983 Some(tracer_tg) => {
984 tracer_tg.maybe_notify_tracer(task, &mut pids, &parent, zombie)
985 }
986 None => Some(zombie),
987 };
988
989 if let Some(zombie) = maybe_zombie {
990 parent.do_zombie_notifications(zombie, &mut pids);
991 }
992 } else {
993 zombie.release(&mut pids);
994 }
995
996 let parent_tg = parent.as_ref().map(|p| p.upgrade());
1002 self.kill_orphaned_pgrp(&pids, parent_tg.as_deref(), Some(self));
1003
1004 self.write().set_exited();
1005 } else {
1006 pids.remove_task(&task.tid);
1007 let mut children_to_signal = Vec::new();
1014 for child in state.children() {
1015 let _token = allow_subclass();
1018 let child_state = child.read();
1019 if let Some(signal) = child_state.parent_death_signal_for_exiting_task(
1020 task, false,
1021 ) {
1022 children_to_signal.push((child.clone(), signal));
1023 }
1024 }
1025 std::mem::drop(state);
1026 self.send_parent_death_signals(task, children_to_signal);
1027 }
1028 }
1029
1030 fn send_parent_death_signals(
1031 &self,
1032 task: &Arc<Task>,
1033 children_to_signal: impl IntoIterator<Item = (Arc<ThreadGroup>, Signal)>,
1034 ) {
1035 let uid = task.real_creds().uid;
1036 for (child, signal) in children_to_signal {
1037 child.write().send_signal(SignalInfo::with_detail(
1042 signal,
1043 SI_USER as i32,
1044 SignalDetail::Kill { pid: self.leader.clone(), uid },
1045 ));
1046 }
1047 }
1048
1049 fn detach_ptracees(&self, pids: &mut PidTableGuard<'_>) {
1051 let tracee_tids = self.ptracees.lock().iter().map(|info| info.tid.clone()).collect_vec();
1052 for tracee_tid in tracee_tids {
1053 let Ok(tracee) = tracee_tid.get_task() else {
1054 continue;
1055 };
1056
1057 let mut should_send_sigkill = false;
1058 if let Some(ptrace) = &tracee.read().ptrace {
1059 should_send_sigkill = ptrace.has_option(PtraceOptions::EXITKILL);
1060 }
1061 if should_send_sigkill {
1062 send_standard_signal(tracee.as_ref(), SignalInfo::kernel(SIGKILL));
1063 }
1064
1065 let _ = ptrace_detach(
1066 pids,
1067 PtraceTracer::Exiting(self),
1068 tracee.as_ref(),
1069 &UserAddress::NULL,
1070 );
1071 }
1072 }
1073
1074 pub fn do_zombie_notifications(
1075 &self,
1076 zombie: OwnedRef<ZombieProcess>,
1077 pids: &mut PidTableGuard<'_>,
1078 ) {
1079 let mut state = self.write();
1080
1081 state.children.remove(&zombie.task.pid);
1082 state.deferred_zombie_ptracers.retain(|dzp| dzp.tracee_pid != zombie.task.pid);
1083
1084 let exit_signal = zombie.exit_signal;
1085 let mut signal_info = zombie.to_wait_result().as_signal_info();
1086
1087 let should_make_zombie = if exit_signal == Some(SIGCHLD) {
1097 let action = self.signal_actions.get(SIGCHLD);
1098 action.sa_handler != SIG_IGN && (action.sa_flags & SA_NOCLDWAIT as u64) == 0
1099 } else {
1100 true
1101 };
1102 if should_make_zombie {
1103 state.zombie_children.push(zombie);
1104 } else {
1105 state.reap_zombie(zombie, pids);
1106 }
1107
1108 state.lifecycle_waiters.notify_value(ThreadGroupLifecycleWaitValue::ChildStatus);
1109
1110 if let Some(exit_signal) = exit_signal {
1112 signal_info.signal = exit_signal;
1113 state.send_signal(signal_info);
1114 }
1115 }
1116
1117 fn maybe_notify_tracer(
1121 &self,
1122 tracee: &Task,
1123 pids: &mut PidTableGuard<'_>,
1124 parent: &ThreadGroup,
1125 zombie: OwnedRef<ZombieProcess>,
1126 ) -> Option<OwnedRef<ZombieProcess>> {
1127 let mut state = self.write();
1128 if state.zombie_ptracees.has_tracee(&tracee.tid) {
1129 if self == parent {
1130 if let Some(zombie_notification) = state.zombie_ptracees.detach(pids, &tracee.tid) {
1135 zombie_notification.discard(pids);
1136 }
1137 return Some(zombie);
1138 } else {
1139 if state.is_exited() {
1142 return Some(zombie);
1144 }
1145
1146 drop(state);
1149 {
1150 let mut parent_state = parent.write();
1152 parent_state
1153 .deferred_zombie_ptracers
1154 .push(DeferredZombiePTracer::new(self, tracee));
1155 parent_state.children.remove(&tracee.pid);
1156 }
1157
1158 let mut state = self.write();
1164 state.zombie_ptracees.set_parent_of(&tracee.tid, Some(zombie), parent);
1165 tracee.write().notify_ptracers();
1166 return None;
1167 }
1168 } else if self == parent {
1169 state.children.remove(&tracee.pid);
1172 zombie.release(pids);
1173 return None;
1174 }
1175 Some(zombie)
1178 }
1179
1180 fn find_reaper(&self) -> Option<ThreadGroupParent> {
1182 let mut weak_parent = self.read().parent.clone()?;
1183 loop {
1184 weak_parent = {
1185 let parent = weak_parent.upgrade();
1186 let parent_state = parent.read();
1187 if parent_state.is_child_subreaper {
1188 break;
1189 }
1190 match parent_state.parent {
1191 Some(ref next_parent) => next_parent.clone(),
1192 None => break,
1193 }
1194 };
1195 }
1196 Some(weak_parent)
1197 }
1198
1199 pub fn setsid(&self) -> Result<(), Errno> {
1200 let mut pids = self.kernel.pids.lock();
1201 let pid = self.leader.clone();
1202
1203 let scope = RcuReadScope::new();
1205 if self.leader.is_process_group(&scope) || self.leader.is_session(&scope) {
1206 return error!(EPERM);
1207 }
1208
1209 let self_arc = self.to_owned();
1210 let (old_process_group, check_old_pgrp, child_pgrps) = {
1211 let mut state = self.write();
1212 if !state.is_running() {
1213 return error!(ESRCH);
1214 }
1215 let old_process_group = self.pgid();
1216 if state.session == self.leader || old_process_group == self.leader {
1217 return error!(EPERM);
1218 }
1219 self.process_group.update(pid.clone(), &pids, &state);
1220 let old_session = std::mem::replace(&mut state.session, pid.clone());
1221 state.controlling_terminal = None;
1222
1223 pids.detach_pgid(&old_process_group, self);
1224 pids.detach_sid(&old_session, self);
1225 pids.attach_pgid(&pid, &self_arc);
1226 pids.attach_sid(&pid, &self_arc);
1227
1228 let parent_tg = state.parent.as_ref().map(|p| p.upgrade());
1229 let mut child_pgrps = HashSet::new();
1230 for child in state.children() {
1231 let _token = allow_subclass();
1232 let child_state = child.read();
1233 let child_pgid = child.pgid();
1234 if child_state.session == old_session && child_pgid != old_process_group {
1235 child_pgrps.insert(child_pgid);
1236 }
1237 }
1238 std::mem::drop(state);
1239
1240 let check_old_pgrp = parent_tg.is_some_and(|parent_tg| {
1241 let parent_state = parent_tg.read();
1242 parent_state.session == old_session && parent_tg.pgid() != old_process_group
1243 });
1244
1245 (old_process_group, check_old_pgrp, child_pgrps)
1246 };
1247 std::mem::drop(scope);
1248
1249 if check_old_pgrp {
1252 Self::check_orphaned_pgrp(&pids, &old_process_group, None);
1253 }
1254 for child_pgrp in child_pgrps {
1255 Self::check_orphaned_pgrp(&pids, &child_pgrp, None);
1256 }
1257
1258 Ok(())
1259 }
1260
1261 pub fn pgid(&self) -> Pid {
1263 self.process_group.get()
1264 }
1265
1266 pub fn setpgid(
1267 &self,
1268 current_task: &CurrentTask,
1269 target: &Task,
1270 pgid: &Pid,
1271 ) -> Result<(), Errno> {
1272 let mut pids = self.kernel.pids.lock();
1273
1274 let (old_process_group, check_old_pgrp, check_new_pgrp) = {
1275 let current_session = Arc::clone(&self.read().session);
1276
1277 let target_thread_group = target.thread_group().write();
1279 if !target_thread_group.is_running() {
1280 return error!(ESRCH);
1281 }
1282 let parent_tg = target_thread_group.parent.as_ref().map(|p| p.upgrade());
1283 let is_target_current_process_child =
1284 parent_tg.as_ref().is_some_and(|tg| tg.leader == self.leader);
1285 if target_thread_group.base.leader != self.leader && !is_target_current_process_child {
1286 return error!(ESRCH);
1287 }
1288
1289 if is_target_current_process_child && target_thread_group.did_exec {
1291 return error!(EACCES);
1292 }
1293
1294 if target_thread_group.base.leader == target_thread_group.session
1296 || current_session != target_thread_group.session
1297 {
1298 return error!(EPERM);
1299 }
1300
1301 let old_process_group = target.thread_group().pgid();
1302 if *pgid == old_process_group {
1303 return Ok(());
1304 }
1305
1306 if *pgid != target_thread_group.base.leader {
1308 let scope = RcuReadScope::new();
1309 if !pgid.is_process_group_in_session(&target_thread_group.session, &scope) {
1310 return error!(EPERM);
1311 }
1312 }
1313 security::check_setpgid_access(current_task, target)?;
1314
1315 target.thread_group().process_group.update(pgid.clone(), &pids, &target_thread_group);
1316 pids.detach_pgid(&old_process_group, target.thread_group());
1317 pids.attach_pgid(pgid, target.thread_group());
1318
1319 let check_new_pgrp = target_thread_group.children().any(|child| child.pgid() == *pgid);
1320 std::mem::drop(target_thread_group);
1321
1322 let check_old_pgrp = parent_tg.is_some_and(|parent_tg| {
1323 let parent_state = parent_tg.read();
1324 parent_state.session == current_session && parent_tg.pgid() != old_process_group
1325 });
1326
1327 (old_process_group, check_old_pgrp, check_new_pgrp)
1328 };
1329
1330 if check_old_pgrp {
1333 Self::check_orphaned_pgrp(&pids, &old_process_group, None);
1334 }
1335 if check_new_pgrp {
1336 Self::check_orphaned_pgrp(&pids, pgid, None);
1337 }
1338
1339 Ok(())
1340 }
1341
1342 fn itimer_real(&self) -> IntervalTimerHandle {
1343 self.timers.itimer_real()
1344 }
1345
1346 pub fn set_itimer(
1347 &self,
1348 current_task: &CurrentTask,
1349 which: u32,
1350 value: itimerval,
1351 ) -> Result<itimerval, Errno> {
1352 if which == ITIMER_PROF || which == ITIMER_VIRTUAL {
1353 if value.it_value.tv_sec == 0 && value.it_value.tv_usec == 0 {
1357 return Ok(itimerval::default());
1358 }
1359 track_stub!(TODO("https://fxbug.dev/322874521"), "Unsupported itimer type", which);
1360 return error!(ENOTSUP);
1361 }
1362
1363 if which != ITIMER_REAL {
1364 return error!(EINVAL);
1365 }
1366 let itimer_real = self.itimer_real();
1367 let prev_remaining = itimer_real.time_remaining();
1368 if value.it_value.tv_sec != 0 || value.it_value.tv_usec != 0 {
1369 itimer_real.arm(current_task, itimerspec_from_itimerval(value), false)?;
1370 } else {
1371 itimer_real.disarm(current_task)?;
1372 }
1373 Ok(itimerval {
1374 it_value: timeval_from_duration(prev_remaining.remainder),
1375 it_interval: timeval_from_duration(prev_remaining.interval),
1376 })
1377 }
1378
1379 pub fn get_itimer(&self, which: u32) -> Result<itimerval, Errno> {
1380 if which == ITIMER_PROF || which == ITIMER_VIRTUAL {
1381 return Ok(itimerval::default());
1383 }
1384 if which != ITIMER_REAL {
1385 return error!(EINVAL);
1386 }
1387 let remaining = self.itimer_real().time_remaining();
1388 Ok(itimerval {
1389 it_value: timeval_from_duration(remaining.remainder),
1390 it_interval: timeval_from_duration(remaining.interval),
1391 })
1392 }
1393
1394 fn check_stopped_state(
1397 &self,
1398 new_stopped: StopState,
1399 finalize_only: bool,
1400 ) -> Option<StopState> {
1401 let stopped = self.load_stopped();
1402 if finalize_only && !stopped.is_stopping_or_stopped() {
1403 return Some(stopped);
1404 }
1405
1406 if stopped.is_illegal_transition(new_stopped) {
1407 return Some(stopped);
1408 }
1409
1410 return None;
1411 }
1412
1413 pub fn set_stopped(
1420 &self,
1421 new_stopped: StopState,
1422 siginfo: Option<SignalInfo>,
1423 finalize_only: bool,
1424 ) -> StopState {
1425 if let Some(stopped) = self.check_stopped_state(new_stopped, finalize_only) {
1427 return stopped;
1428 }
1429
1430 self.write().set_stopped(new_stopped, siginfo, finalize_only)
1431 }
1432
1433 pub fn controlling_terminal(&self) -> Option<ControllingTerminal> {
1435 self.read().controlling_terminal.clone()
1436 }
1437
1438 fn check_controlling_session(
1440 session: &Pid,
1441 controlling_session: &Option<ControllingSession>,
1442 ) -> Result<(), Errno> {
1443 if controlling_session.as_ref().is_some_and(|cs| Arc::ptr_eq(session, &cs.session)) {
1444 Ok(())
1445 } else {
1446 error!(ENOTTY)
1447 }
1448 }
1449
1450 pub fn get_foreground_process_group(&self, terminal: &Terminal) -> Result<pid_t, Errno> {
1451 let state = self.read();
1452 let terminal_state = terminal.read();
1453
1454 Self::check_controlling_session(&state.session, &terminal_state.controlling_session)?;
1457 if !state
1458 .controlling_terminal
1459 .as_ref()
1460 .is_some_and(|ct| std::ptr::eq(terminal, Arc::as_ptr(&ct.terminal)))
1461 {
1462 return error!(ENOTTY);
1463 }
1464 let controlling_session =
1465 terminal_state.controlling_session.as_ref().ok_or_else(|| errno!(ENOTTY))?;
1466 Ok(controlling_session.foreground_pgid())
1467 }
1468
1469 pub fn set_foreground_process_group(
1470 &self,
1471 current_task: &CurrentTask,
1472 terminal: &Terminal,
1473 pgid: &Pid,
1474 ) -> Result<(), Errno> {
1475 let process_group;
1476 let send_ttou;
1477 {
1478 let state = self.read();
1480 process_group = self.pgid();
1481 let mut terminal_state = terminal.write();
1482 Self::check_controlling_session(&state.session, &terminal_state.controlling_session)?;
1483 if !state
1484 .controlling_terminal
1485 .as_ref()
1486 .is_some_and(|ct| std::ptr::eq(terminal, Arc::as_ptr(&ct.terminal)))
1487 {
1488 return error!(ENOTTY);
1489 }
1490
1491 {
1493 let scope = RcuReadScope::new();
1494 if !pgid.is_process_group_in_session(&state.session, &scope) {
1495 return error!(EPERM);
1496 }
1497 }
1498
1499 let controlling_session =
1500 terminal_state.controlling_session.as_mut().ok_or_else(|| errno!(ENOTTY))?;
1501
1502 send_ttou = process_group.id != controlling_session.foreground_pgid()
1505 && !current_task.read().signal_mask().has_signal(SIGTTOU)
1506 && self.signal_actions.get(SIGTTOU).sa_handler != SIG_IGN;
1507
1508 if !send_ttou {
1509 controlling_session.set_foreground_process_group(pgid.clone());
1510 }
1511 }
1512
1513 if send_ttou {
1515 process_group.send_signals_to_pgid(&[SIGTTOU]);
1516 return error!(EINTR);
1517 }
1518
1519 Ok(())
1520 }
1521
1522 pub fn set_controlling_terminal(
1523 &self,
1524 current_task: &CurrentTask,
1525 terminal: &Terminal,
1526 side: TerminalSide,
1527 steal: bool,
1528 is_readable: bool,
1529 ) -> Result<(), Errno> {
1530 let steal_from_session;
1531 let new_controlling_terminal = ControllingTerminal::new(terminal, side);
1532 {
1533 let mut state = self.write();
1535 let mut terminal_state = terminal.write();
1536
1537 if state.session != self.leader {
1540 return error!(EPERM);
1541 }
1542
1543 if let Some(ref current_ct) = state.controlling_terminal {
1544 if current_ct.matches(terminal, side) {
1545 return Ok(());
1546 } else {
1547 return error!(EPERM);
1548 }
1549 }
1550
1551 let session_pid = state.session.clone();
1552 let other_session_pid = terminal_state.controlling_session().cloned();
1553 let mut has_admin_capability_determined = false;
1554 let mut stolen_session = None;
1555
1556 if let Some(other_sid) = other_session_pid {
1557 if other_sid != session_pid {
1558 if !steal {
1564 return error!(EPERM);
1565 }
1566 security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1567 has_admin_capability_determined = true;
1568 stolen_session = Some(other_sid);
1569 }
1570 }
1571
1572 if !is_readable && !has_admin_capability_determined {
1573 security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1574 }
1575
1576 state.controlling_terminal = Some(new_controlling_terminal);
1577 terminal_state.controlling_session =
1578 Some(ControllingSession::new(&session_pid, &self.pgid()));
1579 steal_from_session = stolen_session;
1580 }
1581
1582 if let Some(other_sid) = steal_from_session {
1584 other_sid.clear_controlling_terminal(Some((terminal, None)));
1585 }
1586
1587 Ok(())
1588 }
1589
1590 pub fn release_controlling_terminal(
1591 &self,
1592 _current_task: &CurrentTask,
1593 terminal: &Terminal,
1594 side: TerminalSide,
1595 ) -> Result<(), Errno> {
1596 let controlling_session = {
1597 let mut state = self.write();
1599 let is_session_leader = state.session == self.leader;
1600 let mut terminal_state = terminal.write();
1601
1602 Self::check_controlling_session(&state.session, &terminal_state.controlling_session)?;
1604 if !state.controlling_terminal.as_ref().is_some_and(|ct| ct.matches(terminal, side)) {
1605 return error!(ENOTTY);
1606 }
1607
1608 state.controlling_terminal = None;
1609 is_session_leader.then(|| terminal_state.controlling_session.take()).flatten()
1610 };
1611
1612 if let Some(controlling_session) = controlling_session {
1613 controlling_session.disassociate(terminal, Some(side));
1614 }
1615
1616 Ok(())
1617 }
1618
1619 fn check_orphaned_pgrp(
1622 _pids: &PidTableGuard<'_>,
1623 pgrp: &Pid,
1624 ignored_tg: Option<&ThreadGroup>,
1625 ) {
1626 let scope = RcuReadScope::new();
1627 if pgrp.will_become_orphaned_pgrp(&scope, ignored_tg) && pgrp.has_stopped_jobs(&scope) {
1628 pgrp.send_signals_to_pgid(&[SIGHUP, SIGCONT]);
1629 }
1630 }
1631
1632 pub fn kill_orphaned_pgrp(
1638 &self,
1639 pids: &PidTableGuard<'_>,
1640 parent: Option<&ThreadGroup>,
1641 ignored_tg: Option<&ThreadGroup>,
1642 ) {
1643 let Some(parent_tg) = parent else {
1644 return;
1645 };
1646
1647 let (pgrp, my_sid) = {
1648 let state = self.read();
1649 (self.pgid(), state.session.id)
1650 };
1651
1652 let (parent_pgid, parent_sid) = {
1653 let parent_state = parent_tg.read();
1654 (parent_tg.pgid().id, parent_state.session.id)
1655 };
1656
1657 if parent_pgid == pgrp.id || parent_sid != my_sid {
1659 return;
1660 }
1661
1662 Self::check_orphaned_pgrp(pids, &pgrp, ignored_tg);
1663 }
1664
1665 pub fn get_rlimit(&self, resource: Resource) -> u64 {
1666 self.limits.lock().get(resource).rlim_cur
1667 }
1668
1669 pub fn adjust_rlimits(
1671 current_task: &CurrentTask,
1672 target_task: &Task,
1673 resource: Resource,
1674 maybe_new_limit: Option<rlimit>,
1675 ) -> Result<rlimit, Errno> {
1676 let thread_group = target_task.thread_group();
1677 let mut limit_state = thread_group.limits.lock();
1678 let old_limit = limit_state.get(resource);
1679 if let Some(new_limit) = maybe_new_limit {
1680 if new_limit.rlim_max > old_limit.rlim_max
1681 && !security::is_task_capable_noaudit(current_task, CAP_SYS_RESOURCE)
1682 {
1683 return error!(EPERM);
1684 }
1685 security::task_setrlimit(current_task, &target_task, old_limit, new_limit)?;
1686 limit_state.set(resource, new_limit)
1687 }
1688 Ok(old_limit)
1689 }
1690
1691 pub fn time_stats(&self) -> TaskTimeStats {
1692 let process: &zx::Process = if self.process.as_handle_ref().is_invalid() {
1693 assert_eq!(
1696 self as *const ThreadGroup,
1697 Arc::as_ptr(&self.kernel.kthreads.system_thread_group())
1698 );
1699 &self.kernel.kthreads.starnix_process
1700 } else {
1701 &self.process
1702 };
1703
1704 let info =
1705 zx::Task::get_runtime_info(process).expect("Failed to get starnix process stats");
1706 TaskTimeStats {
1707 user_time: zx::MonotonicDuration::from_nanos(info.cpu_time),
1708 system_time: zx::MonotonicDuration::default(),
1710 }
1711 }
1712
1713 pub fn get_ptracees_and(
1717 &self,
1718 selector: &ProcessSelector,
1719 f: &mut dyn FnMut(&Task, &TaskMutableState),
1720 ) {
1721 for task_ref in self
1722 .ptracees
1723 .lock()
1724 .iter()
1725 .filter(|info| selector.match_tid(&info.tid))
1726 .filter_map(|info| info.tid.get_task().ok())
1727 {
1728 let task_state = task_ref.write();
1729 if task_state.ptrace.is_some() {
1730 f(&task_ref, &task_state);
1731 }
1732 }
1733 }
1734
1735 pub fn get_waitable_ptracee(
1740 &self,
1741 selector: &ProcessSelector,
1742 options: &WaitingOptions,
1743 pids: &mut PidTableGuard<'_>,
1744 ) -> Option<WaitResult> {
1745 let waitable_entry = self.write().zombie_ptracees.get_waitable_entry(selector, options);
1747 match waitable_entry {
1748 None => (),
1749 Some((zombie, None)) => return Some(zombie.to_wait_result()),
1750 Some((zombie, Some((tg, z)))) => {
1751 if let Some(tg) = tg.upgrade() {
1752 if Arc::as_ptr(&tg) != self as *const Self {
1753 tg.do_zombie_notifications(z, pids);
1754 } else {
1755 {
1756 let mut state = tg.write();
1757 state.children.remove(&z.task.pid);
1758 state
1759 .deferred_zombie_ptracers
1760 .retain(|dzp| dzp.tracee_pid != z.task.pid);
1761 }
1762
1763 z.release(pids);
1764 };
1765 }
1766 return Some(zombie.to_wait_result());
1767 }
1768 }
1769
1770 let mut tasks = vec![];
1771
1772 self.get_ptracees_and(selector, &mut |task: &Task, _| {
1774 tasks.push(task.weak_self.clone());
1775 });
1776 for task in tasks {
1777 let Some(task_ref) = task.upgrade() else {
1778 continue;
1779 };
1780
1781 let process_state = &mut task_ref.thread_group().write();
1782 let mut task_state = task_ref.write();
1783 if task_state
1784 .ptrace
1785 .as_ref()
1786 .is_some_and(|ptrace| ptrace.is_waitable(task_ref.load_stopped(), options))
1787 {
1788 let info = process_state.tasks.iter().next().unwrap().clone();
1794 let uid = info.real_creds().uid;
1795 let mut exit_status = None;
1796 let exit_signal = process_state.exit_signal.clone();
1797 let time_stats =
1798 process_state.base.time_stats() + process_state.children_time_stats;
1799 let task_stopped = task_ref.load_stopped();
1800
1801 #[derive(PartialEq)]
1802 enum ExitType {
1803 None,
1804 Cont,
1805 Stop,
1806 Kill,
1807 }
1808 if process_state.is_waitable() {
1809 let ptrace = &mut task_state.ptrace;
1810 let process_stopped = process_state.base.load_stopped();
1812 let mut fn_type = ExitType::None;
1813 if process_stopped == StopState::Awake && options.wait_for_continued {
1814 fn_type = ExitType::Cont;
1815 }
1816 let mut event = ptrace
1817 .as_ref()
1818 .map_or(PtraceEvent::None, |ptrace| {
1819 ptrace.event_data.as_ref().map_or(PtraceEvent::None, |data| data.event)
1820 })
1821 .clone();
1822 if process_stopped == StopState::GroupStopped
1824 && (options.wait_for_stopped || ptrace.is_some())
1825 {
1826 fn_type = ExitType::Stop;
1827 }
1828 if fn_type != ExitType::None {
1829 let siginfo = if options.keep_waitable_state {
1830 process_state.last_signal.clone()
1831 } else {
1832 process_state.last_signal.take()
1833 };
1834 if let Some(mut siginfo) = siginfo {
1835 if task_ref.thread_group().load_stopped() == StopState::GroupStopped
1836 && ptrace.as_ref().is_some_and(|ptrace| ptrace.is_seized())
1837 {
1838 if event == PtraceEvent::None {
1839 event = PtraceEvent::Stop;
1840 }
1841 siginfo.code |= (PtraceEvent::Stop as i32) << 8;
1842 }
1843 if siginfo.signal == SIGKILL {
1844 fn_type = ExitType::Kill;
1845 }
1846 exit_status = match fn_type {
1847 ExitType::Stop => Some((
1848 ExitStatus::Stop(siginfo, event),
1849 process_state.base.leader.clone(),
1850 )),
1851 ExitType::Cont => Some((
1852 ExitStatus::Continue(siginfo, event),
1853 process_state.base.leader.clone(),
1854 )),
1855 ExitType::Kill => Some((
1856 ExitStatus::Kill(siginfo),
1857 process_state.base.leader.clone(),
1858 )),
1859 _ => None,
1860 };
1861 }
1862 ptrace
1865 .as_mut()
1866 .map(|ptrace| ptrace.get_last_signal(options.keep_waitable_state));
1867 }
1868 }
1869 if exit_status.is_none() {
1870 if let Some(ptrace) = task_state.ptrace.as_mut() {
1871 let mut fn_type = ExitType::None;
1873 let event = ptrace
1874 .event_data
1875 .as_ref()
1876 .map_or(PtraceEvent::None, |event| event.event);
1877 if task_stopped == StopState::Awake {
1878 fn_type = ExitType::Cont;
1879 }
1880 if task_stopped.is_stopping_or_stopped()
1881 || ptrace.stop_status == PtraceStatus::Listening
1882 {
1883 fn_type = ExitType::Stop;
1884 }
1885 if fn_type != ExitType::None {
1886 if let Some(siginfo) =
1887 ptrace.get_last_signal(options.keep_waitable_state)
1888 {
1889 if siginfo.signal == SIGKILL {
1890 fn_type = ExitType::Kill;
1891 }
1892 exit_status = match fn_type {
1893 ExitType::Stop => Some((
1894 ExitStatus::Stop(siginfo, event),
1895 task_ref.tid.clone(),
1896 )),
1897 ExitType::Cont => Some((
1898 ExitStatus::Continue(siginfo, event),
1899 task_ref.tid.clone(),
1900 )),
1901 ExitType::Kill => {
1902 Some((ExitStatus::Kill(siginfo), task_ref.tid.clone()))
1903 }
1904 _ => None,
1905 };
1906 }
1907 }
1908 }
1909 }
1910 if let Some((exit_status, pid)) = exit_status {
1911 return Some(WaitResult {
1912 pid,
1913 uid,
1914 zombie_state: ZombieState { exit_status, time_stats },
1915 exit_signal,
1916 });
1917 }
1918 }
1919 }
1920 None
1921 }
1922
1923 pub fn send_signal_unchecked(
1933 &self,
1934 current_task: &CurrentTask,
1935 unchecked_signal: UncheckedSignal,
1936 ) -> Result<(), Errno> {
1937 if let Some(signal) = self.check_signal_access(current_task, unchecked_signal)? {
1938 let signal_info = SignalInfo::with_detail(
1939 signal,
1940 SI_USER as i32,
1941 SignalDetail::Kill {
1942 pid: current_task.pid.clone(),
1943 uid: current_task.current_creds().uid,
1944 },
1945 );
1946
1947 self.write().send_signal(signal_info);
1948 }
1949
1950 Ok(())
1951 }
1952
1953 pub unsafe fn send_signal_unchecked_debug(
1958 &self,
1959 current_task: &CurrentTask,
1960 unchecked_signal: UncheckedSignal,
1961 ) -> Result<(), Errno> {
1962 let signal = Signal::try_from(unchecked_signal)?;
1963 let signal_info = SignalInfo::with_detail(
1964 signal,
1965 SI_USER as i32,
1966 SignalDetail::Kill {
1967 pid: current_task.pid.clone(),
1968 uid: current_task.current_creds().uid,
1969 },
1970 );
1971
1972 self.write().send_signal(signal_info);
1973 Ok(())
1974 }
1975
1976 #[track_caller]
1990 pub fn send_signal_unchecked_with_info(
1991 &self,
1992 current_task: &CurrentTask,
1993 unchecked_signal: UncheckedSignal,
1994 siginfo: UncheckedSignalInfo,
1995 options: IntoSignalInfoOptions,
1996 ) -> Result<(), Errno> {
1997 if matches!(options, IntoSignalInfoOptions::CheckSigno)
1998 && siginfo.signo() as u64 != unchecked_signal.raw()
1999 {
2000 return error!(EINVAL);
2001 }
2002
2003 if let Some(signal) = self.check_signal_access(current_task, unchecked_signal)? {
2004 self.write().send_signal(siginfo.into_signal_info(signal, options)?);
2005 }
2006
2007 Ok(())
2008 }
2009
2010 fn check_signal_access(
2018 &self,
2019 current_task: &CurrentTask,
2020 unchecked_signal: UncheckedSignal,
2021 ) -> Result<Option<Signal>, Errno> {
2022 let Some(target_task) = self.read().get_signalable_task() else {
2026 return Ok(None);
2030 };
2031 current_task.can_signal(&target_task, unchecked_signal)?;
2032
2033 if unchecked_signal.is_zero() {
2035 return Ok(None);
2036 }
2037
2038 let signal = Signal::try_from(unchecked_signal)?;
2039 security::check_signal_access(current_task, &target_task, signal)?;
2040
2041 Ok(Some(signal))
2042 }
2043
2044 pub fn has_signal_queued(&self, signal: Signal) -> bool {
2045 self.pending_signals.lock().has_queued(signal)
2046 }
2047
2048 pub fn num_signals_queued(&self) -> usize {
2049 self.pending_signals.lock().num_queued()
2050 }
2051
2052 pub fn get_pending_signals(&self) -> SigSet {
2053 self.pending_signals.lock().pending()
2054 }
2055
2056 pub fn is_any_signal_allowed_by_mask(&self, mask: SigSet) -> bool {
2057 self.pending_signals.lock().is_any_allowed_by_mask(mask)
2058 }
2059
2060 pub fn take_next_signal_where<F>(&self, predicate: F) -> Option<SignalInfo>
2061 where
2062 F: Fn(&SignalInfo) -> bool,
2063 {
2064 let mut signals = self.pending_signals.lock();
2065 let r = signals.take_next_where(predicate);
2066 self.has_pending_signals.store(!signals.is_empty(), Ordering::Relaxed);
2067 r
2068 }
2069
2070 pub async fn shut_down(this: Weak<Self>, deferred_shutdown_deadline: zx::MonotonicInstant) {
2079 const SHUTDOWN_SIGNAL_HANDLING_TIMEOUT: zx::MonotonicDuration =
2080 zx::MonotonicDuration::from_seconds(1);
2081 const SHUTDOWN_CRASH_REPORT_TIMEOUT: zx::MonotonicDuration =
2084 zx::MonotonicDuration::from_millis(500);
2085
2086 let (tg_name, mut on_exited) = {
2088 let Some(this) = this.upgrade() else {
2090 return;
2091 };
2092
2093 let mut state = this.write();
2094 if state.is_exited() {
2095 return;
2097 }
2098
2099 let (on_exited_send, on_exited) = futures::channel::oneshot::channel();
2101 state.exit_notifier = Some(on_exited_send);
2102
2103 let tg_name = format!("{this:?}");
2105
2106 (tg_name, on_exited)
2107 };
2108
2109 async fn exited_by(
2111 on_exited: &mut futures::channel::oneshot::Receiver<()>,
2112 deadline: zx::MonotonicInstant,
2113 ) -> bool {
2114 let timeout = fuchsia_async::Timer::new(deadline);
2115 futures::pin_mut!(timeout);
2116 futures::select_biased! {
2117 _ = &mut *on_exited => true,
2118 _ = timeout => false,
2119 }
2120 }
2121
2122 let created_during_shutdown =
2125 this.upgrade().is_some_and(|tg| tg.created_during_shutdown.load(Ordering::Relaxed));
2126 let mut exited = false;
2127 if !created_during_shutdown {
2128 log_debug!(tg:% = tg_name; "shutting down thread group, sending SIGTERM");
2129 this.upgrade().map(|tg| tg.write().send_signal(SignalInfo::kernel(SIGTERM)));
2130
2131 exited = exited_by(
2133 &mut on_exited,
2134 zx::MonotonicInstant::after(SHUTDOWN_SIGNAL_HANDLING_TIMEOUT),
2135 )
2136 .await;
2137 }
2138
2139 let deferred = created_during_shutdown
2142 || (!exited
2143 && this.upgrade().is_some_and(|tg| {
2144 let state = tg.read();
2145 state.is_running() && state.is_handling_core_dump_signal()
2146 }));
2147 if deferred {
2148 if zx::MonotonicInstant::get() < deferred_shutdown_deadline {
2149 log_info!(tg:% = tg_name; "deferred shutdown process, deferring SIGKILL");
2150 exited = exited_by(&mut on_exited, deferred_shutdown_deadline).await;
2151 }
2152 if !exited {
2153 this.upgrade().map(|tg| tg.kernel.kill_processes_created_during_shutdown());
2154 exited = exited_by(
2158 &mut on_exited,
2159 zx::MonotonicInstant::after(SHUTDOWN_CRASH_REPORT_TIMEOUT),
2160 )
2161 .await;
2162 }
2163 }
2164
2165 if !exited {
2166 log_debug!(tg:% = tg_name; "sending SIGKILL");
2167 this.upgrade().map(|tg| tg.write().send_signal(SignalInfo::forced(SIGKILL)));
2168 }
2169
2170 log_debug!(tg:% = tg_name; "waiting for exit");
2171 on_exited.await.ok();
2174 log_debug!(tg:% = tg_name; "thread group shutdown complete");
2175 }
2176
2177 pub fn get_process_koid(&self) -> Result<Koid, Status> {
2183 self.process.koid()
2184 }
2185}
2186
2187pub enum WaitableChildResult {
2188 ReadyNow(Box<WaitResult>),
2189 ShouldWait,
2190 NoneFound,
2191}
2192
2193#[apply(state_implementation!)]
2194impl ThreadGroupMutableState<Base = ThreadGroup> {
2195 pub fn parent_death_signal(&self) -> Option<Signal> {
2197 self.parent_death_signal
2198 }
2199
2200 pub fn set_parent_death_signal(&mut self, signal: Option<Signal>) {
2202 self.parent_death_signal = signal;
2203 }
2204
2205 fn parent_death_signal_for_exiting_task(
2213 &self,
2214 exiting_task: &Arc<Task>,
2215 parent_is_subreaper: bool,
2216 ) -> Option<Signal> {
2217 let should_signal =
2218 self.parent.as_ref().is_some_and(|parent| match parent.creator_task() {
2219 Some(creator_task) => Weak::ptr_eq(creator_task, &exiting_task.weak_self),
2220 None => parent_is_subreaper,
2221 });
2222 should_signal.then_some(self.parent_death_signal).flatten()
2223 }
2224
2225 pub fn leader(&self) -> pid_t {
2226 self.base.leader.id
2227 }
2228
2229 pub fn leader_command(&self) -> TaskCommand {
2230 self.get_task(self.leader())
2231 .map(|l| l.command())
2232 .unwrap_or_else(|| TaskCommand::new(b"<leader exited>"))
2233 }
2234
2235 pub fn is_running(&self) -> bool {
2236 matches!(self.run_state, ThreadGroupRunState::Running)
2237 }
2238
2239 pub fn is_handling_core_dump_signal(&self) -> bool {
2242 self.tasks().iter().any(|t| t.flags().contains(TaskFlags::IN_CORE_DUMP_SIGNAL_HANDLER))
2243 }
2244
2245 pub fn is_deferred_shutdown_process(&self) -> bool {
2250 self.base.kernel.is_shutting_down()
2251 && (self.base.created_during_shutdown.load(Ordering::Relaxed)
2252 || self.is_handling_core_dump_signal())
2253 }
2254
2255 pub fn is_exited(&self) -> bool {
2256 matches!(self.run_state, ThreadGroupRunState::Exited(_))
2257 }
2258
2259 fn set_exiting(&mut self, exit_status: ExitStatus) {
2261 self.run_state = ThreadGroupRunState::Exiting(exit_status);
2262 }
2263
2264 fn set_exited(&mut self) {
2265 let ThreadGroupRunState::Exiting(exit_status) = std::mem::take(&mut self.run_state) else {
2266 panic!("Must transition from Exiting to Exited");
2267 };
2268 self.run_state = ThreadGroupRunState::Exited(exit_status);
2269
2270 if let Some(notifier) = self.exit_notifier.take() {
2271 let _ = notifier.send(());
2272 }
2273 }
2274
2275 pub fn children(&self) -> impl Iterator<Item = Arc<ThreadGroup>> + '_ {
2281 self.children.iter().flat_map(|pid| pid.get_thread_group().ok())
2282 }
2283
2284 pub fn tasks(&self) -> Vec<Arc<Task>> {
2285 self.tasks.iter().flat_map(|info| info.tid.get_task().ok()).collect()
2286 }
2287
2288 pub fn first_task(&self) -> Option<Arc<Task>> {
2289 self.tasks.iter().find_map(|info| info.tid.get_task().ok())
2290 }
2291
2292 pub fn task_ids(&self) -> impl Iterator<Item = tid_t> + '_ {
2293 self.tasks.iter().map(|info| info.tid.id)
2294 }
2295
2296 pub fn contains_task(&self, tid: tid_t) -> bool {
2297 self.tasks.iter().any(|info| info.tid.id == tid)
2298 }
2299
2300 pub fn get_task(&self, tid: tid_t) -> Option<Arc<Task>> {
2301 self.tasks.iter().find(|info| info.tid.id == tid).and_then(|info| info.tid.get_task().ok())
2302 }
2303
2304 pub fn tasks_count(&self) -> usize {
2305 self.tasks.len()
2306 }
2307
2308 pub fn get_ppid(&self) -> pid_t {
2309 match &self.parent {
2310 Some(parent) => parent.upgrade().leader.id,
2311 None => 0,
2312 }
2313 }
2314
2315 fn reap_zombie(&mut self, zombie: OwnedRef<ZombieProcess>, pids: &mut PidTableGuard<'_>) {
2317 self.children_time_stats += zombie.state.time_stats;
2318 zombie.release(pids);
2319 }
2320
2321 pub fn is_waitable(&self) -> bool {
2324 return self.last_signal.is_some() && !self.base.load_stopped().is_in_progress();
2325 }
2326
2327 pub fn get_waitable_zombie(
2328 &mut self,
2329 zombie_list: &dyn Fn(&mut ThreadGroupMutableState) -> &mut Vec<OwnedRef<ZombieProcess>>,
2330 selector: &ProcessSelector,
2331 options: &WaitingOptions,
2332 pids: &mut PidTableGuard<'_>,
2333 ) -> Option<WaitResult> {
2334 let selected_zombie_position = zombie_list(self)
2336 .iter()
2337 .rev()
2338 .position(|zombie| zombie.matches_selector_and_waiting_option(selector, options))
2339 .map(|position_starting_from_the_back| {
2340 zombie_list(self).len() - 1 - position_starting_from_the_back
2341 });
2342
2343 selected_zombie_position.map(|position| {
2344 if options.keep_waitable_state {
2345 zombie_list(self)[position].to_wait_result()
2346 } else {
2347 let zombie = zombie_list(self).remove(position);
2348 let result = zombie.to_wait_result();
2349 self.reap_zombie(zombie, pids);
2350 result
2351 }
2352 })
2353 }
2354
2355 pub fn is_correct_exit_signal(for_clone: bool, exit_code: Option<Signal>) -> bool {
2356 for_clone == (exit_code != Some(SIGCHLD))
2357 }
2358
2359 fn get_waitable_running_children(
2360 &self,
2361 selector: &ProcessSelector,
2362 options: &WaitingOptions,
2363 ) -> WaitableChildResult {
2364 let filter_children_by_pid_selector = |child: &ThreadGroup| match selector {
2366 ProcessSelector::Any => true,
2367 ProcessSelector::Pid(pid) => &child.leader == pid,
2368 ProcessSelector::Pgid(pgid) => &child.pgid() == pgid,
2369 };
2370
2371 let filter_children_by_waiting_options = |child: &ThreadGroup| {
2373 if options.wait_for_all {
2374 return true;
2375 }
2376 let _token = allow_subclass();
2380 Self::is_correct_exit_signal(options.wait_for_clone, child.read().exit_signal)
2381 };
2382
2383 let mut selected_children = self
2386 .children()
2387 .filter(|tg| filter_children_by_pid_selector(&tg))
2388 .filter(|tg| filter_children_by_waiting_options(&tg))
2389 .peekable();
2390 if selected_children.peek().is_none() {
2391 if self.deferred_zombie_ptracers.iter().any(|dzp| match selector {
2393 ProcessSelector::Any => true,
2394 ProcessSelector::Pid(pid) => &dzp.tracee_pid == pid,
2395 ProcessSelector::Pgid(pgid) => dzp.tracee_pgid().as_ref() == Some(pgid),
2396 }) {
2397 return WaitableChildResult::ShouldWait;
2398 }
2399
2400 return WaitableChildResult::NoneFound;
2401 }
2402 for child in selected_children {
2403 let _token = allow_subclass();
2407 let child = child.write();
2408 if child.last_signal.is_some() {
2409 let build_wait_result = |mut child: ThreadGroupWriteGuard<'_>,
2410 exit_status: &dyn Fn(SignalInfo) -> ExitStatus|
2411 -> WaitResult {
2412 let siginfo = if options.keep_waitable_state {
2413 child.last_signal.clone().unwrap()
2414 } else {
2415 child.last_signal.take().unwrap()
2416 };
2417 let exit_status = if siginfo.signal == SIGKILL {
2418 ExitStatus::Kill(siginfo)
2420 } else {
2421 exit_status(siginfo)
2422 };
2423 let info = child.tasks.iter().next().unwrap();
2424 let uid = info.real_creds().uid;
2425 WaitResult {
2426 pid: child.base.leader.clone(),
2427 uid,
2428 zombie_state: ZombieState {
2429 exit_status,
2430 time_stats: child.base.time_stats() + child.children_time_stats,
2431 },
2432 exit_signal: child.exit_signal,
2433 }
2434 };
2435 let child_stopped = child.base.load_stopped();
2436 if child_stopped == StopState::Awake && options.wait_for_continued {
2437 return WaitableChildResult::ReadyNow(Box::new(build_wait_result(
2438 child,
2439 &|siginfo| ExitStatus::Continue(siginfo, PtraceEvent::None),
2440 )));
2441 }
2442 if child_stopped == StopState::GroupStopped && options.wait_for_stopped {
2443 return WaitableChildResult::ReadyNow(Box::new(build_wait_result(
2444 child,
2445 &|siginfo| ExitStatus::Stop(siginfo, PtraceEvent::None),
2446 )));
2447 }
2448 }
2449 }
2450
2451 WaitableChildResult::ShouldWait
2452 }
2453
2454 pub fn get_waitable_child(
2460 &mut self,
2461 selector: &ProcessSelector,
2462 options: &WaitingOptions,
2463 pids: &mut PidTableGuard<'_>,
2464 ) -> WaitableChildResult {
2465 if options.wait_for_exited {
2466 if let Some(waitable_zombie) = self.get_waitable_zombie(
2467 &|state: &mut ThreadGroupMutableState| &mut state.zombie_children,
2468 selector,
2469 options,
2470 pids,
2471 ) {
2472 return WaitableChildResult::ReadyNow(Box::new(waitable_zombie));
2473 }
2474 }
2475
2476 self.get_waitable_running_children(selector, options)
2477 }
2478
2479 pub fn get_running_task(&self) -> Result<Arc<Task>, Errno> {
2481 self.tasks
2482 .iter()
2483 .find_map(|info| info.tid.get_task().ok().filter(|task| task.is_running()))
2484 .ok_or_else(|| errno!(ESRCH))
2485 }
2486
2487 pub fn get_running_task_with_files(&self) -> Result<Arc<Task>, Errno> {
2490 if let Ok(task) = self.base.leader.get_task() {
2491 if task.is_running() && task.files().is_ok() {
2492 return Ok(task);
2493 }
2494 }
2495 self.tasks
2496 .iter()
2497 .find_map(|info| {
2498 info.tid.get_task().ok().filter(|task| task.is_running() && task.files().is_ok())
2499 })
2500 .ok_or_else(|| errno!(ESRCH))
2501 }
2502
2503 fn get_signalable_task(&self) -> Option<Arc<Task>> {
2508 let mut non_running = if let Ok(task) = self.base.leader.get_task() {
2509 if task.is_running() {
2510 return Some(task);
2511 }
2512 Some(task)
2513 } else {
2514 None
2515 };
2516 for container in &self.tasks {
2517 if let Ok(task) = container.tid.get_task() {
2518 if task.is_running() {
2519 return Some(task);
2520 }
2521 if non_running.is_none() {
2522 non_running = Some(task);
2523 }
2524 }
2525 }
2526 non_running
2527 }
2528
2529 pub fn set_stopped(
2536 mut self,
2537 new_stopped: StopState,
2538 siginfo: Option<SignalInfo>,
2539 finalize_only: bool,
2540 ) -> StopState {
2541 if let Some(stopped) = self.base.check_stopped_state(new_stopped, finalize_only) {
2542 return stopped;
2543 }
2544
2545 if self.base.load_stopped() == StopState::Waking
2548 && (new_stopped == StopState::GroupStopping || new_stopped == StopState::GroupStopped)
2549 {
2550 return self.base.load_stopped();
2551 }
2552
2553 self.store_stopped(new_stopped);
2557 if let Some(signal) = &siginfo {
2558 if signal.signal != SIGKILL {
2562 self.last_signal = siginfo;
2563 }
2564 }
2565 if new_stopped == StopState::Waking || new_stopped == StopState::ForceWaking {
2566 self.lifecycle_waiters.notify_value(ThreadGroupLifecycleWaitValue::Stopped);
2567 };
2568
2569 let parent = (!new_stopped.is_in_progress()).then(|| self.parent.clone()).flatten();
2570
2571 std::mem::drop(self);
2573 if let Some(parent) = parent {
2574 let parent = parent.upgrade();
2575 parent
2576 .write()
2577 .lifecycle_waiters
2578 .notify_value(ThreadGroupLifecycleWaitValue::ChildStatus);
2579 }
2580
2581 new_stopped
2582 }
2583
2584 fn store_stopped(&mut self, state: StopState) {
2585 self.base.stop_state.store(state, Ordering::Relaxed)
2590 }
2591
2592 #[allow(unused_mut, reason = "needed for some but not all macro outputs")]
2602 fn terminate_tasks(
2603 mut self,
2604 exit_status: ExitStatus,
2605 wake_signal: SignalInfo,
2606 excluded_tid: Option<&Pid>,
2607 ) {
2608 if self.is_running() {
2609 self.set_exiting(exit_status.clone());
2610 }
2611
2612 let tasks: Vec<Arc<Task>> = self
2613 .tasks()
2614 .into_iter()
2615 .filter(|task| task.is_running() && excluded_tid != Some(&task.tid))
2616 .collect();
2617 for task in &tasks {
2618 let mut task_state = task.write();
2619 task_state.set_exit_status_if_not_already(exit_status.clone());
2620 task_state.force_wake_for_exit();
2621 }
2622
2623 self.set_stopped(
2624 StopState::ForceWaking,
2625 Some(wake_signal),
2626 false,
2627 );
2628
2629 for task in tasks {
2630 task.interrupt();
2631 }
2632 }
2633
2634 #[allow(unused_mut, reason = "needed for some but not all macro outputs")]
2635 pub(crate) fn force_exit_for_sigkill(mut self, signal_info: SignalInfo) {
2636 let exit_status = ExitStatus::Kill(signal_info.clone());
2637 self.terminate_tasks(exit_status, signal_info, None);
2638 }
2639
2640 #[allow(unused_mut, reason = "needed for some but not all macro outputs")]
2642 pub fn send_signal(mut self, signal_info: SignalInfo) {
2643 let ignores_signal = self.base.ignores_signal(&signal_info);
2644 if ignores_signal
2645 && (signal_info.signal == SIGKILL
2646 || !self
2647 .tasks
2648 .iter()
2649 .filter_map(|info| info.tid.get_task().ok())
2650 .any(|task| task.is_running() && task.read().is_ptraced()))
2651 {
2652 return;
2653 }
2654
2655 if signal_info.signal == SIGKILL {
2658 self.force_exit_for_sigkill(signal_info);
2659 return;
2660 }
2661
2662 let sigaction = self.base.signal_actions.get(signal_info.signal);
2663 let action = action_for_signal(&signal_info, sigaction);
2664
2665 let queue_on_group = action != DeliveryAction::Ignore
2671 || self
2672 .tasks
2673 .iter()
2674 .filter_map(|info| info.tid.get_task().ok())
2675 .filter(|task| task.is_running())
2676 .any(|task| {
2677 let task_state = task.read();
2678 task_state.is_signal_masked(signal_info.signal)
2679 || task_state.is_signal_masked_by_saved_mask(signal_info.signal)
2680 || task_state.is_ptraced()
2681 });
2682
2683 if queue_on_group {
2684 let mut pending_signals = self.base.pending_signals.lock();
2685 pending_signals.enqueue(signal_info.clone());
2686 self.base.has_pending_signals.store(true, Ordering::Relaxed);
2687 }
2688
2689 let tasks: Vec<Pid> = self.tasks.iter().map(|info| info.tid.clone()).collect();
2690
2691 if signal_info.signal == SIGCONT {
2693 self.set_stopped(
2694 StopState::Waking,
2695 Some(signal_info.clone()),
2696 false,
2697 );
2698 }
2699
2700 let mut has_interrupted_task = false;
2701 for task in tasks.iter().flat_map(|pid| pid.get_task().ok()) {
2702 if !task.is_running() {
2703 continue;
2704 }
2705
2706 let mut task_state = task.write();
2707
2708 if signal_info.signal == SIGCONT {
2709 task_state.set_stopped(
2710 StopState::Waking,
2711 None,
2712 None,
2713 None,
2714 );
2715 }
2716
2717 let is_masked = task_state.is_signal_masked(signal_info.signal);
2718 let was_masked = task_state.is_signal_masked_by_saved_mask(signal_info.signal);
2719
2720 let is_queued = (!ignores_signal
2721 && (action != DeliveryAction::Ignore || is_masked || was_masked))
2722 || task_state.is_ptraced();
2723
2724 if is_queued {
2725 task_state.notify_signal_waiters(&signal_info.signal);
2726
2727 let is_fatal = action == DeliveryAction::Terminate && !task_state.is_ptraced();
2728
2729 if !is_masked
2730 && action.must_interrupt(Some(sigaction))
2731 && (!has_interrupted_task || is_fatal)
2732 {
2733 drop(task_state);
2736 task.interrupt();
2737 has_interrupted_task = true;
2738 }
2739 }
2740 }
2741 }
2742}
2743
2744#[cfg(test)]
2745mod test {
2746 use super::*;
2747 use crate::ptrace::{PtraceAttachType, ptrace_attach, ptrace_dispatch, ptrace_traceme};
2748 use crate::signals::{dequeue_signal, send_signal};
2749 use crate::testing::*;
2750 use starnix_syscalls::SUCCESS;
2751 use starnix_uapi::signals::SIGSTOP;
2752 use starnix_uapi::user_address::{UserAddress, UserRef};
2753 use starnix_uapi::{
2754 CLONE_SIGHAND, CLONE_THREAD, CLONE_VM, PTRACE_CONT, PTRACE_KILL, PTRACE_O_EXITKILL,
2755 SI_TKILL,
2756 };
2757
2758 #[::fuchsia::test]
2759 async fn test_setsid() {
2760 spawn_kernel_and_run(async |current_task| {
2761 fn get_process_group(task: &Task) -> Pid {
2762 task.thread_group().pgid()
2763 }
2764 assert_eq!(current_task.thread_group().setsid(), error!(EPERM));
2765
2766 let child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2767 assert_eq!(get_process_group(¤t_task), get_process_group(&child_task));
2768
2769 let old_process_group = child_task.thread_group().pgid();
2770 assert_eq!(child_task.thread_group().setsid(), Ok(()));
2771 assert_eq!(child_task.thread_group().read().session, child_task.pid);
2772 let scope = RcuReadScope::new();
2773 assert!(
2774 !old_process_group
2775 .pgid_thread_groups(&scope)
2776 .any(|tg| Arc::ptr_eq(&tg, child_task.thread_group()))
2777 );
2778 })
2779 .await;
2780 }
2781
2782 #[::fuchsia::test]
2783 async fn test_exit_status() {
2784 spawn_kernel_and_run(async |current_task| {
2785 let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
2786 child.thread_group().kill(ExitStatus::Exit(42), None);
2787 assert_eq!(child.exit_status(), Some(ExitStatus::Exit(42)));
2788 std::mem::drop(child);
2789 assert_eq!(
2790 current_task.thread_group().read().zombie_children[0].state.exit_status,
2791 ExitStatus::Exit(42)
2792 );
2793 })
2794 .await;
2795 }
2796
2797 #[::fuchsia::test]
2798 async fn test_kill_thread_group_when_already_exiting() {
2799 spawn_kernel_and_run(async |current_task| {
2800 let clone_thread_flags = (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64;
2801 let mut leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
2802 let mut sibling = leader.clone_task_for_test(clone_thread_flags, None);
2803
2804 leader.thread_group().write().set_exiting(ExitStatus::Exit(42));
2807 sibling.kill_thread_group(ExitStatus::Exit(99));
2808 assert!(sibling.is_exitted());
2809
2810 leader.kill_thread_group(ExitStatus::Exit(0));
2811 assert!(leader.is_exitted());
2812 std::mem::drop(sibling);
2813 std::mem::drop(leader);
2814 assert_eq!(
2815 current_task.thread_group().read().zombie_children[0].state.exit_status,
2816 ExitStatus::Exit(42)
2817 );
2818 })
2819 .await;
2820 }
2821
2822 #[::fuchsia::test]
2823 async fn test_unblockable_signals_to_init_ignored() {
2824 spawn_kernel_and_run(async |current_task| {
2825 assert!(current_task.thread_group().is_init());
2826
2827 for signal in [SIGKILL, SIGSTOP] {
2828 assert_eq!(
2830 current_task.thread_group().send_signal_unchecked(current_task, signal.into()),
2831 Ok(())
2832 );
2833 assert!(current_task.thread_group().read().is_running());
2834 assert!(!current_task.is_exitted());
2835 assert_eq!(current_task.read().queued_signal_count(signal), 0);
2836
2837 let user_signal_info = SignalInfo::with_sender(
2840 signal,
2841 SI_TKILL,
2842 SignalDetail::Kill {
2843 pid: current_task.pid.clone(),
2844 uid: current_task.current_creds().uid,
2845 },
2846 Some(current_task.weak_self.clone()),
2847 );
2848 assert_eq!(send_signal(current_task, user_signal_info), Ok(()));
2849 assert!(current_task.thread_group().read().is_running());
2850 assert!(!current_task.is_exitted());
2851 assert_eq!(current_task.read().queued_signal_count(signal), 0);
2852 }
2853
2854 let mut tracer = current_task.clone_task_for_test(0, Some(SIGCHLD));
2858 assert_eq!(
2859 ptrace_attach(
2860 &mut tracer,
2861 ¤t_task.tid,
2862 PtraceAttachType::Seize,
2863 UserAddress::from(PTRACE_O_EXITKILL as u64),
2864 ),
2865 Ok(SUCCESS)
2866 );
2867
2868 for signal in [SIGKILL, SIGSTOP] {
2869 current_task.write().set_stopped(
2871 StopState::SignalDeliveryStopping,
2872 Some(SignalInfo::kernel(SIGSTOP)),
2873 None,
2874 None,
2875 );
2876 current_task.write().set_stopped(
2877 StopState::SignalDeliveryStopped,
2878 None,
2879 None,
2880 None,
2881 );
2882
2883 if signal == SIGKILL {
2884 assert_eq!(
2885 ptrace_dispatch(
2886 &mut tracer,
2887 PTRACE_KILL,
2888 ¤t_task.tid,
2889 UserAddress::NULL,
2890 UserAddress::NULL,
2891 ),
2892 Ok(SUCCESS)
2893 );
2894 assert!(current_task.thread_group().read().is_running());
2895 assert!(!current_task.is_exitted());
2896 }
2897
2898 assert_eq!(
2901 ptrace_dispatch(
2902 &mut tracer,
2903 PTRACE_CONT,
2904 ¤t_task.tid,
2905 UserAddress::NULL,
2906 UserAddress::from(signal.number() as u64),
2907 ),
2908 Ok(SUCCESS)
2909 );
2910 assert!(current_task.thread_group().read().is_running());
2911 assert!(!current_task.is_exitted());
2912 assert_eq!(current_task.load_stopped(), StopState::Waking);
2913 assert_eq!(current_task.read().queued_signal_count(signal), 0);
2914 }
2915
2916 current_task.write().set_stopped(StopState::Awake, None, None, None);
2919 current_task.thread_group().set_stopped(StopState::Awake, None, false);
2920 assert_eq!(
2921 current_task.thread_group().send_signal_unchecked(current_task, SIGSTOP.into()),
2922 Ok(())
2923 );
2924 assert!(current_task.thread_group().has_signal_queued(SIGSTOP));
2925 dequeue_signal(current_task);
2926 assert_eq!(current_task.load_stopped(), StopState::SignalDeliveryStopping);
2927 current_task.write().set_stopped(StopState::Awake, None, None, None);
2928
2929 assert_eq!(send_signal(current_task, SignalInfo::kernel(SIGSTOP)), Ok(()));
2930 assert_eq!(current_task.read().queued_signal_count(SIGSTOP), 1);
2931
2932 std::mem::drop(tracer);
2935 assert!(current_task.thread_group().read().is_running());
2936 assert!(!current_task.is_exitted());
2937 assert_eq!(current_task.read().queued_signal_count(SIGKILL), 0);
2938
2939 dequeue_signal(current_task);
2942 assert_eq!(current_task.read().queued_signal_count(SIGSTOP), 0);
2943 assert_eq!(current_task.thread_group().load_stopped(), StopState::Awake);
2944 assert!(current_task.thread_group().read().is_running());
2945 assert!(!current_task.is_exitted());
2946
2947 current_task.thread_group().write().send_signal(SignalInfo::forced(SIGKILL));
2949 assert!(!current_task.thread_group().read().is_running());
2950 assert!(current_task.is_exitted());
2951 })
2952 .await;
2953 }
2954
2955 #[::fuchsia::test]
2956 async fn test_sigkill_fast_path_exiting() {
2957 spawn_kernel_and_run(async |current_task| {
2958 let clone_thread_flags = (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64;
2959
2960 let process_killed_leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
2962 let process_killed_thread =
2963 process_killed_leader.clone_task_for_test(clone_thread_flags, None);
2964 assert!(process_killed_leader.thread_group().read().is_running());
2965 assert!(!process_killed_leader.is_exitted());
2966 assert!(!process_killed_thread.is_exitted());
2967
2968 assert_eq!(
2969 process_killed_leader
2970 .thread_group()
2971 .send_signal_unchecked(current_task, SIGKILL.into()),
2972 Ok(())
2973 );
2974 assert!(!process_killed_leader.thread_group().read().is_running());
2975 assert!(process_killed_leader.is_exitted());
2976 assert!(process_killed_thread.is_exitted());
2977 std::mem::drop(process_killed_thread);
2978 std::mem::drop(process_killed_leader);
2979
2980 let task_killed_leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
2982 let task_killed_thread =
2983 task_killed_leader.clone_task_for_test(clone_thread_flags, None);
2984 let task_sigkill_info = SignalInfo::with_sender(
2985 SIGKILL,
2986 SI_TKILL,
2987 SignalDetail::Kill {
2988 pid: current_task.pid.clone(),
2989 uid: current_task.current_creds().uid,
2990 },
2991 Some(current_task.weak_self.clone()),
2992 );
2993 assert_eq!(send_signal(&task_killed_thread, task_sigkill_info), Ok(()));
2994 assert!(!task_killed_leader.thread_group().read().is_running());
2995 assert!(task_killed_leader.is_exitted());
2996 assert!(task_killed_thread.is_exitted());
2997 })
2998 .await;
2999 }
3000
3001 #[::fuchsia::test]
3002 async fn test_setgpid() {
3003 spawn_kernel_and_run(async |current_task| {
3004 assert_eq!(current_task.thread_group().setsid(), error!(EPERM));
3005
3006 let child_task1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
3007 let child_task2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
3008 let execd_child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
3009 execd_child_task.thread_group().write().did_exec = true;
3010 let other_session_child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
3011 assert_eq!(other_session_child_task.thread_group().setsid(), Ok(()));
3012
3013 assert_eq!(
3014 child_task1.thread_group().setpgid(¤t_task, ¤t_task, ¤t_task.pid),
3015 error!(ESRCH)
3016 );
3017 assert_eq!(
3018 current_task.thread_group().setpgid(
3019 ¤t_task,
3020 &execd_child_task,
3021 &execd_child_task.pid
3022 ),
3023 error!(EACCES)
3024 );
3025 assert_eq!(
3026 current_task.thread_group().setpgid(
3027 ¤t_task,
3028 ¤t_task,
3029 ¤t_task.pid
3030 ),
3031 error!(EPERM)
3032 );
3033 assert_eq!(
3034 current_task.thread_group().setpgid(
3035 ¤t_task,
3036 &other_session_child_task,
3037 &other_session_child_task.pid
3038 ),
3039 error!(EPERM)
3040 );
3041 assert_eq!(
3042 current_task.thread_group().setpgid(¤t_task, &child_task1, &child_task2.pid),
3043 error!(EPERM)
3044 );
3045 assert_eq!(
3046 current_task.thread_group().setpgid(
3047 ¤t_task,
3048 &child_task1,
3049 &other_session_child_task.pid
3050 ),
3051 error!(EPERM)
3052 );
3053
3054 assert_eq!(
3055 child_task1.thread_group().setpgid(¤t_task, &child_task1, &child_task1.pid),
3056 Ok(())
3057 );
3058 assert_eq!(child_task1.thread_group().read().session, current_task.tid);
3059 assert_eq!(child_task1.thread_group().pgid(), child_task1.tid);
3060
3061 let old_process_group = child_task2.thread_group().pgid();
3062 assert_eq!(
3063 current_task.thread_group().setpgid(¤t_task, &child_task2, &child_task1.pid),
3064 Ok(())
3065 );
3066 assert_eq!(child_task2.thread_group().pgid(), child_task1.tid);
3067 let scope = RcuReadScope::new();
3068 assert!(
3069 !old_process_group
3070 .pgid_thread_groups(&scope)
3071 .any(|tg| Arc::ptr_eq(&tg, child_task2.thread_group()))
3072 );
3073
3074 let child_task3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
3075 assert_eq!(
3076 child_task3.thread_group().setpgid(¤t_task, &child_task3, &child_task3.pid),
3077 Ok(())
3078 );
3079 assert_eq!(
3081 current_task.thread_group().setpgid(¤t_task, &child_task1, &child_task3.pid),
3082 Ok(())
3083 );
3084 assert_eq!(child_task1.thread_group().pgid(), child_task3.tid);
3085
3086 assert_eq!(
3088 child_task1.thread_group().setpgid(¤t_task, &child_task1, &child_task1.pid),
3089 Ok(())
3090 );
3091 assert_eq!(child_task1.thread_group().pgid(), child_task1.tid);
3092 let pg1 = child_task1.thread_group().pgid();
3093 let pg2 = child_task2.thread_group().pgid();
3094 assert_eq!(pg1, pg2);
3095
3096 assert_eq!(
3097 crate::task::syscalls::sys_setpgid(¤t_task, child_task1.pid.id, -1),
3098 error!(EINVAL)
3099 );
3100 assert_eq!(
3101 crate::task::syscalls::sys_setpgid(¤t_task, child_task1.pid.id, 255),
3102 error!(EPERM)
3103 );
3104 })
3105 .await;
3106 }
3107
3108 #[::fuchsia::test]
3109 async fn test_adopt_children() {
3110 spawn_kernel_and_run(async |current_task| {
3111 let task1 = current_task.clone_task_for_test(0, None);
3112 let task2 = task1.clone_task_for_test(0, None);
3113 let task3 = task2.clone_task_for_test(0, None);
3114
3115 assert_eq!(task3.thread_group().read().get_ppid(), task2.tid.id);
3116 assert!(current_task.thread_group().read().children.contains(&task1.pid));
3117 assert!(task1.thread_group().read().children.contains(&task2.pid));
3118 assert!(task2.thread_group().read().children.contains(&task3.pid));
3119
3120 task2.thread_group().kill(ExitStatus::Exit(0), None);
3121 std::mem::drop(task2);
3122
3123 assert_eq!(task3.thread_group().read().get_ppid(), current_task.tid.id);
3125 assert!(current_task.thread_group().read().children.contains(&task3.pid));
3126 assert!(!task1.thread_group().read().children.contains(&task3.pid));
3127 })
3128 .await;
3129 }
3130
3131 #[::fuchsia::test]
3132 async fn test_thread_group_children_lifecycle() {
3133 spawn_kernel_and_run(async |current_task| {
3134 let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
3135 let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
3136 let child1_pid = child1.pid.clone();
3137 let child2_pid = child2.pid.clone();
3138
3139 assert!(current_task.thread_group().read().children.contains(&child1_pid));
3140 assert!(current_task.thread_group().read().children.contains(&child2_pid));
3141
3142 let children_pids: HashSet<Pid> =
3143 current_task.thread_group().read().children().map(|tg| tg.leader.clone()).collect();
3144 assert!(children_pids.contains(&child1_pid));
3145 assert!(children_pids.contains(&child2_pid));
3146
3147 child1.thread_group().kill(ExitStatus::Exit(0), None);
3148 std::mem::drop(child1);
3149
3150 assert!(child1_pid.get_thread_group().is_err());
3151 assert!(!current_task.thread_group().read().children.contains(&child1_pid));
3152 assert!(current_task.thread_group().read().children.contains(&child2_pid));
3153 let children_pids: HashSet<Pid> =
3154 current_task.thread_group().read().children().map(|tg| tg.leader.clone()).collect();
3155 assert!(!children_pids.contains(&child1_pid));
3156 assert!(children_pids.contains(&child2_pid));
3157
3158 child2.thread_group().kill(ExitStatus::Exit(0), None);
3159 std::mem::drop(child2);
3160
3161 assert!(child2_pid.get_thread_group().is_err());
3162 assert!(current_task.thread_group().read().children.is_empty());
3163 assert_eq!(current_task.thread_group().read().children().count(), 0);
3164 })
3165 .await;
3166 }
3167
3168 #[::fuchsia::test]
3169 async fn test_ptrace_unspawned_child_drop() {
3170 spawn_kernel_and_run(async |current_task| {
3171 let mut child = create_task(current_task.kernel(), "tracee");
3172 let child_pid = child.pid.clone();
3173 assert!(current_task.thread_group().read().children.contains(&child_pid));
3174
3175 assert_eq!(ptrace_traceme(&mut child), Ok(SUCCESS));
3176 child.thread_group().kill(ExitStatus::Exit(0), None);
3177 std::mem::drop(child);
3178
3179 assert!(!current_task.thread_group().read().children.contains(&child_pid));
3180 assert!(current_task.thread_group().read().zombie_children.is_empty());
3181
3182 let child_leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
3185 let child_pid = child_leader.pid.clone();
3186 let mut child_thread: AutoReleasableTask = child_leader
3187 .clone_task(
3188 (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64,
3189 None,
3190 UserRef::default(),
3191 UserRef::default(),
3192 UserRef::default(),
3193 )
3194 .expect("failed to create thread")
3195 .into();
3196 assert_ne!(child_thread.tid, child_pid);
3197 assert_eq!(child_thread.pid, child_pid);
3198 assert!(current_task.thread_group().read().children.contains(&child_pid));
3199
3200 child_leader.write().set_exit_status(ExitStatus::Exit(0));
3202 std::mem::drop(child_leader);
3203 assert!(current_task.thread_group().read().children.contains(&child_pid));
3204
3205 assert_eq!(ptrace_traceme(&mut child_thread), Ok(SUCCESS));
3206 child_thread.thread_group().kill(ExitStatus::Exit(0), None);
3207 std::mem::drop(child_thread);
3208
3209 assert!(!current_task.thread_group().read().children.contains(&child_pid));
3210 assert!(current_task.thread_group().read().zombie_children.is_empty());
3211 })
3212 .await;
3213 }
3214
3215 #[::fuchsia::test]
3216 async fn test_getppid_after_self_and_parent_exit() {
3217 spawn_kernel_and_run(async |current_task| {
3218 let task1 = current_task.clone_task_for_test(0, None);
3219 let task2 = task1.clone_task_for_test(0, None);
3220
3221 let tg1 = task1.thread_group().clone();
3223 let tg2 = task2.thread_group().clone();
3224
3225 assert_eq!(tg1.read().get_ppid(), current_task.tid.id);
3226 assert_eq!(tg2.read().get_ppid(), task1.tid.id);
3227
3228 tg2.kill(ExitStatus::Exit(0), None);
3230 std::mem::drop(task2);
3231
3232 tg1.kill(ExitStatus::Exit(0), None);
3234 std::mem::drop(task1);
3235 std::mem::drop(tg1);
3236
3237 let _ = tg2.read().get_ppid();
3240 })
3241 .await;
3242 }
3243
3244 #[::fuchsia::test]
3245 async fn test_thread_group_rcu_links_and_adapters() {
3246 spawn_kernel_and_run(async |current_task| {
3247 let scope = RcuReadScope::new();
3248 let tg = current_task.thread_group();
3249 let node_ptr = tg.pgrp_node.read(&scope);
3250 assert!(!node_ptr.is_null());
3251 let link_ptr = ThreadGroupLinkAdapter::to_link(node_ptr);
3252 assert_eq!(ThreadGroupLinkAdapter::from_link(link_ptr).as_ptr(), node_ptr.as_ptr());
3253
3254 let init_pid = current_task.pid.clone();
3255
3256 assert!(init_pid.is_process_group(&scope));
3257 assert!(init_pid.is_session(&scope));
3258 assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 1);
3259 assert_eq!(init_pid.sid_thread_groups(&scope).count(), 1);
3260
3261 let child = current_task.clone_task_for_test(0, None);
3262 assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 2);
3263 assert_eq!(init_pid.sid_thread_groups(&scope).count(), 2);
3264
3265 child
3268 .thread_group()
3269 .setpgid(¤t_task, &child, &child.pid)
3270 .expect("setpgid failed");
3271 assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 1);
3272 assert_eq!(init_pid.sid_thread_groups(&scope).count(), 2);
3273 assert!(child.pid.is_process_group(&scope));
3274 assert!(!child.pid.is_session(&scope));
3275 assert_eq!(child.pid.pgid_thread_groups(&scope).count(), 1);
3276
3277 child
3279 .thread_group()
3280 .setpgid(¤t_task, &child, &init_pid)
3281 .expect("setpgid back to init failed");
3282 assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 2);
3283 assert_eq!(init_pid.sid_thread_groups(&scope).count(), 2);
3284
3285 child.thread_group().setsid().expect("setsid failed");
3286 assert_eq!(init_pid.pgid_thread_groups(&scope).count(), 1);
3287 assert_eq!(init_pid.sid_thread_groups(&scope).count(), 1);
3288 assert!(child.pid.is_process_group(&scope));
3289 assert!(child.pid.is_session(&scope));
3290 assert_eq!(child.pid.pgid_thread_groups(&scope).count(), 1);
3291 assert_eq!(child.pid.sid_thread_groups(&scope).count(), 1);
3292
3293 let grandchild = child.clone_task_for_test(0, None);
3297 let child_pid = child.pid.clone();
3298 child.thread_group().kill(ExitStatus::Exit(0), None);
3299 std::mem::drop(child);
3300 {
3301 let mut pids = current_task.kernel().pids.lock();
3302 for zombie in current_task.thread_group().write().zombie_children.drain(..) {
3303 zombie.release(&mut pids);
3304 }
3305 }
3306 assert!(child_pid.get_process().is_none());
3307 assert!(current_task.kernel().pids.get(child_pid.id).is_ok());
3308
3309 grandchild
3310 .thread_group()
3311 .setpgid(&grandchild, &grandchild, &grandchild.pid)
3312 .expect("grandchild setpgid failed");
3313 assert!(!child_pid.is_process_group(&scope));
3314 assert!(child_pid.is_session(&scope));
3315 assert!(current_task.kernel().pids.get(child_pid.id).is_ok());
3316 assert!(grandchild.pid.is_process_group(&scope));
3317
3318 grandchild.thread_group().kill(ExitStatus::Exit(0), None);
3319 std::mem::drop(grandchild);
3320 {
3321 let mut pids = current_task.kernel().pids.lock();
3322 for zombie in current_task.thread_group().write().zombie_children.drain(..) {
3323 zombie.release(&mut pids);
3324 }
3325 }
3326
3327 assert!(!child_pid.is_process_group(&scope));
3328 assert!(!child_pid.is_session(&scope));
3329 assert!(current_task.kernel().pids.get(child_pid.id).is_err());
3330 })
3331 .await;
3332 }
3333
3334 #[::fuchsia::test]
3335 async fn test_orphaned_pgrp_signals_stopped_jobs() {
3336 spawn_kernel_and_run(async |init| {
3337 let parent = init.clone_task_for_test(0, Some(SIGCHLD));
3340 parent.thread_group().setsid().expect("setsid");
3341 let child = parent.clone_task_for_test(0, Some(SIGCHLD));
3342 child.thread_group().setpgid(&child, &child, &child.pid).expect("setpgid");
3343
3344 child.thread_group().set_stopped(StopState::GroupStopped, None, false);
3345 assert_eq!(child.thread_group().load_stopped(), StopState::GroupStopped);
3346 assert!(!child.read().has_signal_pending(SIGHUP));
3347
3348 parent.thread_group().kill(ExitStatus::Exit(0), None);
3349 std::mem::drop(parent);
3350
3351 assert!(child.read().has_signal_pending(SIGHUP));
3352 assert_ne!(child.thread_group().load_stopped(), StopState::GroupStopped);
3353
3354 child.thread_group().kill(ExitStatus::Exit(0), None);
3355 std::mem::drop(child);
3356
3357 let session_leader = init.clone_task_for_test(0, Some(SIGCHLD));
3360 session_leader.thread_group().setsid().expect("setsid");
3361 let middle = session_leader.clone_task_for_test(0, Some(SIGCHLD));
3362 let leaf = middle.clone_task_for_test(0, Some(SIGCHLD));
3363 leaf.thread_group().setpgid(&leaf, &leaf, &leaf.pid).expect("setpgid");
3364
3365 leaf.thread_group().set_stopped(StopState::GroupStopped, None, false);
3366 assert_eq!(leaf.thread_group().load_stopped(), StopState::GroupStopped);
3367 assert!(!leaf.read().has_signal_pending(SIGHUP));
3368
3369 middle.thread_group().setsid().expect("middle setsid");
3370
3371 assert!(leaf.read().has_signal_pending(SIGHUP));
3372 assert_ne!(leaf.thread_group().load_stopped(), StopState::GroupStopped);
3373 })
3374 .await;
3375 }
3376
3377 #[::fuchsia::test]
3378 async fn test_setpgid_multithreaded_task_migration() {
3379 spawn_kernel_and_run(async |current_task| {
3380 let kernel = current_task.kernel();
3381
3382 let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
3384 let child_pid = child.get_pid();
3385 let child_thread = child.clone_task_for_test(
3386 (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
3387 Some(SIGCHLD),
3388 );
3389
3390 let old_pgid_entry = kernel.pids.get(current_task.get_pid()).unwrap();
3391 let scope = RcuReadScope::new();
3392
3393 let initial_members: Vec<_> =
3395 old_pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3396 assert!(initial_members.contains(&child_pid));
3397
3398 assert_eq!(
3400 current_task.thread_group().setpgid(¤t_task, &child, &child.pid),
3401 Ok(())
3402 );
3403
3404 let old_members: Vec<_> =
3406 old_pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3407 assert!(!old_members.contains(&child_pid));
3408
3409 let new_pgid_entry = kernel.pids.get(child_pid).unwrap();
3411 let new_members: Vec<_> =
3412 new_pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3413 assert_eq!(new_members, vec![child_pid]);
3414 assert_eq!(child_thread.thread_group().pgid(), child.pid);
3415 })
3416 .await;
3417 }
3418
3419 #[::fuchsia::test]
3420 async fn test_setsid_multithreaded_task_migration() {
3421 spawn_kernel_and_run(async |current_task| {
3422 let kernel = current_task.kernel();
3423
3424 let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
3426 let child_pid = child.get_pid();
3427 let child_thread = child.clone_task_for_test(
3428 (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
3429 Some(SIGCHLD),
3430 );
3431
3432 let old_sid_entry = kernel.pids.get(current_task.get_pid()).unwrap();
3433 let scope = RcuReadScope::new();
3434
3435 let initial_sid_members: Vec<_> =
3437 old_sid_entry.sid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3438 assert!(initial_sid_members.contains(&child_pid));
3439
3440 assert_eq!(child.thread_group().setsid(), Ok(()));
3442
3443 let old_sid_members: Vec<_> =
3445 old_sid_entry.sid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3446 assert!(!old_sid_members.contains(&child_pid));
3447
3448 let new_pid_entry = kernel.pids.get(child_pid).unwrap();
3450 let new_sid_members: Vec<_> =
3451 new_pid_entry.sid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3452 assert_eq!(new_sid_members, vec![child_pid]);
3453
3454 let new_pgid_members: Vec<_> =
3455 new_pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3456 assert_eq!(new_pgid_members, vec![child_pid]);
3457 assert_eq!(child_thread.thread_group().read().session, child.pid);
3458 assert_eq!(child_thread.thread_group().pgid(), child.pid);
3459 })
3460 .await;
3461 }
3462
3463 #[::fuchsia::test]
3464 async fn test_orphaned_pgrp_detection_and_signaling() {
3465 spawn_kernel_and_run(async |current_task| {
3466 let kernel = current_task.kernel();
3467
3468 let parent_proc = current_task.clone_task_for_test(0, Some(SIGCHLD));
3470
3471 let child = parent_proc.clone_task_for_test(0, Some(SIGCHLD));
3473 assert_eq!(
3474 parent_proc.thread_group().setpgid(&parent_proc, &child, &child.pid),
3475 Ok(())
3476 );
3477
3478 let child_pgid_entry = kernel.pids.get(child.get_pid()).unwrap();
3479 let scope = RcuReadScope::new();
3480
3481 assert!(!child_pgid_entry.will_become_orphaned_pgrp(&scope, None));
3484
3485 assert!(
3487 child_pgid_entry
3488 .will_become_orphaned_pgrp(&scope, Some(parent_proc.thread_group()))
3489 );
3490
3491 assert!(!child_pgid_entry.has_stopped_jobs(&scope));
3493
3494 child.thread_group().set_stopped(StopState::GroupStopped, None, false);
3496 assert!(child_pgid_entry.has_stopped_jobs(&scope));
3497
3498 {
3500 let pids = kernel.pids.lock();
3501 child.thread_group().kill_orphaned_pgrp(
3502 &pids,
3503 Some(parent_proc.thread_group()),
3504 Some(parent_proc.thread_group()),
3505 );
3506 }
3507 assert!(child.thread_group().has_signal_queued(SIGHUP));
3508 assert!(child.thread_group().has_signal_queued(SIGCONT));
3509 })
3510 .await;
3511 }
3512
3513 #[::fuchsia::test]
3514 async fn test_setsid_rejected_on_process_group_leader() {
3515 spawn_kernel_and_run(async |current_task| {
3516 assert_eq!(current_task.thread_group().setsid(), error!(EPERM));
3518
3519 let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
3521
3522 assert_eq!(
3524 current_task.thread_group().setpgid(¤t_task, &child1, &child1.pid),
3525 Ok(())
3526 );
3527 assert_eq!(child1.thread_group().pgid(), child1.tid);
3528
3529 assert_eq!(child1.thread_group().setsid(), error!(EPERM));
3531
3532 let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
3534 assert_eq!(
3535 current_task.thread_group().setpgid(¤t_task, &child2, &child1.pid),
3536 Ok(())
3537 );
3538 assert_eq!(child2.thread_group().pgid(), child1.tid);
3540
3541 assert_eq!(child2.thread_group().setsid(), Ok(()));
3543 assert_eq!(child2.thread_group().pgid(), child2.tid);
3544 assert_eq!(child2.thread_group().read().session, child2.tid);
3545
3546 assert_eq!(child2.thread_group().setsid(), error!(EPERM));
3548 })
3549 .await;
3550 }
3551
3552 #[::fuchsia::test]
3553 async fn test_setpgid_many_threads_stress_and_teardown() {
3554 spawn_kernel_and_run(async |current_task| {
3555 let kernel = current_task.kernel();
3556
3557 let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
3559 let child_pid = child.get_pid();
3560 let mut thread_tasks = vec![];
3561 for _ in 0..7 {
3562 let thread = child.clone_task_for_test(
3563 (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
3564 Some(SIGCHLD),
3565 );
3566 thread_tasks.push(thread);
3567 }
3568 assert_eq!(child.thread_group().read().tasks_count(), 8);
3569
3570 let old_pgid_entry = kernel.pids.get(current_task.get_pid()).unwrap();
3571 let scope = RcuReadScope::new();
3572
3573 let initial_members: Vec<_> =
3575 old_pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3576 assert!(initial_members.contains(&child_pid));
3577
3578 assert_eq!(
3580 current_task.thread_group().setpgid(¤t_task, &child, &child.pid),
3581 Ok(())
3582 );
3583
3584 let old_members_after: Vec<_> =
3586 old_pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3587 assert!(!old_members_after.contains(&child_pid));
3588
3589 let new_pgid_entry = kernel.pids.get(child_pid).unwrap();
3590 let new_members: Vec<_> =
3591 new_pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3592 assert_eq!(new_members, vec![child_pid]);
3593
3594 let target_child = current_task.clone_task_for_test(0, Some(SIGCHLD));
3596 assert_eq!(
3597 current_task.thread_group().setpgid(
3598 ¤t_task,
3599 &target_child,
3600 &target_child.pid
3601 ),
3602 Ok(())
3603 );
3604
3605 assert_eq!(
3607 current_task.thread_group().setpgid(¤t_task, &child, &target_child.pid),
3608 Ok(())
3609 );
3610
3611 assert_eq!(new_pgid_entry.pgid_thread_groups(&scope).count(), 0);
3613
3614 let target_pgid_entry = kernel.pids.get(target_child.get_pid()).unwrap();
3616 let target_members: Vec<_> =
3617 target_pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3618 assert_eq!(target_members.len(), 2);
3619 assert!(target_members.contains(&child_pid));
3620 assert!(target_members.contains(&target_child.get_pid()));
3621
3622 for thread in thread_tasks {
3624 drop(thread);
3625 let updated_members: Vec<_> =
3626 target_pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3627 assert_eq!(updated_members.len(), 2);
3628 assert!(updated_members.contains(&child_pid));
3629 }
3630
3631 child.thread_group().kill(ExitStatus::Exit(0), None);
3633 drop(child);
3634 let remaining_members: Vec<_> =
3635 target_pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3636 assert_eq!(remaining_members, vec![target_child.get_pid()]);
3637 })
3638 .await;
3639 }
3640
3641 #[::fuchsia::test]
3642 async fn test_multi_process_orphan_detection_and_signaling() {
3643 spawn_kernel_and_run(async |current_task| {
3644 let kernel = current_task.kernel();
3645
3646 let leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
3648
3649 let c1 = leader.clone_task_for_test(0, Some(SIGCHLD));
3651 assert_eq!(leader.thread_group().setpgid(&leader, &c1, &c1.pid), Ok(()));
3652 let c1_pid = c1.get_pid();
3653 let c1_pgid_entry = kernel.pids.get(c1_pid).unwrap();
3654
3655 let c2 = leader.clone_task_for_test(0, Some(SIGCHLD));
3657 assert_eq!(leader.thread_group().setpgid(&leader, &c2, &c1.pid), Ok(()));
3658
3659 let c3 = c1.clone_task_for_test(0, Some(SIGCHLD));
3661 assert_eq!(c1.thread_group().setpgid(&c1, &c3, &c1.pid), Ok(()));
3662
3663 let scope = RcuReadScope::new();
3664
3665 assert!(!c1_pgid_entry.will_become_orphaned_pgrp(&scope, None));
3668
3669 c1.thread_group().set_stopped(StopState::GroupStopped, None, false);
3671 assert!(c1_pgid_entry.has_stopped_jobs(&scope));
3672
3673 assert!(c1_pgid_entry.will_become_orphaned_pgrp(&scope, Some(leader.thread_group())));
3676
3677 {
3679 let pids = kernel.pids.lock();
3680 c1.thread_group().kill_orphaned_pgrp(
3681 &pids,
3682 Some(leader.thread_group()),
3683 Some(leader.thread_group()),
3684 );
3685 }
3686
3687 assert!(c1.thread_group().has_signal_queued(SIGHUP));
3689 assert!(c1.thread_group().has_signal_queued(SIGCONT));
3690 assert!(c2.thread_group().has_signal_queued(SIGHUP));
3691 assert!(c2.thread_group().has_signal_queued(SIGCONT));
3692 assert!(c3.thread_group().has_signal_queued(SIGHUP));
3693 assert!(c3.thread_group().has_signal_queued(SIGCONT));
3694 })
3695 .await;
3696 }
3697
3698 #[::fuchsia::test]
3699 async fn test_orphan_prevented_by_sibling_external_parent() {
3700 spawn_kernel_and_run(async |current_task| {
3701 let kernel = current_task.kernel();
3702
3703 let leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
3705
3706 let p2 = leader.clone_task_for_test(0, Some(SIGCHLD));
3708 assert_eq!(leader.thread_group().setpgid(&leader, &p2, &p2.pid), Ok(()));
3709
3710 let c1 = leader.clone_task_for_test(0, Some(SIGCHLD));
3712 assert_eq!(leader.thread_group().setpgid(&leader, &c1, &c1.pid), Ok(()));
3713 let c1_pid = c1.get_pid();
3714 let c1_pgid_entry = kernel.pids.get(c1_pid).unwrap();
3715
3716 let c2 = p2.clone_task_for_test(0, Some(SIGCHLD));
3718 assert_eq!(p2.thread_group().setpgid(&p2, &c2, &c1.pid), Ok(()));
3719
3720 let scope = RcuReadScope::new();
3721
3722 c1.thread_group().set_stopped(StopState::GroupStopped, None, false);
3724
3725 assert!(!c1_pgid_entry.will_become_orphaned_pgrp(&scope, Some(leader.thread_group())));
3730
3731 {
3733 let pids = kernel.pids.lock();
3734 c1.thread_group().kill_orphaned_pgrp(
3735 &pids,
3736 Some(leader.thread_group()),
3737 Some(leader.thread_group()),
3738 );
3739 }
3740 assert!(!c1.thread_group().has_signal_queued(SIGHUP));
3741 assert!(!c2.thread_group().has_signal_queued(SIGHUP));
3742 })
3743 .await;
3744 }
3745
3746 #[::fuchsia::test]
3747 async fn test_setpgid_vacated_group_orphaned_and_signaled() {
3748 spawn_kernel_and_run(async |current_task| {
3749 let kernel = current_task.kernel();
3750
3751 let leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
3753
3754 let c1 = leader.clone_task_for_test(0, Some(SIGCHLD));
3756 assert_eq!(leader.thread_group().setpgid(&leader, &c1, &c1.pid), Ok(()));
3757 let c1_pid = c1.get_pid();
3758
3759 let c2 = leader.clone_task_for_test(0, Some(SIGCHLD));
3761 assert_eq!(leader.thread_group().setpgid(&leader, &c2, &c1.pid), Ok(()));
3762
3763 c1.thread_group().set_stopped(StopState::GroupStopped, None, false);
3765
3766 leader.thread_group().write().children.remove(&c1.tid);
3768 c1.thread_group().write().parent = None;
3769
3770 let scope = RcuReadScope::new();
3771 let c1_pgid_entry = kernel.pids.get(c1_pid).unwrap();
3772
3773 assert!(!c1_pgid_entry.will_become_orphaned_pgrp(&scope, None));
3776
3777 assert_eq!(leader.thread_group().setpgid(&leader, &c2, &c2.pid), Ok(()));
3779
3780 assert!(c1.thread_group().has_signal_queued(SIGHUP));
3784 assert!(c1.thread_group().has_signal_queued(SIGCONT));
3785 })
3786 .await;
3787 }
3788
3789 #[::fuchsia::test]
3790 async fn test_lifecycle_stress_concurrent_rcu_traversal() {
3791 spawn_kernel_and_run(async |current_task| {
3792 let kernel = current_task.kernel();
3793
3794 let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
3796 child.thread_group().setsid().expect("setsid");
3797 let child_pid = child.get_pid();
3798
3799 let pgid_entry = kernel.pids.get(child_pid).unwrap();
3800 let running = Arc::new(AtomicBool::new(true));
3801
3802 let r1_entry = pgid_entry.clone();
3804 let r1_running = Arc::clone(&running);
3805 let reader1 = std::thread::spawn(move || {
3806 while r1_running.load(Ordering::Relaxed) {
3807 let scope = RcuReadScope::new();
3808 let mut count = 0;
3809 for tg in r1_entry.pgid_thread_groups(&scope) {
3810 count += 1;
3811 let _pid = tg.leader.id;
3812 let _is_running = tg.read().is_running();
3813 assert!(count <= 20, "Cycle or unexpected count in pgid_thread_groups");
3814 }
3815 }
3816 });
3817
3818 let r2_entry = pgid_entry.clone();
3820 let r2_running = Arc::clone(&running);
3821 let reader2 = std::thread::spawn(move || {
3822 while r2_running.load(Ordering::Relaxed) {
3823 let scope = RcuReadScope::new();
3824 let mut count = 0;
3825 for tg in r2_entry.sid_thread_groups(&scope) {
3826 count += 1;
3827 let _pid = tg.leader.id;
3828 let _is_running = tg.read().is_running();
3829 assert!(count <= 20, "Cycle or unexpected count in sid_thread_groups");
3830 }
3831 }
3832 });
3833
3834 let r3_entry = pgid_entry.clone();
3836 let r3_running = Arc::clone(&running);
3837 let reader3 = std::thread::spawn(move || {
3838 while r3_running.load(Ordering::Relaxed) {
3839 let scope = RcuReadScope::new();
3840 let _orphaned = r3_entry.will_become_orphaned_pgrp(&scope, None);
3841 let _has_stopped = r3_entry.has_stopped_jobs(&scope);
3842 }
3843 });
3844
3845 let r4_entry = pgid_entry.clone();
3847 let r4_running = Arc::clone(&running);
3848 let signaler = std::thread::spawn(move || {
3849 while r4_running.load(Ordering::Relaxed) {
3850 r4_entry.send_signals_to_pgid(&[SIGCONT]);
3851 }
3852 });
3853
3854 for _ in 0..15 {
3856 let mut procs = Vec::new();
3857 for _ in 0..4 {
3858 let p = child.clone_task_for_test(0, Some(SIGCHLD));
3859 procs.push(p);
3860 }
3861
3862 for p in procs.drain(..2) {
3863 p.thread_group().kill(ExitStatus::Exit(0), None);
3864 drop(p);
3865 }
3866
3867 let p = child.clone_task_for_test(0, Some(SIGCHLD));
3868 procs.push(p);
3869
3870 for p in procs {
3871 p.thread_group().kill(ExitStatus::Exit(0), None);
3872 drop(p);
3873 }
3874
3875 let mut pids = kernel.pids.lock();
3876 for zombie in child.thread_group().write().zombie_children.drain(..) {
3877 zombie.release(&mut pids);
3878 }
3879 }
3880
3881 running.store(false, Ordering::Relaxed);
3882 reader1.join().expect("Reader 1 failed");
3883 reader2.join().expect("Reader 2 failed");
3884 reader3.join().expect("Reader 3 failed");
3885 signaler.join().expect("Signaler failed");
3886
3887 let scope = RcuReadScope::new();
3889 let remaining: Vec<_> =
3890 pgid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
3891 assert_eq!(remaining, vec![child_pid]);
3892 })
3893 .await;
3894 }
3895
3896 #[::fuchsia::test]
3897 async fn test_process_group_thread_groups_deduplicates_multithreaded() {
3898 spawn_kernel_and_run(async |current_task| {
3899 let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
3901 assert_eq!(
3902 current_task.thread_group().setpgid(¤t_task, &child1, &child1.pid),
3903 Ok(())
3904 );
3905 let pgrp = child1.thread_group().pgid();
3906
3907 let _c1_t1 = child1.clone_task_for_test(
3909 (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
3910 Some(SIGCHLD),
3911 );
3912 let _c1_t2 = child1.clone_task_for_test(
3913 (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
3914 Some(SIGCHLD),
3915 );
3916 let _c1_t3 = child1.clone_task_for_test(
3917 (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
3918 Some(SIGCHLD),
3919 );
3920
3921 let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
3923 assert_eq!(
3924 current_task.thread_group().setpgid(¤t_task, &child2, &child1.pid),
3925 Ok(())
3926 );
3927 let _c2_t1 = child2.clone_task_for_test(
3928 (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
3929 Some(SIGCHLD),
3930 );
3931 let _c2_t2 = child2.clone_task_for_test(
3932 (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
3933 Some(SIGCHLD),
3934 );
3935
3936 let scope = RcuReadScope::new();
3938 let groups: Vec<_> = pgrp.pgid_thread_groups(&scope).collect();
3939 assert_eq!(groups.len(), 2);
3940 assert!(groups.contains(child1.thread_group()));
3941 assert!(groups.contains(child2.thread_group()));
3942 })
3943 .await;
3944 }
3945
3946 #[::fuchsia::test]
3947 async fn test_thread_group_remove_detaches_last_task() {
3948 spawn_kernel_and_run(async |current_task| {
3949 let kernel = current_task.kernel();
3950
3951 let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
3953 assert_eq!(
3954 current_task.thread_group().setpgid(¤t_task, &child, &child.pid),
3955 Ok(())
3956 );
3957 let child_pid = child.get_pid();
3958 let pgid_entry = kernel.pids.get(child_pid).unwrap();
3959 let sid_entry = kernel.pids.get(current_task.get_pid()).unwrap();
3960
3961 let extra_thread = child.clone_task_for_test(
3963 (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
3964 Some(SIGCHLD),
3965 );
3966
3967 drop(extra_thread);
3969 let scope = RcuReadScope::new();
3970 assert!(pgid_entry.pgid_thread_groups(&scope).any(|tg| tg.leader.id == child_pid));
3971 assert!(sid_entry.sid_thread_groups(&scope).any(|tg| tg.leader.id == child_pid));
3972
3973 child.thread_group().kill(ExitStatus::Exit(0), None);
3975 drop(child);
3976 assert!(!pgid_entry.pgid_thread_groups(&scope).any(|tg| tg.leader.id == child_pid));
3977 assert!(!sid_entry.sid_thread_groups(&scope).any(|tg| tg.leader.id == child_pid));
3978 })
3979 .await;
3980 }
3981}