clone: add all tasks to process task list

This prevents a bug where `sys_exit` calls `exit_group` for the thread's
process, even when there are still active threads.
This commit is contained in:
Matthew Leach
2025-12-27 09:55:26 +00:00
committed by Ashwin Naren
parent 0f566f37e7
commit 116a1adbd0
7 changed files with 23 additions and 16 deletions

View File

@@ -304,11 +304,11 @@ State:\t{state}
Tgid:\t{tgid}
FDSize:\t{fd_size}
Pid:\t{pid}
Threads:\t{threads}\n",
Threads:\t{tasks}\n",
name = name.as_str(),
tgid = task.process.tgid,
fd_size = task.fd_table.lock_save_irq().len(),
threads = task.process.threads.lock_save_irq().len(),
tasks = task.process.tasks.lock_save_irq().len(),
),
TaskFileType::Comm => format!("{name}\n", name = name.as_str()),
TaskFileType::State => format!("{state}\n"),

View File

@@ -166,7 +166,14 @@ pub async fn sys_clone(
let tid = new_task.tid;
sched::insert_task_cross_cpu(Arc::new(new_task));
let task = Arc::new(new_task);
sched::insert_task_cross_cpu(task.clone());
task.process
.tasks
.lock_save_irq()
.insert(tid, Arc::downgrade(&task));
// Honour CLONE_*SETTID semantics for the parent and (shared-VM) child.
if flags.contains(CloneFlags::CLONE_PARENT_SETTID) && !parent_tidptr.is_null() {

View File

@@ -43,7 +43,7 @@ pub fn do_exit_group(exit_code: ChildState) {
// Signal all other threads in the group to terminate. We iterate over Weak
// pointers and upgrade them.
for thread_weak in process.threads.lock_save_irq().values() {
for thread_weak in process.tasks.lock_save_irq().values() {
if let Some(other_thread) = thread_weak.upgrade() {
// Don't signal ourselves
if other_thread.tid != task.tid {
@@ -117,18 +117,18 @@ pub async fn sys_exit(exit_code: usize) -> Result<usize> {
}
let process = Arc::clone(&task.process);
let mut thread_lock = process.threads.lock_save_irq();
let mut tasks_lock = process.tasks.lock_save_irq();
// How many threads are left? We must count live ones.
let live_threads = thread_lock
let live_tasks = tasks_lock
.values()
.filter(|t| t.upgrade().is_some())
.count();
if live_threads <= 1 {
// We are the last thread. This is equivalent to an exit_group. The
// exit code for an implicit exit_group is often 0.
drop(thread_lock);
if live_tasks <= 1 {
// We are the last task. This is equivalent to an exit_group. The exit
// code for an implicit exit_group is often 0.
drop(tasks_lock);
// NOTE: We don't need to worry about a race condition here. Since
// we've established we're the only thread and we're executing a
@@ -144,7 +144,7 @@ pub async fn sys_exit(exit_code: usize) -> Result<usize> {
*task.state.lock_save_irq() = TaskState::Finished;
// Remove ourself from the process's thread list.
thread_lock.remove(&task.tid);
tasks_lock.remove(&task.tid);
// 3. This thread stops executing forever. The task struct will be
// deallocated when the last Arc<Task> is dropped (e.g., by the

View File

@@ -94,7 +94,7 @@ pub struct ThreadGroup {
pub umask: SpinLock<u32>,
pub parent: SpinLock<Option<Weak<ThreadGroup>>>,
pub children: SpinLock<BTreeMap<Tgid, Arc<ThreadGroup>>>,
pub threads: SpinLock<BTreeMap<Tid, Weak<Task>>>,
pub tasks: SpinLock<BTreeMap<Tid, Weak<Task>>>,
pub signals: Arc<SpinLock<SignalState>>,
pub rsrc_lim: Arc<SpinLock<ResourceLimits>>,
pub pending_signals: SpinLock<SigSet>,

View File

@@ -73,7 +73,7 @@ impl ThreadGroupBuilder {
// couldn't then differentiate between a child and a parent.
next_tid: AtomicU32::new(1),
state: SpinLock::new(ProcessState::Running),
threads: SpinLock::new(BTreeMap::new()),
tasks: SpinLock::new(BTreeMap::new()),
});
TG_LIST

View File

@@ -82,7 +82,7 @@ pub fn sys_tkill(tid: PidT, signal: UserSigId) -> Result<usize> {
} else {
let task = current_task
.process
.threads
.tasks
.lock_save_irq()
.get(&target_tid)
.and_then(|t| t.upgrade())

View File

@@ -255,7 +255,7 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
parent.signals.lock_save_irq().set_pending(SigId::SIGCHLD);
}
for thr_weak in process.threads.lock_save_irq().values() {
for thr_weak in process.tasks.lock_save_irq().values() {
if let Some(thr) = thr_weak.upgrade() {
*thr.state.lock_save_irq() = TaskState::Stopped;
}
@@ -268,7 +268,7 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
let process = &task.process;
// Wake up all sleeping threads in the process.
for thr_weak in process.threads.lock_save_irq().values() {
for thr_weak in process.tasks.lock_save_irq().values() {
if let Some(thr) = thr_weak.upgrade() {
let mut st = thr.state.lock_save_irq();
if *st == TaskState::Sleeping {