Skip to content

Commit f117481

Browse files
committed
fix(signal): commit fatal group exit before wakeup
Publish the shared fatal group-exit code under the sighand lock before exposing private SIGKILL pending state. This prevents de_thread cancellation from racing ahead and replacing SIGKILL with EAGAIN. Report the shared group-exit status consistently through zombie waits and natural-parent SIGCHLD notification, matching Linux observable semantics. Signed-off-by: longjin <longjin@dragonos.org>
1 parent 7dc117b commit f117481

3 files changed

Lines changed: 36 additions & 15 deletions

File tree

kernel/src/ipc/signal.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,26 @@ impl Signal {
431431
compiler_fence(core::sync::atomic::Ordering::SeqCst);
432432
// 根据信号类型选择添加到线程级 pending 还是进程级 shared_pending
433433
let is_thread_target = matches!(pt, PidType::PID);
434+
let target_pcb = if is_thread_target {
435+
if self.wants_signal(pcb.clone()) {
436+
Some(pcb.clone())
437+
} else {
438+
None
439+
}
440+
} else {
441+
self.select_group_signal_target(pcb.clone())
442+
};
443+
444+
// Linux commits SIGNAL_GROUP_EXIT and its shared exit code under the
445+
// sighand lock before making a fatal signal observable to de_thread().
446+
// Otherwise the exec owner can see private SIGKILL pending, cancel
447+
// exec, and win the race to publish EAGAIN as the group exit status.
448+
if let Some(target_pcb) = target_pcb.as_ref() {
449+
if self.start_fatal_group_exit_if_needed(pcb.clone(), target_pcb.clone()) {
450+
return;
451+
}
452+
}
453+
434454
if is_thread_target {
435455
// 线程级信号:添加到线程的 sig_pending
436456
pcb.sig_info_mut()
@@ -449,27 +469,13 @@ impl Signal {
449469
// 若目标进程存在 signalfd 监听该信号,需要唤醒其等待者/epoll。
450470
crate::ipc::signalfd::notify_signalfd_for_pcb(&pcb, *self);
451471

452-
let target_pcb = if is_thread_target {
453-
if self.wants_signal(pcb.clone()) {
454-
Some(pcb.clone())
455-
} else {
456-
None
457-
}
458-
} else {
459-
self.select_group_signal_target(pcb.clone())
460-
};
461-
462472
let Some(target_pcb) = target_pcb else {
463473
if is_thread_target {
464474
pcb.recalc_sigpending();
465475
}
466476
return;
467477
};
468478

469-
if self.start_fatal_group_exit_if_needed(pcb.clone(), target_pcb.clone()) {
470-
return;
471-
}
472-
473479
target_pcb.recalc_sigpending();
474480
compiler_fence(core::sync::atomic::Ordering::SeqCst);
475481
signal_wake_up(target_pcb, *self == Signal::SIGKILL);

kernel/src/process/exit.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,16 @@ fn report_wait_event(
498498
WaitRelation::Ptraced => reap_blocked_by_group_exec(child_pcb),
499499
});
500500
if is_zombie && !delayed_zombie && kwo.options.contains(WaitOption::WEXITED) {
501-
let Some(raw_wstatus) = state.raw_wstatus().map(|status| status as i32) else {
501+
let Some(task_wstatus) = state.raw_wstatus().map(|status| status as i32) else {
502502
return CandidateDecision::Pending { can_change: false };
503503
};
504+
// Linux wait_task_zombie() exposes signal->group_exit_code after
505+
// SIGNAL_GROUP_EXIT for both natural and ptrace zombie waits.
506+
let raw_wstatus = child_pcb
507+
.sighand()
508+
.group_exit_code_if_set()
509+
.map(|status| status as i32)
510+
.unwrap_or(task_wstatus);
504511
let consume = !kwo.options.contains(WaitOption::WNOWAIT);
505512
match relation {
506513
WaitRelation::Natural => {

kernel/src/process/manager/exit.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ impl ProcessManager {
189189
.raw_wstatus()
190190
.expect("natural-parent exit notification requires an exited child")
191191
as i32;
192+
// Preserve Linux's observable group-exit status even though DragonOS
193+
// stores the last task's local do_exit() argument separately (for
194+
// example, de_thread() may return EAGAIN after SIGKILL committed).
195+
let raw_status = child
196+
.sighand()
197+
.group_exit_code_if_set()
198+
.map(|status| status as i32)
199+
.unwrap_or(raw_status);
192200
let (status, code) = wstatus_to_waitid_exit_info(raw_status);
193201
let pid = child
194202
.task_pid_nr_ns(PidType::PID, Some(parent.active_pid_ns()))

0 commit comments

Comments
 (0)