1use crate::device::terminal::{Terminal, TerminalController};
6use crate::mutable_state::{state_accessor, state_implementation};
7use crate::ptrace::{
8 AtomicStopState, PtraceAllowedPtracers, PtraceEvent, PtraceOptions, PtraceStatus, PtraceTracer,
9 StopState, ZombiePtracees, ptrace_detach,
10};
11use crate::security;
12use crate::signals::syscalls::WaitingOptions;
13use crate::signals::{
14 DeliveryAction, IntoSignalInfoOptions, QueuedSignals, SignalActions, SignalDetail, SignalInfo,
15 UncheckedSignalInfo, action_for_signal, send_standard_signal,
16};
17use crate::task::memory_attribution::MemoryAttributionLifecycleEvent;
18use crate::task::{
19 ControllingTerminal, CurrentTask, ExitStatus, Kernel, PidTable, ProcessGroup, Session,
20 SessionDisassociation, Task, TaskMutableState, TaskPersistentInfo, TypedWaitQueue,
21};
22use crate::time::{IntervalTimerHandle, TimerTable};
23use itertools::Itertools;
24use macro_rules_attribute::apply;
25use starnix_lifecycle::{AtomicCounter, DropNotifier};
26use starnix_logging::{log_debug, log_error, log_info, log_warn, track_stub};
27use starnix_sync::{
28 LockDepMutex, LockDepRwLock, RwLockWriteGuard, ThreadGroupLimits, ThreadGroupMutableStateLock,
29 ThreadGroupPendingSignalsLock, ThreadGroupPtraceesLock, allow_subclass, ordered_write_lock,
30};
31use starnix_task_command::TaskCommand;
32use starnix_types::ownership::{OwnedRef, Releasable};
33use starnix_types::stats::TaskTimeStats;
34use starnix_types::time::{itimerspec_from_itimerval, timeval_from_duration};
35use starnix_uapi::arc_key::WeakKey;
36use starnix_uapi::auth::{CAP_SYS_ADMIN, CAP_SYS_RESOURCE, Credentials};
37use starnix_uapi::errors::Errno;
38use starnix_uapi::personality::PersonalityFlags;
39use starnix_uapi::resource_limits::{Resource, ResourceLimits};
40use starnix_uapi::signals::{
41 SIGCHLD, SIGCONT, SIGHUP, SIGKILL, SIGTERM, SIGTTOU, SigSet, Signal, UncheckedSignal,
42};
43use starnix_uapi::user_address::UserAddress;
44use starnix_uapi::{
45 ITIMER_PROF, ITIMER_REAL, ITIMER_VIRTUAL, SI_TKILL, SI_USER, SIG_IGN, errno, error, itimerval,
46 pid_t, rlimit, tid_t, uid_t,
47};
48use std::collections::BTreeMap;
49use std::fmt;
50use std::sync::atomic::{AtomicBool, Ordering};
51use std::sync::{Arc, OnceLock, Weak};
52use zx::{Koid, Status};
53
54#[derive(Debug)]
55pub struct ZirconProcess {
56 process: zx::Process,
57 koid: Result<Koid, Status>,
58}
59
60impl ZirconProcess {
61 pub fn new(process: zx::Process) -> Self {
62 let koid = process.koid();
63 Self { process, koid }
64 }
65
66 pub fn koid(&self) -> Result<Koid, Status> {
67 self.koid
68 }
69}
70
71impl std::ops::Deref for ZirconProcess {
72 type Target = zx::Process;
73 fn deref(&self) -> &Self::Target {
74 &self.process
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
80pub struct ThreadGroupKey {
81 pid: pid_t,
82 thread_group: WeakKey<ThreadGroup>,
83}
84
85impl ThreadGroupKey {
86 pub fn pid(&self) -> pid_t {
91 self.pid
92 }
93}
94
95impl std::ops::Deref for ThreadGroupKey {
96 type Target = Weak<ThreadGroup>;
97 fn deref(&self) -> &Self::Target {
98 &self.thread_group.0
99 }
100}
101
102impl From<&ThreadGroup> for ThreadGroupKey {
103 fn from(tg: &ThreadGroup) -> Self {
104 Self { pid: tg.leader, thread_group: WeakKey::from(&tg.weak_self.upgrade().unwrap()) }
105 }
106}
107
108impl<T: AsRef<ThreadGroup>> From<T> for ThreadGroupKey {
109 fn from(tg: T) -> Self {
110 tg.as_ref().into()
111 }
112}
113
114#[repr(u64)]
116pub enum ThreadGroupLifecycleWaitValue {
117 ChildStatus,
119 Stopped,
121}
122
123impl Into<u64> for ThreadGroupLifecycleWaitValue {
124 fn into(self) -> u64 {
125 self as u64
126 }
127}
128
129#[derive(Clone, Debug)]
132pub struct DeferredZombiePTracer {
133 pub tracer_thread_group_key: ThreadGroupKey,
135 pub tracee_tid: tid_t,
137 pub tracee_pgid: pid_t,
139 pub tracee_thread_group_key: ThreadGroupKey,
141}
142
143impl DeferredZombiePTracer {
144 fn new(tracer: &ThreadGroup, tracee: &Task, tracee_pgid: pid_t) -> Self {
145 Self {
146 tracer_thread_group_key: tracer.into(),
147 tracee_tid: tracee.tid,
148 tracee_pgid,
149 tracee_thread_group_key: tracee.thread_group_key.clone(),
150 }
151 }
152}
153
154pub struct ThreadGroupMutableState {
156 pub parent: Option<ThreadGroupParent>,
161
162 pub exit_signal: Option<Signal>,
164
165 tasks: BTreeMap<tid_t, TaskContainer>,
172
173 pub children: BTreeMap<pid_t, Weak<ThreadGroup>>,
180
181 pub zombie_children: Vec<OwnedRef<ZombieProcess>>,
183
184 pub zombie_ptracees: ZombiePtracees,
186
187 pub deferred_zombie_ptracers: Vec<DeferredZombiePTracer>,
190
191 pub lifecycle_waiters: TypedWaitQueue<ThreadGroupLifecycleWaitValue>,
193
194 pub is_child_subreaper: bool,
197
198 pub process_group: Arc<ProcessGroup>,
200
201 pub did_exec: bool,
202
203 pub last_signal: Option<SignalInfo>,
207
208 run_state: ThreadGroupRunState,
212
213 pub children_time_stats: TaskTimeStats,
215
216 pub personality: PersonalityFlags,
218
219 pub allowed_ptracers: PtraceAllowedPtracers,
221
222 exit_notifier: Option<futures::channel::oneshot::Sender<()>>,
224
225 pub notifier: Option<std::sync::mpsc::Sender<MemoryAttributionLifecycleEvent>>,
227}
228
229pub struct ThreadGroup {
253 pub weak_self: Weak<ThreadGroup>,
256
257 pub kernel: Arc<Kernel>,
259
260 pub process: ZirconProcess,
269
270 pub root_vmar: zx::Vmar,
272
273 pub leader: pid_t,
277
278 pub leader_task: OnceLock<Weak<Task>>,
285
286 pub signal_actions: Arc<SignalActions>,
288
289 pub timers: TimerTable,
291
292 pub drop_notifier: DropNotifier,
294
295 stop_state: AtomicStopState,
299
300 mutable_state: LockDepRwLock<ThreadGroupMutableState, ThreadGroupMutableStateLock>,
302
303 pub limits: LockDepMutex<ResourceLimits, ThreadGroupLimits>,
307
308 pub next_seccomp_filter_id: AtomicCounter<u64>,
313
314 pub ptracees: LockDepMutex<BTreeMap<tid_t, TaskContainer>, ThreadGroupPtraceesLock>,
316
317 pub pending_signals: LockDepMutex<QueuedSignals, ThreadGroupPendingSignalsLock>,
319
320 pub has_pending_signals: AtomicBool,
323
324 pub start_time: zx::MonotonicInstant,
326
327 log_syscalls_as_info: AtomicBool,
329}
330
331impl fmt::Debug for ThreadGroup {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 write!(
334 f,
335 "{}({})",
336 self.process.get_name().unwrap_or(zx::Name::new_lossy("<unknown>")),
337 self.leader
338 )
339 }
340}
341
342impl ThreadGroup {
343 pub fn sync_syscall_log_level(&self) {
344 let command = self.read().leader_command();
345 let filters = self.kernel.syscall_log_filters.lock();
346 let should_log = filters.iter().any(|f| f.matches(&command));
347 let prev_should_log = self.log_syscalls_as_info.swap(should_log, Ordering::Relaxed);
348 let change_str = match (should_log, prev_should_log) {
349 (true, false) => Some("Enabled"),
350 (false, true) => Some("Disabled"),
351 _ => None,
352 };
353 if let Some(change_str) = change_str {
354 log_info!(
355 "{change_str} info syscall logs for thread group {} (command: {command})",
356 self.leader
357 );
358 }
359 }
360
361 #[inline]
362 pub fn syscall_log_level(&self) -> starnix_logging::Level {
363 if self.log_syscalls_as_info.load(Ordering::Relaxed) {
364 starnix_logging::Level::Info
365 } else {
366 starnix_logging::Level::Trace
367 }
368 }
369}
370
371impl PartialEq for ThreadGroup {
372 fn eq(&self, other: &Self) -> bool {
373 self.leader == other.leader
374 }
375}
376
377impl Drop for ThreadGroup {
378 fn drop(&mut self) {
379 let state = self.mutable_state.get_mut();
380 assert!(state.tasks.is_empty());
381 assert!(state.children.is_empty());
382 assert!(state.zombie_children.is_empty());
383 assert!(state.zombie_ptracees.is_empty());
384 #[cfg(any(test, debug_assertions))]
385 assert!(
386 state
387 .parent
388 .as_ref()
389 .and_then(|p| p.0.upgrade().as_ref().map(|p| p
390 .read()
391 .children
392 .get(&self.leader)
393 .is_none()))
394 .unwrap_or(true)
395 );
396 }
397}
398
399pub struct ThreadGroupParent(Weak<ThreadGroup>);
402
403impl ThreadGroupParent {
404 pub fn new(t: Weak<ThreadGroup>) -> Self {
405 debug_assert!(t.upgrade().is_some());
406 Self(t)
407 }
408
409 pub fn upgrade(&self) -> Arc<ThreadGroup> {
410 self.0.upgrade().expect("ThreadGroupParent references must always be valid")
411 }
412}
413
414impl Clone for ThreadGroupParent {
415 fn clone(&self) -> Self {
416 Self(self.0.clone())
417 }
418}
419
420#[derive(Debug, Clone)]
423pub enum ProcessSelector {
424 Any,
426 Pid(pid_t),
428 Pgid(pid_t),
430 Process(ThreadGroupKey),
432}
433
434impl ProcessSelector {
435 pub fn match_tid(&self, tid: tid_t, pid_table: &PidTable) -> bool {
436 match *self {
437 ProcessSelector::Pid(p) => {
438 if p == tid {
439 true
440 } else {
441 if let Ok(task_ref) = pid_table.get_task(tid) {
442 task_ref.get_pid() == p
443 } else {
444 false
445 }
446 }
447 }
448 ProcessSelector::Any => true,
449 ProcessSelector::Pgid(pgid) => {
450 if let Ok(task_ref) = pid_table.get_task(tid) {
451 pid_table.get_process_group(pgid).as_ref()
452 == Some(&task_ref.thread_group().read().process_group)
453 } else {
454 false
455 }
456 }
457 ProcessSelector::Process(ref key) => {
458 if let Some(tg) = key.upgrade() {
459 tg.read().tasks.contains_key(&tid)
460 } else {
461 false
462 }
463 }
464 }
465 }
466}
467
468#[derive(Clone, Debug, PartialEq, Eq)]
469pub struct ProcessExitInfo {
470 pub status: ExitStatus,
471 pub exit_signal: Option<Signal>,
472}
473
474#[derive(Clone, Debug, Default, PartialEq, Eq)]
475enum ThreadGroupRunState {
476 #[default]
477 Running,
478 Exiting(ExitStatus),
479 Exited(ExitStatus),
480}
481
482#[derive(Clone, Debug, PartialEq, Eq)]
483pub struct WaitResult {
484 pub pid: pid_t,
485 pub uid: uid_t,
486
487 pub exit_info: ProcessExitInfo,
488
489 pub time_stats: TaskTimeStats,
491}
492
493impl WaitResult {
494 pub fn as_signal_info(&self) -> SignalInfo {
496 SignalInfo::with_detail(
497 SIGCHLD,
498 self.exit_info.status.signal_info_code(),
499 SignalDetail::SIGCHLD {
500 pid: self.pid,
501 uid: self.uid,
502 status: self.exit_info.status.signal_info_status(),
503 },
504 )
505 }
506}
507
508#[derive(Debug)]
509pub struct ZombieProcess {
510 pub thread_group_key: ThreadGroupKey,
511 pub pgid: pid_t,
512 pub uid: uid_t,
513
514 pub exit_info: ProcessExitInfo,
515
516 pub time_stats: TaskTimeStats,
518
519 pub is_canonical: bool,
522}
523
524impl PartialEq for ZombieProcess {
525 fn eq(&self, other: &Self) -> bool {
526 self.thread_group_key == other.thread_group_key
528 && self.pgid == other.pgid
529 && self.uid == other.uid
530 && self.is_canonical == other.is_canonical
531 }
532}
533
534impl Eq for ZombieProcess {}
535
536impl PartialOrd for ZombieProcess {
537 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
538 Some(self.cmp(other))
539 }
540}
541
542impl Ord for ZombieProcess {
543 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
544 self.thread_group_key.cmp(&other.thread_group_key)
545 }
546}
547
548impl ZombieProcess {
549 pub fn new(
550 thread_group: ThreadGroupStateRef<'_>,
551 credentials: &Credentials,
552 exit_info: ProcessExitInfo,
553 ) -> OwnedRef<Self> {
554 let time_stats = thread_group.base.time_stats() + thread_group.children_time_stats;
555 OwnedRef::new(ZombieProcess {
556 thread_group_key: thread_group.base.into(),
557 pgid: thread_group.process_group.leader,
558 uid: credentials.uid,
559 exit_info,
560 time_stats,
561 is_canonical: true,
562 })
563 }
564
565 pub fn pid(&self) -> pid_t {
566 self.thread_group_key.pid()
567 }
568
569 pub fn to_wait_result(&self) -> WaitResult {
570 WaitResult {
571 pid: self.pid(),
572 uid: self.uid,
573 exit_info: self.exit_info.clone(),
574 time_stats: self.time_stats,
575 }
576 }
577
578 pub fn as_artificial(&self) -> Self {
579 ZombieProcess {
580 thread_group_key: self.thread_group_key.clone(),
581 pgid: self.pgid,
582 uid: self.uid,
583 exit_info: self.exit_info.clone(),
584 time_stats: self.time_stats,
585 is_canonical: false,
586 }
587 }
588
589 pub fn matches_selector(&self, selector: &ProcessSelector) -> bool {
590 match *selector {
591 ProcessSelector::Any => true,
592 ProcessSelector::Pid(pid) => self.pid() == pid,
593 ProcessSelector::Pgid(pgid) => self.pgid == pgid,
594 ProcessSelector::Process(ref key) => self.thread_group_key == *key,
595 }
596 }
597
598 pub fn matches_selector_and_waiting_option(
599 &self,
600 selector: &ProcessSelector,
601 options: &WaitingOptions,
602 ) -> bool {
603 if !self.matches_selector(selector) {
604 return false;
605 }
606
607 if options.wait_for_all {
608 true
609 } else {
610 options.wait_for_clone == (self.exit_info.exit_signal != Some(SIGCHLD))
613 }
614 }
615}
616
617impl Releasable for ZombieProcess {
618 type Context<'a> = &'a mut PidTable;
619
620 fn release<'a>(self, pids: &'a mut PidTable) {
621 if self.is_canonical {
622 pids.remove_zombie(self.pid());
623 }
624 }
625}
626
627#[must_use = "Notifications must be explicitly delivered or discarded"]
645pub struct ZombieNotification {
646 pub recipient: Weak<ThreadGroup>,
648
649 pub zombie: OwnedRef<ZombieProcess>,
651}
652
653impl ZombieNotification {
654 pub fn new(recipient: Weak<ThreadGroup>, zombie: OwnedRef<ZombieProcess>) -> Self {
655 Self { recipient, zombie }
656 }
657
658 pub fn deliver(self, pids: &mut PidTable) {
664 if let Some(parent) = self.recipient.upgrade() {
665 parent.do_zombie_notifications(self.zombie);
666 } else {
667 log_warn!("Zombie {} reaped silently", self.zombie.pid());
668 self.zombie.release(pids);
669 }
670 }
671
672 pub fn discard(self, pids: &mut PidTable) {
676 self.zombie.release(pids);
677 }
678}
679
680impl ThreadGroup {
681 pub fn new(
683 kernel: Arc<Kernel>,
684 process: zx::Process,
685 root_vmar: zx::Vmar,
686 parent: Option<ThreadGroupWriteGuard<'_>>,
687 leader: pid_t,
688 exit_signal: Option<Signal>,
689 process_group: Arc<ProcessGroup>,
690 signal_actions: Arc<SignalActions>,
691 ) -> Arc<ThreadGroup> {
692 debug_assert!(!process.is_invalid());
693 debug_assert!(!root_vmar.is_invalid());
694 Self::new_internal(
695 kernel,
696 process,
697 root_vmar,
698 parent,
699 leader,
700 exit_signal,
701 process_group,
702 signal_actions,
703 )
704 }
705
706 pub fn for_system(
708 kernel: Arc<Kernel>,
709 leader: pid_t,
710 process_group: Arc<ProcessGroup>,
711 ) -> Arc<ThreadGroup> {
712 Self::new_internal(
713 kernel,
714 zx::Process::invalid(),
715 zx::Vmar::invalid(),
716 None,
717 leader,
718 Some(SIGCHLD),
719 process_group,
720 SignalActions::default(),
721 )
722 }
723
724 pub fn for_test(
732 kernel: Arc<Kernel>,
733 process: zx::Process,
734 parent: ThreadGroupWriteGuard<'_>,
735 leader: pid_t,
736 process_group: Arc<ProcessGroup>,
737 ) -> Arc<ThreadGroup> {
738 Self::new_internal(
739 kernel,
740 process,
741 zx::Vmar::invalid(),
742 Some(parent),
743 leader,
744 Some(SIGCHLD),
745 process_group,
746 SignalActions::default(),
747 )
748 }
749
750 fn new_internal(
751 kernel: Arc<Kernel>,
752 process: zx::Process,
753 root_vmar: zx::Vmar,
754 parent: Option<ThreadGroupWriteGuard<'_>>,
755 leader: pid_t,
756 exit_signal: Option<Signal>,
757 process_group: Arc<ProcessGroup>,
758 signal_actions: Arc<SignalActions>,
759 ) -> Arc<ThreadGroup> {
760 Arc::new_cyclic(|weak_self| {
761 let process = ZirconProcess::new(process);
762 let mut thread_group = ThreadGroup {
763 weak_self: weak_self.clone(),
764 kernel,
765 process,
766 root_vmar,
767 leader,
768 leader_task: OnceLock::new(),
769 signal_actions,
770 timers: Default::default(),
771 drop_notifier: Default::default(),
772 limits: LockDepMutex::new(
775 parent
776 .as_ref()
777 .map(|p| p.base.limits.lock().clone())
778 .unwrap_or(Default::default()),
779 ),
780 next_seccomp_filter_id: Default::default(),
781 ptracees: Default::default(),
782 stop_state: AtomicStopState::new(StopState::Awake),
783 pending_signals: Default::default(),
784 has_pending_signals: Default::default(),
785 start_time: zx::MonotonicInstant::get(),
786 mutable_state: ThreadGroupMutableState {
787 parent: parent
788 .as_ref()
789 .map(|p| ThreadGroupParent::new(p.base.weak_self.clone())),
790 exit_signal,
791 tasks: BTreeMap::new(),
792 children: BTreeMap::new(),
793 zombie_children: vec![],
794 zombie_ptracees: ZombiePtracees::new(),
795 deferred_zombie_ptracers: vec![],
796 lifecycle_waiters: TypedWaitQueue::<ThreadGroupLifecycleWaitValue>::default(),
797 is_child_subreaper: false,
798 process_group: Arc::clone(&process_group),
799 did_exec: false,
800 last_signal: None,
801 run_state: Default::default(),
802 children_time_stats: Default::default(),
803 personality: parent
804 .as_ref()
805 .map(|p| p.personality)
806 .unwrap_or(Default::default()),
807 allowed_ptracers: PtraceAllowedPtracers::None,
808 exit_notifier: None,
809 notifier: None,
810 }
811 .into(),
812 log_syscalls_as_info: AtomicBool::new(false),
813 };
814
815 if let Some(mut parent) = parent {
816 thread_group.next_seccomp_filter_id.reset(parent.base.next_seccomp_filter_id.get());
817 parent.children.insert(leader, weak_self.clone());
818 process_group.insert(&thread_group);
819 };
820 thread_group
821 })
822 }
823
824 state_accessor!(ThreadGroup, mutable_state);
825
826 pub fn load_stopped(&self) -> StopState {
827 self.stop_state.load(Ordering::Relaxed)
828 }
829
830 pub fn kill(&self, exit_status: ExitStatus, mut current_task: Option<&mut CurrentTask>) {
839 if let Some(ref mut current_task) = current_task {
840 current_task
841 .ptrace_event(PtraceOptions::TRACEEXIT, exit_status.signal_info_status() as u64);
842 }
843 let mut pids = self.kernel.pids.write();
844 let mut state = self.write();
845 if !state.is_running() {
846 return;
847 }
848
849 state.run_state = ThreadGroupRunState::Exiting(exit_status.clone());
850
851 let zombie_notifications = state.zombie_ptracees.detach_all(&mut pids);
853
854 let tasks = state.tasks();
857 drop(state);
858
859 for notification in zombie_notifications {
860 notification.deliver(&mut pids);
861 }
862 self.detach_ptracees(&mut pids);
863
864 for task in tasks {
865 task.write().set_exit_status(exit_status.clone());
866 send_standard_signal(&task, SignalInfo::kernel(SIGKILL));
867 }
868 }
869
870 pub fn add(&self, task: Arc<Task>) -> Result<(), Errno> {
871 let mut state = self.write();
872 if !state.is_running() {
873 if state.tasks_count() == 0 {
874 log_warn!(
875 "Task {} with leader {} not running while adding its first task, \
876 not sending creation notification",
877 task.tid,
878 self.leader
879 );
880 }
881 return error!(EINVAL);
882 }
883 if task.tid == self.leader {
884 let _ = self.leader_task.set(Arc::downgrade(&task));
885 }
886 state.tasks.insert(task.tid, (&task).into());
887
888 Ok(())
889 }
890
891 pub fn remove(&self, mut pids: RwLockWriteGuard<'_, PidTable>, task: &Arc<Task>) {
896 task.set_ptrace_zombie(&mut pids);
897 pids.remove_task(task.tid);
898
899 let mut state = self.write();
900
901 let persistent_info: TaskPersistentInfo =
902 if let Some(container) = state.tasks.remove(&task.tid) {
903 container.into()
904 } else {
905 debug_assert!(!state.is_running());
908 return;
909 };
910
911 if state.tasks.is_empty() {
912 let exit_status = if let ThreadGroupRunState::Exiting(exit_status) = &state.run_state {
913 exit_status.clone()
914 } else {
915 let exit_status = task.exit_status().unwrap_or_else(|| {
916 log_error!("Exiting without an exit code.");
917 ExitStatus::Exit(u8::MAX)
918 });
919 state.set_exiting(exit_status.clone());
920 exit_status
921 };
922
923 let zombie_notifications = state.zombie_ptracees.detach_all(&mut pids);
925
926 let exit_info =
928 ProcessExitInfo { status: exit_status, exit_signal: state.exit_signal.clone() };
929 let zombie =
930 ZombieProcess::new(state.as_ref(), &persistent_info.real_creds(), exit_info);
931 pids.kill_process(self.leader, OwnedRef::downgrade(&zombie));
932
933 let session = state.leave_process_group(&pids);
934
935 std::mem::drop(state);
945
946 session.disassociate_controlling_terminal();
949
950 for notification in zombie_notifications {
951 notification.deliver(&mut pids);
952 }
953
954 self.kernel.cgroups.lock_cgroup2_pid_table().remove_process(self.into());
958
959 self.detach_ptracees(&mut pids);
960
961 let parent = self.read().parent.clone();
964 let reaper = self.find_reaper();
965
966 {
967 if let Some(reaper) = reaper {
969 let reaper = reaper.upgrade();
970 {
971 let mut reaper_state = reaper.write();
972 let _token = allow_subclass();
976 let mut state = self.write();
977 for (_pid, weak_child) in std::mem::take(&mut state.children) {
978 if let Some(child) = weak_child.upgrade() {
979 let _token = allow_subclass();
984 let mut child_state = child.write();
985
986 child_state.exit_signal = Some(SIGCHLD);
987 child_state.parent =
988 Some(ThreadGroupParent::new(Arc::downgrade(&reaper)));
989 reaper_state.children.insert(child.leader, weak_child.clone());
990 }
991 }
992 reaper_state.zombie_children.append(&mut state.zombie_children);
993 }
994 ZombiePtracees::reparent(self, &reaper);
995 } else {
996 let mut state = self.write();
998 for zombie in state.zombie_children.drain(..) {
999 zombie.release(&mut pids);
1000 }
1001 }
1002 }
1003
1004 self.write().parent = None;
1006
1007 #[cfg(any(test, debug_assertions))]
1008 {
1009 let state = self.read();
1010 assert!(state.zombie_children.is_empty());
1011 assert!(state.zombie_ptracees.is_empty());
1012 }
1013
1014 if let Some(ref parent) = parent {
1015 let parent = parent.upgrade();
1016
1017 let tracer_tg = task
1018 .read()
1019 .ptrace
1020 .as_ref()
1021 .and_then(|ptrace| ptrace.core_state.thread_group.upgrade());
1022
1023 let maybe_zombie = match tracer_tg {
1024 Some(tracer_tg) => {
1025 tracer_tg.maybe_notify_tracer(task, &mut pids, &parent, zombie)
1026 }
1027 None => Some(zombie),
1028 };
1029
1030 if let Some(zombie) = maybe_zombie {
1031 parent.do_zombie_notifications(zombie);
1032 }
1033 } else {
1034 zombie.release(&mut pids);
1035 }
1036
1037 if let Some(parent) = parent {
1043 let parent = parent.upgrade();
1044 parent.check_orphans(&pids);
1045 }
1046
1047 self.write().set_exited();
1048 }
1049 }
1050
1051 fn detach_ptracees(&self, pids: &mut PidTable) {
1053 let tracee_tids = self.ptracees.lock().keys().cloned().collect_vec();
1054 for tracee_tid in tracee_tids {
1055 let Ok(tracee) = pids.get_task(tracee_tid) else {
1056 continue;
1057 };
1058
1059 let mut should_send_sigkill = false;
1060 if let Some(ptrace) = &tracee.read().ptrace {
1061 should_send_sigkill = ptrace.has_option(PtraceOptions::EXITKILL);
1062 }
1063 if should_send_sigkill {
1064 send_standard_signal(tracee.as_ref(), SignalInfo::kernel(SIGKILL));
1065 }
1066
1067 let _ = ptrace_detach(
1068 pids,
1069 PtraceTracer::Exiting(self),
1070 tracee.as_ref(),
1071 &UserAddress::NULL,
1072 );
1073 }
1074 }
1075
1076 pub fn do_zombie_notifications(&self, zombie: OwnedRef<ZombieProcess>) {
1077 let mut state = self.write();
1078
1079 state.children.remove(&zombie.pid());
1080 state
1081 .deferred_zombie_ptracers
1082 .retain(|dzp| dzp.tracee_thread_group_key != zombie.thread_group_key);
1083
1084 let exit_signal = zombie.exit_info.exit_signal;
1085 let mut signal_info = zombie.to_wait_result().as_signal_info();
1086
1087 state.zombie_children.push(zombie);
1088 state.lifecycle_waiters.notify_value(ThreadGroupLifecycleWaitValue::ChildStatus);
1089
1090 if let Some(exit_signal) = exit_signal {
1092 signal_info.signal = exit_signal;
1093 state.send_signal(signal_info);
1094 }
1095 }
1096
1097 fn maybe_notify_tracer(
1101 &self,
1102 tracee: &Task,
1103 mut pids: &mut PidTable,
1104 parent: &ThreadGroup,
1105 zombie: OwnedRef<ZombieProcess>,
1106 ) -> Option<OwnedRef<ZombieProcess>> {
1107 let mut state = self.write();
1108 if state.zombie_ptracees.has_tracee(tracee.tid) {
1109 if self == parent {
1110 if let Some(zombie_notification) = state.zombie_ptracees.detach(pids, tracee.tid) {
1115 zombie_notification.discard(pids);
1116 }
1117 return Some(zombie);
1118 } else {
1119 if !state.is_running() {
1122 return Some(zombie);
1124 }
1125
1126 drop(state);
1129 {
1130 let tracee_pgid = tracee.thread_group().read().process_group.leader;
1132 let mut parent_state = parent.write();
1133 parent_state.deferred_zombie_ptracers.push(DeferredZombiePTracer::new(
1134 self,
1135 tracee,
1136 tracee_pgid,
1137 ));
1138 parent_state.children.remove(&tracee.get_pid());
1139 }
1140
1141 let mut state = self.write();
1147 state.zombie_ptracees.set_parent_of(tracee.tid, Some(zombie), parent);
1148 tracee.write().notify_ptracers();
1149 return None;
1150 }
1151 } else if self == parent {
1152 parent.write().children.remove(&tracee.tid);
1155 zombie.release(&mut pids);
1156 return None;
1157 }
1158 Some(zombie)
1161 }
1162
1163 fn find_reaper(&self) -> Option<ThreadGroupParent> {
1165 let mut weak_parent = self.read().parent.clone()?;
1166 loop {
1167 weak_parent = {
1168 let parent = weak_parent.upgrade();
1169 let parent_state = parent.read();
1170 if parent_state.is_child_subreaper {
1171 break;
1172 }
1173 match parent_state.parent {
1174 Some(ref next_parent) => next_parent.clone(),
1175 None => break,
1176 }
1177 };
1178 }
1179 Some(weak_parent)
1180 }
1181
1182 pub fn setsid(&self) -> Result<(), Errno> {
1183 let pids = self.kernel.pids.read();
1184 if pids.get_process_group(self.leader).is_some() {
1185 return error!(EPERM);
1186 }
1187 let process_group = ProcessGroup::new(self.leader, None);
1188 pids.add_process_group(process_group.clone());
1189 let session = self.write().set_process_group(process_group, &pids);
1190 session.disassociate_controlling_terminal();
1191 self.check_orphans(&pids);
1192
1193 Ok(())
1194 }
1195
1196 pub fn setpgid(
1197 &self,
1198 current_task: &CurrentTask,
1199 target: &Task,
1200 pgid: pid_t,
1201 ) -> Result<(), Errno> {
1202 let pids = self.kernel.pids.read();
1203
1204 {
1205 let current_process_group = Arc::clone(&self.read().process_group);
1206
1207 let mut target_thread_group = target.thread_group().write();
1209 let is_target_current_process_child =
1210 target_thread_group.parent.as_ref().map(|tg| tg.upgrade().leader)
1211 == Some(self.leader);
1212 if target_thread_group.leader() != self.leader && !is_target_current_process_child {
1213 return error!(ESRCH);
1214 }
1215
1216 if is_target_current_process_child && target_thread_group.did_exec {
1219 return error!(EACCES);
1220 }
1221
1222 let new_process_group;
1223 {
1224 let target_process_group = &target_thread_group.process_group;
1225
1226 if target_thread_group.leader() == target_process_group.session.leader
1228 || current_process_group.session != target_process_group.session
1229 {
1230 return error!(EPERM);
1231 }
1232
1233 let target_pgid = if pgid == 0 { target_thread_group.leader() } else { pgid };
1234 if target_pgid < 0 {
1235 return error!(EINVAL);
1236 }
1237
1238 if target_pgid == target_process_group.leader {
1239 return Ok(());
1240 }
1241
1242 if target_pgid != target_thread_group.leader() {
1245 new_process_group =
1246 pids.get_process_group(target_pgid).ok_or_else(|| errno!(EPERM))?;
1247 if new_process_group.session != target_process_group.session {
1248 return error!(EPERM);
1249 }
1250 security::check_setpgid_access(current_task, target)?;
1251 } else {
1252 security::check_setpgid_access(current_task, target)?;
1253 new_process_group =
1255 ProcessGroup::new(target_pgid, Some(target_process_group.session.clone()));
1256 pids.add_process_group(new_process_group.clone());
1257 }
1258 }
1259
1260 let session = target_thread_group.set_process_group(new_process_group, &pids);
1261 std::mem::drop(target_thread_group);
1262 session.disassociate_controlling_terminal();
1265 }
1266
1267 target.thread_group().check_orphans(&pids);
1268
1269 Ok(())
1270 }
1271
1272 fn itimer_real(&self) -> IntervalTimerHandle {
1273 self.timers.itimer_real()
1274 }
1275
1276 pub fn set_itimer(
1277 &self,
1278 current_task: &CurrentTask,
1279 which: u32,
1280 value: itimerval,
1281 ) -> Result<itimerval, Errno> {
1282 if which == ITIMER_PROF || which == ITIMER_VIRTUAL {
1283 if value.it_value.tv_sec == 0 && value.it_value.tv_usec == 0 {
1287 return Ok(itimerval::default());
1288 }
1289 track_stub!(TODO("https://fxbug.dev/322874521"), "Unsupported itimer type", which);
1290 return error!(ENOTSUP);
1291 }
1292
1293 if which != ITIMER_REAL {
1294 return error!(EINVAL);
1295 }
1296 let itimer_real = self.itimer_real();
1297 let prev_remaining = itimer_real.time_remaining();
1298 if value.it_value.tv_sec != 0 || value.it_value.tv_usec != 0 {
1299 itimer_real.arm(current_task, itimerspec_from_itimerval(value), false)?;
1300 } else {
1301 itimer_real.disarm(current_task)?;
1302 }
1303 Ok(itimerval {
1304 it_value: timeval_from_duration(prev_remaining.remainder),
1305 it_interval: timeval_from_duration(prev_remaining.interval),
1306 })
1307 }
1308
1309 pub fn get_itimer(&self, which: u32) -> Result<itimerval, Errno> {
1310 if which == ITIMER_PROF || which == ITIMER_VIRTUAL {
1311 return Ok(itimerval::default());
1313 }
1314 if which != ITIMER_REAL {
1315 return error!(EINVAL);
1316 }
1317 let remaining = self.itimer_real().time_remaining();
1318 Ok(itimerval {
1319 it_value: timeval_from_duration(remaining.remainder),
1320 it_interval: timeval_from_duration(remaining.interval),
1321 })
1322 }
1323
1324 fn check_stopped_state(
1327 &self,
1328 new_stopped: StopState,
1329 finalize_only: bool,
1330 ) -> Option<StopState> {
1331 let stopped = self.load_stopped();
1332 if finalize_only && !stopped.is_stopping_or_stopped() {
1333 return Some(stopped);
1334 }
1335
1336 if stopped.is_illegal_transition(new_stopped) {
1337 return Some(stopped);
1338 }
1339
1340 return None;
1341 }
1342
1343 pub fn set_stopped(
1350 &self,
1351 new_stopped: StopState,
1352 siginfo: Option<SignalInfo>,
1353 finalize_only: bool,
1354 ) -> StopState {
1355 if let Some(stopped) = self.check_stopped_state(new_stopped, finalize_only) {
1357 return stopped;
1358 }
1359
1360 self.write().set_stopped(new_stopped, siginfo, finalize_only)
1361 }
1362
1363 fn check_terminal_controller(
1366 session: &Arc<Session>,
1367 terminal_controller: &Option<TerminalController>,
1368 ) -> Result<(), Errno> {
1369 if let Some(terminal_controller) = terminal_controller {
1370 if let Some(terminal_session) = terminal_controller.session.upgrade() {
1371 if Arc::ptr_eq(session, &terminal_session) {
1372 return Ok(());
1373 }
1374 }
1375 }
1376 error!(ENOTTY)
1377 }
1378
1379 pub fn get_foreground_process_group(&self, terminal: &Terminal) -> Result<pid_t, Errno> {
1380 let state = self.read();
1381 let process_group = &state.process_group;
1382 let terminal_state = terminal.read();
1383
1384 Self::check_terminal_controller(&process_group.session, &terminal_state.controller)?;
1387 let pid = process_group.session.read().get_foreground_process_group_leader();
1388 Ok(pid)
1389 }
1390
1391 pub fn set_foreground_process_group(
1392 &self,
1393 current_task: &CurrentTask,
1394 terminal: &Terminal,
1395 pgid: pid_t,
1396 ) -> Result<(), Errno> {
1397 let process_group;
1398 let send_ttou;
1399 {
1400 let pids = self.kernel.pids.read();
1402 let state = self.read();
1403 process_group = Arc::clone(&state.process_group);
1404 let terminal_state = terminal.read();
1405 Self::check_terminal_controller(&process_group.session, &terminal_state.controller)?;
1406
1407 if pgid < 0 {
1409 return error!(EINVAL);
1410 }
1411
1412 let new_process_group = pids.get_process_group(pgid).ok_or_else(|| errno!(ESRCH))?;
1413 if new_process_group.session != process_group.session {
1414 return error!(EPERM);
1415 }
1416
1417 let mut session_state = process_group.session.write();
1418 send_ttou = process_group.leader != session_state.get_foreground_process_group_leader()
1421 && !current_task.read().signal_mask().has_signal(SIGTTOU)
1422 && self.signal_actions.get(SIGTTOU).sa_handler != SIG_IGN;
1423
1424 if !send_ttou {
1425 session_state.set_foreground_process_group(&new_process_group);
1426 }
1427 }
1428
1429 if send_ttou {
1431 process_group.send_signals(&[SIGTTOU]);
1432 return error!(EINTR);
1433 }
1434
1435 Ok(())
1436 }
1437
1438 pub fn set_controlling_terminal(
1439 &self,
1440 current_task: &CurrentTask,
1441 terminal: &Terminal,
1442 is_main: bool,
1443 steal: bool,
1444 is_readable: bool,
1445 ) -> Result<(), Errno> {
1446 let state = self.read();
1448 let process_group = &state.process_group;
1449 let mut terminal_state = terminal.write();
1450
1451 let other_session = terminal_state.controller.as_ref().and_then(|cs| cs.session.upgrade());
1454 let (mut session_writer, other_session) =
1455 if let Some(other_session) = other_session.as_ref() {
1456 if *other_session == process_group.session {
1457 (process_group.session.mutable_state.write(), None)
1458 } else {
1459 let (session_writer, other_session_writer) = ordered_write_lock(
1460 &process_group.session.mutable_state,
1461 &other_session.mutable_state,
1462 );
1463 (session_writer, Some((other_session, other_session_writer)))
1464 }
1465 } else {
1466 (process_group.session.mutable_state.write(), None)
1467 };
1468
1469 if process_group.session.leader != self.leader {
1472 return error!(EINVAL);
1473 }
1474 if let Some(ref current_ct) = session_writer.controlling_terminal {
1475 if current_ct.matches(terminal, is_main) {
1476 return Ok(());
1477 } else {
1478 return error!(EINVAL);
1479 }
1480 }
1481
1482 let mut has_admin_capability_determined = false;
1483
1484 if let Some((other_session, mut other_session_writer)) = other_session {
1490 debug_assert!(*other_session != process_group.session);
1491 if !steal {
1492 return error!(EPERM);
1493 }
1494 security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1495 has_admin_capability_determined = true;
1496
1497 other_session_writer.controlling_terminal = None;
1499 }
1500
1501 if !is_readable && !has_admin_capability_determined {
1502 security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
1503 }
1504
1505 session_writer.controlling_terminal = Some(ControllingTerminal::new(terminal, is_main));
1506 terminal_state.controller = TerminalController::new(&process_group.session);
1507 Ok(())
1508 }
1509
1510 pub fn release_controlling_terminal(
1511 &self,
1512 _current_task: &CurrentTask,
1513 terminal: &Terminal,
1514 is_main: bool,
1515 ) -> Result<(), Errno> {
1516 let process_group;
1517 {
1518 let state = self.read();
1520 process_group = Arc::clone(&state.process_group);
1521 let mut terminal_state = terminal.write();
1522 let mut session_writer = process_group.session.write();
1523
1524 Self::check_terminal_controller(&process_group.session, &terminal_state.controller)?;
1526 if !session_writer
1527 .controlling_terminal
1528 .as_ref()
1529 .map_or(false, |ct| ct.matches(terminal, is_main))
1530 {
1531 return error!(ENOTTY);
1532 }
1533
1534 session_writer.controlling_terminal = None;
1542 terminal_state.controller = None;
1543 }
1544
1545 if process_group.session.leader == self.leader {
1546 process_group.send_signals(&[SIGHUP, SIGCONT]);
1547 }
1548
1549 Ok(())
1550 }
1551
1552 fn check_orphans(&self, pids: &PidTable) {
1553 let mut thread_groups = self.read().children().collect::<Vec<_>>();
1554 let this = self.weak_self.upgrade().unwrap();
1555 thread_groups.push(this);
1556 let process_groups =
1557 thread_groups.iter().map(|tg| Arc::clone(&tg.read().process_group)).unique();
1558 for pg in process_groups {
1559 pg.check_orphaned(pids);
1560 }
1561 }
1562
1563 pub fn get_rlimit(&self, resource: Resource) -> u64 {
1564 self.limits.lock().get(resource).rlim_cur
1565 }
1566
1567 pub fn adjust_rlimits(
1569 current_task: &CurrentTask,
1570 target_task: &Task,
1571 resource: Resource,
1572 maybe_new_limit: Option<rlimit>,
1573 ) -> Result<rlimit, Errno> {
1574 let thread_group = target_task.thread_group();
1575 let mut limit_state = thread_group.limits.lock();
1576 let old_limit = limit_state.get(resource);
1577 if let Some(new_limit) = maybe_new_limit {
1578 if new_limit.rlim_max > old_limit.rlim_max
1579 && !security::is_task_capable_noaudit(current_task, CAP_SYS_RESOURCE)
1580 {
1581 return error!(EPERM);
1582 }
1583 security::task_setrlimit(current_task, &target_task, old_limit, new_limit)?;
1584 limit_state.set(resource, new_limit)
1585 }
1586 Ok(old_limit)
1587 }
1588
1589 pub fn time_stats(&self) -> TaskTimeStats {
1590 let process: &zx::Process = if self.process.as_handle_ref().is_invalid() {
1591 assert_eq!(
1594 self as *const ThreadGroup,
1595 Arc::as_ptr(&self.kernel.kthreads.system_thread_group())
1596 );
1597 &self.kernel.kthreads.starnix_process
1598 } else {
1599 &self.process
1600 };
1601
1602 let info =
1603 zx::Task::get_runtime_info(process).expect("Failed to get starnix process stats");
1604 TaskTimeStats {
1605 user_time: zx::MonotonicDuration::from_nanos(info.cpu_time),
1606 system_time: zx::MonotonicDuration::default(),
1608 }
1609 }
1610
1611 pub fn get_ptracees_and(
1615 &self,
1616 selector: &ProcessSelector,
1617 pids: &PidTable,
1618 f: &mut dyn FnMut(&Task, &TaskMutableState),
1619 ) {
1620 for tracee in self
1621 .ptracees
1622 .lock()
1623 .keys()
1624 .filter(|tracee_tid| selector.match_tid(**tracee_tid, &pids))
1625 .map(|tracee_tid| pids.get_task(*tracee_tid))
1626 {
1627 if let Ok(task_ref) = tracee {
1628 let task_state = task_ref.write();
1629 if task_state.ptrace.is_some() {
1630 f(&task_ref, &task_state);
1631 }
1632 }
1633 }
1634 }
1635
1636 pub fn get_waitable_ptracee(
1641 &self,
1642 selector: &ProcessSelector,
1643 options: &WaitingOptions,
1644 pids: &mut PidTable,
1645 ) -> Option<WaitResult> {
1646 let waitable_entry = self.write().zombie_ptracees.get_waitable_entry(selector, options);
1648 match waitable_entry {
1649 None => (),
1650 Some((zombie, None)) => return Some(zombie.to_wait_result()),
1651 Some((zombie, Some((tg, z)))) => {
1652 if let Some(tg) = tg.upgrade() {
1653 if Arc::as_ptr(&tg) != self as *const Self {
1654 tg.do_zombie_notifications(z);
1655 } else {
1656 {
1657 let mut state = tg.write();
1658 state.children.remove(&z.pid());
1659 state
1660 .deferred_zombie_ptracers
1661 .retain(|dzp| dzp.tracee_thread_group_key != z.thread_group_key);
1662 }
1663
1664 z.release(pids);
1665 };
1666 }
1667 return Some(zombie.to_wait_result());
1668 }
1669 }
1670
1671 let mut tasks = vec![];
1672
1673 self.get_ptracees_and(selector, pids, &mut |task: &Task, _| {
1675 tasks.push(task.weak_self.clone());
1676 });
1677 for task in tasks {
1678 let Some(task_ref) = task.upgrade() else {
1679 continue;
1680 };
1681
1682 let process_state = &mut task_ref.thread_group().write();
1683 let mut task_state = task_ref.write();
1684 if task_state
1685 .ptrace
1686 .as_ref()
1687 .is_some_and(|ptrace| ptrace.is_waitable(task_ref.load_stopped(), options))
1688 {
1689 let mut pid: i32 = 0;
1695 let info = process_state.tasks.values().next().unwrap().info().clone();
1696 let uid = info.real_creds().uid;
1697 let mut exit_status = None;
1698 let exit_signal = process_state.exit_signal.clone();
1699 let time_stats =
1700 process_state.base.time_stats() + process_state.children_time_stats;
1701 let task_stopped = task_ref.load_stopped();
1702
1703 #[derive(PartialEq)]
1704 enum ExitType {
1705 None,
1706 Cont,
1707 Stop,
1708 Kill,
1709 }
1710 if process_state.is_waitable() {
1711 let ptrace = &mut task_state.ptrace;
1712 let process_stopped = process_state.base.load_stopped();
1714 let mut fn_type = ExitType::None;
1715 if process_stopped == StopState::Awake && options.wait_for_continued {
1716 fn_type = ExitType::Cont;
1717 }
1718 let mut event = ptrace
1719 .as_ref()
1720 .map_or(PtraceEvent::None, |ptrace| {
1721 ptrace.event_data.as_ref().map_or(PtraceEvent::None, |data| data.event)
1722 })
1723 .clone();
1724 if process_stopped == StopState::GroupStopped
1726 && (options.wait_for_stopped || ptrace.is_some())
1727 {
1728 fn_type = ExitType::Stop;
1729 }
1730 if fn_type != ExitType::None {
1731 let siginfo = if options.keep_waitable_state {
1732 process_state.last_signal.clone()
1733 } else {
1734 process_state.last_signal.take()
1735 };
1736 if let Some(mut siginfo) = siginfo {
1737 if task_ref.thread_group().load_stopped() == StopState::GroupStopped
1738 && ptrace.as_ref().is_some_and(|ptrace| ptrace.is_seized())
1739 {
1740 if event == PtraceEvent::None {
1741 event = PtraceEvent::Stop;
1742 }
1743 siginfo.code |= (PtraceEvent::Stop as i32) << 8;
1744 }
1745 if siginfo.signal == SIGKILL {
1746 fn_type = ExitType::Kill;
1747 }
1748 exit_status = match fn_type {
1749 ExitType::Stop => Some(ExitStatus::Stop(siginfo, event)),
1750 ExitType::Cont => Some(ExitStatus::Continue(siginfo, event)),
1751 ExitType::Kill => Some(ExitStatus::Kill(siginfo)),
1752 _ => None,
1753 };
1754 }
1755 ptrace
1758 .as_mut()
1759 .map(|ptrace| ptrace.get_last_signal(options.keep_waitable_state));
1760 }
1761 pid = process_state.base.leader;
1762 }
1763 if exit_status == None {
1764 if let Some(ptrace) = task_state.ptrace.as_mut() {
1765 let mut fn_type = ExitType::None;
1767 let event = ptrace
1768 .event_data
1769 .as_ref()
1770 .map_or(PtraceEvent::None, |event| event.event);
1771 if task_stopped == StopState::Awake {
1772 fn_type = ExitType::Cont;
1773 }
1774 if task_stopped.is_stopping_or_stopped()
1775 || ptrace.stop_status == PtraceStatus::Listening
1776 {
1777 fn_type = ExitType::Stop;
1778 }
1779 if fn_type != ExitType::None {
1780 if let Some(siginfo) =
1781 ptrace.get_last_signal(options.keep_waitable_state)
1782 {
1783 if siginfo.signal == SIGKILL {
1784 fn_type = ExitType::Kill;
1785 }
1786 exit_status = match fn_type {
1787 ExitType::Stop => Some(ExitStatus::Stop(siginfo, event)),
1788 ExitType::Cont => Some(ExitStatus::Continue(siginfo, event)),
1789 ExitType::Kill => Some(ExitStatus::Kill(siginfo)),
1790 _ => None,
1791 };
1792 }
1793 }
1794 pid = task_ref.get_tid();
1795 }
1796 }
1797 if let Some(exit_status) = exit_status {
1798 return Some(WaitResult {
1799 pid,
1800 uid,
1801 exit_info: ProcessExitInfo { status: exit_status, exit_signal },
1802 time_stats,
1803 });
1804 }
1805 }
1806 }
1807 None
1808 }
1809
1810 pub fn send_signal_unchecked(
1820 &self,
1821 current_task: &CurrentTask,
1822 unchecked_signal: UncheckedSignal,
1823 ) -> Result<(), Errno> {
1824 if let Some(signal) = self.check_signal_access(current_task, unchecked_signal)? {
1825 let signal_info = SignalInfo::with_detail(
1826 signal,
1827 SI_USER as i32,
1828 SignalDetail::Kill {
1829 pid: current_task.thread_group().leader,
1830 uid: current_task.current_creds().uid,
1831 },
1832 );
1833
1834 self.write().send_signal(signal_info);
1835 }
1836
1837 Ok(())
1838 }
1839
1840 pub unsafe fn send_signal_unchecked_debug(
1845 &self,
1846 current_task: &CurrentTask,
1847 unchecked_signal: UncheckedSignal,
1848 ) -> Result<(), Errno> {
1849 let signal = Signal::try_from(unchecked_signal)?;
1850 let signal_info = SignalInfo::with_detail(
1851 signal,
1852 SI_USER as i32,
1853 SignalDetail::Kill {
1854 pid: current_task.thread_group().leader,
1855 uid: current_task.current_creds().uid,
1856 },
1857 );
1858
1859 self.write().send_signal(signal_info);
1860 Ok(())
1861 }
1862
1863 #[track_caller]
1876 pub fn send_signal_unchecked_with_info(
1877 &self,
1878 current_task: &CurrentTask,
1879 unchecked_signal: UncheckedSignal,
1880 siginfo_ref: UserAddress,
1881 options: IntoSignalInfoOptions,
1882 ) -> Result<(), Errno> {
1883 if let Some(signal) = self.check_signal_access(current_task, unchecked_signal)? {
1884 let siginfo = UncheckedSignalInfo::read_from_siginfo(current_task, siginfo_ref)?;
1885 if self.leader != current_task.get_pid()
1886 && (siginfo.code() >= 0 || siginfo.code() == SI_TKILL)
1887 {
1888 return error!(EPERM);
1889 }
1890
1891 self.write().send_signal(siginfo.into_signal_info(signal, options)?);
1892 }
1893
1894 Ok(())
1895 }
1896
1897 fn check_signal_access(
1905 &self,
1906 current_task: &CurrentTask,
1907 unchecked_signal: UncheckedSignal,
1908 ) -> Result<Option<Signal>, Errno> {
1909 let target_task = self.read().get_any_task()?;
1913 current_task.can_signal(&target_task, unchecked_signal)?;
1914
1915 if unchecked_signal.is_zero() {
1917 return Ok(None);
1918 }
1919
1920 let signal = Signal::try_from(unchecked_signal)?;
1921 security::check_signal_access(current_task, &target_task, signal)?;
1922
1923 Ok(Some(signal))
1924 }
1925
1926 pub fn has_signal_queued(&self, signal: Signal) -> bool {
1927 self.pending_signals.lock().has_queued(signal)
1928 }
1929
1930 pub fn num_signals_queued(&self) -> usize {
1931 self.pending_signals.lock().num_queued()
1932 }
1933
1934 pub fn get_pending_signals(&self) -> SigSet {
1935 self.pending_signals.lock().pending()
1936 }
1937
1938 pub fn is_any_signal_allowed_by_mask(&self, mask: SigSet) -> bool {
1939 self.pending_signals.lock().is_any_allowed_by_mask(mask)
1940 }
1941
1942 pub fn take_next_signal_where<F>(&self, predicate: F) -> Option<SignalInfo>
1943 where
1944 F: Fn(&SignalInfo) -> bool,
1945 {
1946 let mut signals = self.pending_signals.lock();
1947 let r = signals.take_next_where(predicate);
1948 self.has_pending_signals.store(!signals.is_empty(), Ordering::Relaxed);
1949 r
1950 }
1951
1952 pub async fn shut_down(this: Weak<Self>) {
1958 const SHUTDOWN_SIGNAL_HANDLING_TIMEOUT: zx::MonotonicDuration =
1959 zx::MonotonicDuration::from_seconds(1);
1960
1961 let (tg_name, mut on_exited) = {
1963 let Some(this) = this.upgrade() else {
1965 return;
1966 };
1967
1968 let mut state = this.write();
1969 if state.is_exited() {
1970 return;
1972 }
1973
1974 let (on_exited_send, on_exited) = futures::channel::oneshot::channel();
1976 state.exit_notifier = Some(on_exited_send);
1977
1978 let tg_name = format!("{this:?}");
1980
1981 (tg_name, on_exited)
1982 };
1983
1984 log_debug!(tg:% = tg_name; "shutting down thread group, sending SIGTERM");
1985 this.upgrade().map(|tg| tg.write().send_signal(SignalInfo::kernel(SIGTERM)));
1986
1987 let timeout = fuchsia_async::Timer::new(SHUTDOWN_SIGNAL_HANDLING_TIMEOUT);
1989 futures::pin_mut!(timeout);
1990
1991 futures::select_biased! {
1993 _ = &mut on_exited => (),
1994 _ = timeout => {
1995 log_debug!(tg:% = tg_name; "sending SIGKILL");
1996 this.upgrade().map(|tg| tg.write().send_signal(SignalInfo::kernel(SIGKILL)));
1997 },
1998 };
1999
2000 log_debug!(tg:% = tg_name; "waiting for exit");
2001 on_exited.await.ok();
2004 log_debug!(tg:% = tg_name; "thread group shutdown complete");
2005 }
2006
2007 pub fn get_process_koid(&self) -> Result<Koid, Status> {
2013 self.process.koid()
2014 }
2015}
2016
2017pub enum WaitableChildResult {
2018 ReadyNow(Box<WaitResult>),
2019 ShouldWait,
2020 NoneFound,
2021}
2022
2023#[apply(state_implementation!)]
2024impl ThreadGroupMutableState<Base = ThreadGroup> {
2025 pub fn leader(&self) -> pid_t {
2026 self.base.leader
2027 }
2028
2029 pub fn leader_command(&self) -> TaskCommand {
2030 self.get_task(self.leader())
2031 .map(|l| l.command())
2032 .unwrap_or_else(|| TaskCommand::new(b"<leader exited>"))
2033 }
2034
2035 pub fn is_running(&self) -> bool {
2036 matches!(self.run_state, ThreadGroupRunState::Running)
2037 }
2038
2039 pub fn is_exited(&self) -> bool {
2040 matches!(self.run_state, ThreadGroupRunState::Exited(_))
2041 }
2042
2043 fn set_exiting(&mut self, exit_status: ExitStatus) {
2044 self.run_state = ThreadGroupRunState::Exiting(exit_status);
2045 }
2046
2047 fn set_exited(&mut self) {
2048 let ThreadGroupRunState::Exiting(exit_status) = std::mem::take(&mut self.run_state) else {
2049 panic!("Must transition from Exiting to Exited");
2050 };
2051 self.run_state = ThreadGroupRunState::Exited(exit_status);
2052
2053 if let Some(notifier) = self.exit_notifier.take() {
2054 let _ = notifier.send(());
2055 }
2056 }
2057
2058 pub fn children(&self) -> impl Iterator<Item = Arc<ThreadGroup>> + '_ {
2059 self.children.values().map(|v| {
2060 v.upgrade().expect("Weak references to processes in ThreadGroup must always be valid")
2061 })
2062 }
2063
2064 pub fn tasks(&self) -> Vec<Arc<Task>> {
2065 self.tasks.values().flat_map(|t| t.upgrade()).collect()
2066 }
2067
2068 pub fn task_ids(&self) -> impl Iterator<Item = &tid_t> {
2069 self.tasks.keys()
2070 }
2071
2072 pub fn contains_task(&self, tid: tid_t) -> bool {
2073 self.tasks.contains_key(&tid)
2074 }
2075
2076 pub fn get_task(&self, tid: tid_t) -> Option<Arc<Task>> {
2077 self.tasks.get(&tid).and_then(|t| t.upgrade())
2078 }
2079
2080 pub fn tasks_count(&self) -> usize {
2081 self.tasks.len()
2082 }
2083
2084 pub fn get_ppid(&self) -> pid_t {
2085 match &self.parent {
2086 Some(parent) => parent.upgrade().leader,
2087 None => 0,
2088 }
2089 }
2090
2091 fn set_process_group(
2099 &mut self,
2100 process_group: Arc<ProcessGroup>,
2101 pids: &PidTable,
2102 ) -> SessionDisassociation {
2103 if self.process_group == process_group {
2104 return SessionDisassociation::new(None);
2105 }
2106 let session = self.leave_process_group(pids);
2107 self.process_group = process_group;
2108 self.process_group.insert(self.base);
2109 session
2110 }
2111
2112 fn leave_process_group(&mut self, pids: &PidTable) -> SessionDisassociation {
2120 let (is_empty, disassociation) = self.process_group.remove(self.base);
2121 if is_empty {
2122 self.process_group.session.write().remove(self.process_group.leader);
2123 pids.remove_process_group(self.process_group.leader);
2124 }
2125 disassociation
2126 }
2127
2128 pub fn is_waitable(&self) -> bool {
2131 return self.last_signal.is_some() && !self.base.load_stopped().is_in_progress();
2132 }
2133
2134 pub fn get_waitable_zombie(
2135 &mut self,
2136 zombie_list: &dyn Fn(&mut ThreadGroupMutableState) -> &mut Vec<OwnedRef<ZombieProcess>>,
2137 selector: &ProcessSelector,
2138 options: &WaitingOptions,
2139 pids: &mut PidTable,
2140 ) -> Option<WaitResult> {
2141 let selected_zombie_position = zombie_list(self)
2143 .iter()
2144 .rev()
2145 .position(|zombie| zombie.matches_selector_and_waiting_option(selector, options))
2146 .map(|position_starting_from_the_back| {
2147 zombie_list(self).len() - 1 - position_starting_from_the_back
2148 });
2149
2150 selected_zombie_position.map(|position| {
2151 if options.keep_waitable_state {
2152 zombie_list(self)[position].to_wait_result()
2153 } else {
2154 let zombie = zombie_list(self).remove(position);
2155 self.children_time_stats += zombie.time_stats;
2156 let result = zombie.to_wait_result();
2157 zombie.release(pids);
2158 result
2159 }
2160 })
2161 }
2162
2163 pub fn is_correct_exit_signal(for_clone: bool, exit_code: Option<Signal>) -> bool {
2164 for_clone == (exit_code != Some(SIGCHLD))
2165 }
2166
2167 fn get_waitable_running_children(
2168 &self,
2169 selector: &ProcessSelector,
2170 options: &WaitingOptions,
2171 pids: &PidTable,
2172 ) -> WaitableChildResult {
2173 let filter_children_by_pid_selector = |child: &ThreadGroup| match *selector {
2175 ProcessSelector::Any => true,
2176 ProcessSelector::Pid(pid) => child.leader == pid,
2177 ProcessSelector::Pgid(pgid) => {
2178 let _token = allow_subclass();
2182 pids.get_process_group(pgid).as_ref() == Some(&child.read().process_group)
2183 }
2184 ProcessSelector::Process(ref key) => *key == ThreadGroupKey::from(child),
2185 };
2186
2187 let filter_children_by_waiting_options = |child: &ThreadGroup| {
2189 if options.wait_for_all {
2190 return true;
2191 }
2192 let _token = allow_subclass();
2196 Self::is_correct_exit_signal(options.wait_for_clone, child.read().exit_signal)
2197 };
2198
2199 let mut selected_children = self
2202 .children
2203 .values()
2204 .map(|t| t.upgrade().unwrap())
2205 .filter(|tg| filter_children_by_pid_selector(&tg))
2206 .filter(|tg| filter_children_by_waiting_options(&tg))
2207 .peekable();
2208 if selected_children.peek().is_none() {
2209 if self.deferred_zombie_ptracers.iter().any(|dzp| match *selector {
2211 ProcessSelector::Any => true,
2212 ProcessSelector::Pid(pid) => dzp.tracee_thread_group_key.pid() == pid,
2213 ProcessSelector::Pgid(pgid) => pgid == dzp.tracee_pgid,
2214 ProcessSelector::Process(ref key) => *key == dzp.tracee_thread_group_key,
2215 }) {
2216 return WaitableChildResult::ShouldWait;
2217 }
2218
2219 return WaitableChildResult::NoneFound;
2220 }
2221 for child in selected_children {
2222 let _token = allow_subclass();
2226 let child = child.write();
2227 if child.last_signal.is_some() {
2228 let build_wait_result = |mut child: ThreadGroupWriteGuard<'_>,
2229 exit_status: &dyn Fn(SignalInfo) -> ExitStatus|
2230 -> WaitResult {
2231 let siginfo = if options.keep_waitable_state {
2232 child.last_signal.clone().unwrap()
2233 } else {
2234 child.last_signal.take().unwrap()
2235 };
2236 let exit_status = if siginfo.signal == SIGKILL {
2237 ExitStatus::Kill(siginfo)
2239 } else {
2240 exit_status(siginfo)
2241 };
2242 let info = child.tasks.values().next().unwrap().info();
2243 let uid = info.real_creds().uid;
2244 WaitResult {
2245 pid: child.base.leader,
2246 uid,
2247 exit_info: ProcessExitInfo {
2248 status: exit_status,
2249 exit_signal: child.exit_signal,
2250 },
2251 time_stats: child.base.time_stats() + child.children_time_stats,
2252 }
2253 };
2254 let child_stopped = child.base.load_stopped();
2255 if child_stopped == StopState::Awake && options.wait_for_continued {
2256 return WaitableChildResult::ReadyNow(Box::new(build_wait_result(
2257 child,
2258 &|siginfo| ExitStatus::Continue(siginfo, PtraceEvent::None),
2259 )));
2260 }
2261 if child_stopped == StopState::GroupStopped && options.wait_for_stopped {
2262 return WaitableChildResult::ReadyNow(Box::new(build_wait_result(
2263 child,
2264 &|siginfo| ExitStatus::Stop(siginfo, PtraceEvent::None),
2265 )));
2266 }
2267 }
2268 }
2269
2270 WaitableChildResult::ShouldWait
2271 }
2272
2273 pub fn get_waitable_child(
2279 &mut self,
2280 selector: &ProcessSelector,
2281 options: &WaitingOptions,
2282 pids: &mut PidTable,
2283 ) -> WaitableChildResult {
2284 if options.wait_for_exited {
2285 if let Some(waitable_zombie) = self.get_waitable_zombie(
2286 &|state: &mut ThreadGroupMutableState| &mut state.zombie_children,
2287 selector,
2288 options,
2289 pids,
2290 ) {
2291 return WaitableChildResult::ReadyNow(Box::new(waitable_zombie));
2292 }
2293 }
2294
2295 self.get_waitable_running_children(selector, options, pids)
2296 }
2297
2298 pub fn get_running_task(&self) -> Result<Arc<Task>, Errno> {
2300 self.tasks
2301 .iter()
2302 .find_map(|container| container.1.upgrade().filter(|task| task.is_running()))
2303 .ok_or_else(|| errno!(ESRCH))
2304 }
2305
2306 pub fn get_any_task(&self) -> Result<Arc<Task>, Errno> {
2312 self.get_running_task()
2313 .ok()
2314 .or_else(|| self.base.leader_task.get().and_then(|t| t.upgrade()))
2315 .ok_or_else(|| errno!(ESRCH))
2316 }
2317
2318 pub fn set_stopped(
2325 mut self,
2326 new_stopped: StopState,
2327 siginfo: Option<SignalInfo>,
2328 finalize_only: bool,
2329 ) -> StopState {
2330 if let Some(stopped) = self.base.check_stopped_state(new_stopped, finalize_only) {
2331 return stopped;
2332 }
2333
2334 if self.base.load_stopped() == StopState::Waking
2337 && (new_stopped == StopState::GroupStopping || new_stopped == StopState::GroupStopped)
2338 {
2339 return self.base.load_stopped();
2340 }
2341
2342 self.store_stopped(new_stopped);
2346 if let Some(signal) = &siginfo {
2347 if signal.signal != SIGKILL {
2351 self.last_signal = siginfo;
2352 }
2353 }
2354 if new_stopped == StopState::Waking || new_stopped == StopState::ForceWaking {
2355 self.lifecycle_waiters.notify_value(ThreadGroupLifecycleWaitValue::Stopped);
2356 };
2357
2358 let parent = (!new_stopped.is_in_progress()).then(|| self.parent.clone()).flatten();
2359
2360 std::mem::drop(self);
2362 if let Some(parent) = parent {
2363 let parent = parent.upgrade();
2364 parent
2365 .write()
2366 .lifecycle_waiters
2367 .notify_value(ThreadGroupLifecycleWaitValue::ChildStatus);
2368 }
2369
2370 new_stopped
2371 }
2372
2373 fn store_stopped(&mut self, state: StopState) {
2374 self.base.stop_state.store(state, Ordering::Relaxed)
2379 }
2380
2381 #[allow(unused_mut, reason = "needed for some but not all macro outputs")]
2383 pub fn send_signal(mut self, signal_info: SignalInfo) {
2384 let sigaction = self.base.signal_actions.get(signal_info.signal);
2385 let action = action_for_signal(&signal_info, sigaction);
2386
2387 {
2388 let mut pending_signals = self.base.pending_signals.lock();
2389 pending_signals.enqueue(signal_info.clone());
2390 self.base.has_pending_signals.store(true, Ordering::Relaxed);
2391 }
2392 let tasks: Vec<Weak<Task>> = self.tasks.values().map(|t| t.weak_clone()).collect();
2393
2394 if signal_info.signal == SIGKILL {
2396 self.set_stopped(StopState::ForceWaking, Some(signal_info.clone()), false);
2397 } else if signal_info.signal == SIGCONT {
2398 self.set_stopped(StopState::Waking, Some(signal_info.clone()), false);
2399 }
2400
2401 let mut has_interrupted_task = false;
2402 for task in tasks.iter().flat_map(|t| t.upgrade()) {
2403 let mut task_state = task.write();
2404
2405 if signal_info.signal == SIGKILL {
2406 task_state.thaw();
2407 task_state.set_stopped(StopState::ForceWaking, None, None, None);
2408 } else if signal_info.signal == SIGCONT {
2409 task_state.set_stopped(StopState::Waking, None, None, None);
2410 }
2411
2412 let is_masked = task_state.is_signal_masked(signal_info.signal);
2413 let was_masked = task_state.is_signal_masked_by_saved_mask(signal_info.signal);
2414
2415 let is_queued = action != DeliveryAction::Ignore
2416 || is_masked
2417 || was_masked
2418 || task_state.is_ptraced();
2419
2420 if is_queued {
2421 task_state.notify_signal_waiters(&signal_info.signal);
2422
2423 if !is_masked && action.must_interrupt(Some(sigaction)) && !has_interrupted_task {
2424 drop(task_state);
2427 task.interrupt();
2428 has_interrupted_task = true;
2429 }
2430 }
2431 }
2432 }
2433}
2434
2435pub struct TaskContainer(Weak<Task>, TaskPersistentInfo);
2441
2442impl From<&Arc<Task>> for TaskContainer {
2443 fn from(task: &Arc<Task>) -> Self {
2444 Self(Arc::downgrade(task), task.persistent_info.clone())
2445 }
2446}
2447
2448impl From<TaskContainer> for TaskPersistentInfo {
2449 fn from(container: TaskContainer) -> TaskPersistentInfo {
2450 container.1
2451 }
2452}
2453
2454impl TaskContainer {
2455 fn upgrade(&self) -> Option<Arc<Task>> {
2456 self.0.upgrade()
2457 }
2458
2459 fn weak_clone(&self) -> Weak<Task> {
2460 self.0.clone()
2461 }
2462
2463 fn info(&self) -> &TaskPersistentInfo {
2464 &self.1
2465 }
2466}
2467
2468#[cfg(test)]
2469mod test {
2470 use super::*;
2471 use crate::testing::*;
2472
2473 #[::fuchsia::test]
2474 async fn test_setsid() {
2475 spawn_kernel_and_run(async |current_task| {
2476 fn get_process_group(task: &Task) -> Arc<ProcessGroup> {
2477 Arc::clone(&task.thread_group().read().process_group)
2478 }
2479 assert_eq!(current_task.thread_group().setsid(), error!(EPERM));
2480
2481 let child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2482 assert_eq!(get_process_group(¤t_task), get_process_group(&child_task));
2483
2484 let old_process_group = child_task.thread_group().read().process_group.clone();
2485 assert_eq!(child_task.thread_group().setsid(), Ok(()));
2486 assert_eq!(
2487 child_task.thread_group().read().process_group.session.leader,
2488 child_task.get_pid()
2489 );
2490 assert!(!old_process_group.read().thread_groups().contains(child_task.thread_group()));
2491 })
2492 .await;
2493 }
2494
2495 #[::fuchsia::test]
2496 async fn test_exit_status() {
2497 spawn_kernel_and_run(async |current_task| {
2498 let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
2499 child.thread_group().kill(ExitStatus::Exit(42), None);
2500 std::mem::drop(child);
2501 assert_eq!(
2502 current_task.thread_group().read().zombie_children[0].exit_info.status,
2503 ExitStatus::Exit(42)
2504 );
2505 })
2506 .await;
2507 }
2508
2509 #[::fuchsia::test]
2510 async fn test_setgpid() {
2511 spawn_kernel_and_run(async |current_task| {
2512 assert_eq!(current_task.thread_group().setsid(), error!(EPERM));
2513
2514 let child_task1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2515 let child_task2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
2516 let execd_child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2517 execd_child_task.thread_group().write().did_exec = true;
2518 let other_session_child_task = current_task.clone_task_for_test(0, Some(SIGCHLD));
2519 assert_eq!(other_session_child_task.thread_group().setsid(), Ok(()));
2520
2521 assert_eq!(
2522 child_task1.thread_group().setpgid(¤t_task, ¤t_task, 0),
2523 error!(ESRCH)
2524 );
2525 assert_eq!(
2526 current_task.thread_group().setpgid(¤t_task, &execd_child_task, 0),
2527 error!(EACCES)
2528 );
2529 assert_eq!(
2530 current_task.thread_group().setpgid(¤t_task, ¤t_task, 0),
2531 error!(EPERM)
2532 );
2533 assert_eq!(
2534 current_task.thread_group().setpgid(¤t_task, &other_session_child_task, 0),
2535 error!(EPERM)
2536 );
2537 assert_eq!(
2538 current_task.thread_group().setpgid(¤t_task, &child_task1, -1),
2539 error!(EINVAL)
2540 );
2541 assert_eq!(
2542 current_task.thread_group().setpgid(¤t_task, &child_task1, 255),
2543 error!(EPERM)
2544 );
2545 assert_eq!(
2546 current_task.thread_group().setpgid(
2547 ¤t_task,
2548 &child_task1,
2549 other_session_child_task.tid
2550 ),
2551 error!(EPERM)
2552 );
2553
2554 assert_eq!(child_task1.thread_group().setpgid(¤t_task, &child_task1, 0), Ok(()));
2555 assert_eq!(
2556 child_task1.thread_group().read().process_group.session.leader,
2557 current_task.tid
2558 );
2559 assert_eq!(child_task1.thread_group().read().process_group.leader, child_task1.tid);
2560
2561 let old_process_group = child_task2.thread_group().read().process_group.clone();
2562 assert_eq!(
2563 current_task.thread_group().setpgid(¤t_task, &child_task2, child_task1.tid),
2564 Ok(())
2565 );
2566 assert_eq!(child_task2.thread_group().read().process_group.leader, child_task1.tid);
2567 assert!(!old_process_group.read().thread_groups().contains(child_task2.thread_group()));
2568 })
2569 .await;
2570 }
2571
2572 #[::fuchsia::test]
2573 async fn test_adopt_children() {
2574 spawn_kernel_and_run(async |current_task| {
2575 let task1 = current_task.clone_task_for_test(0, None);
2576 let task2 = task1.clone_task_for_test(0, None);
2577 let task3 = task2.clone_task_for_test(0, None);
2578
2579 assert_eq!(task3.thread_group().read().get_ppid(), task2.tid);
2580
2581 task2.thread_group().kill(ExitStatus::Exit(0), None);
2582 std::mem::drop(task2);
2583
2584 assert_eq!(task3.thread_group().read().get_ppid(), current_task.tid);
2586 })
2587 .await;
2588 }
2589
2590 #[::fuchsia::test]
2591 async fn test_getppid_after_self_and_parent_exit() {
2592 spawn_kernel_and_run(async |current_task| {
2593 let task1 = current_task.clone_task_for_test(0, None);
2594 let task2 = task1.clone_task_for_test(0, None);
2595
2596 let tg1 = task1.thread_group().clone();
2598 let tg2 = task2.thread_group().clone();
2599
2600 assert_eq!(tg1.read().get_ppid(), current_task.tid);
2601 assert_eq!(tg2.read().get_ppid(), task1.tid);
2602
2603 tg2.kill(ExitStatus::Exit(0), None);
2605 std::mem::drop(task2);
2606
2607 tg1.kill(ExitStatus::Exit(0), None);
2609 std::mem::drop(task1);
2610 std::mem::drop(tg1);
2611
2612 let _ = tg2.read().get_ppid();
2615 })
2616 .await;
2617 }
2618}