Skip to content

Commit ae28b35

Browse files
authored
fix(process): serialize non-leader exec handoff (#2156)
Model non-leader exec as an owned group-exec transaction that serializes sibling teardown, old-leader exit, fatal cancellation, identity migration, and observer visibility. Atomically preserve PID/TGID/PGID/SID membership, raw-PID lookup, cgroup placement, parent-child ownership, ptrace relations, pidfd identity, session state, and accumulated resource usage across the leader handoff. Align wait, SIGCHLD, ptrace exit notification, fatal thread-group signals, and process-group delivery with Linux 6.6 semantics. Preserve exited-thread CPU accounting and publish child-exit state before waking signal and wait observers. Serialize controlling-TTY ownership and PTY peer lifetime transitions to prevent stale session updates, duplicate group signals, premature ctty removal, and slave-open races. Add deterministic dunitest coverage for non-leader exec identity, pidfd visibility, fatal-signal races, wait ownership, resource accounting, process groups, ptrace siginfo, and PTY lifetime behavior. Fixes #2153 Signed-off-by: longjin <longjin@dragonos.org>
1 parent e374b45 commit ae28b35

28 files changed

Lines changed: 2666 additions & 618 deletions

kernel/src/driver/tty/pty/unix98pty.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,13 @@ impl TtyOperation for Unix98PtyDriverInner {
814814
if subtype == TtyDriverSubType::PtySlave {
815815
if let Some(hook_arc) = tty.private_fields() {
816816
if let Some(hook) = hook_arc.as_any().downcast_ref::<PtyDevPtsLink>() {
817+
// Pin the master before publishing this in-flight slave
818+
// open. The link is weak, so the last master file could
819+
// otherwise drop it between begin_slave_open() and
820+
// pty_common_open(), leaking ENODEV from the latter.
821+
// A concurrent master close is reported as EIO below,
822+
// matching Linux pts open semantics.
823+
let _master = tty.link().ok_or(SystemError::EIO)?;
817824
hook.begin_slave_open()?;
818825
return match PtyCommon::pty_common_open(tty) {
819826
Ok(()) => {

kernel/src/driver/tty/tty_core.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ impl TtyCore {
141141
read_wq: EventWaitQueue::new(),
142142
write_wq: EventWaitQueue::new(),
143143
write_lock: TtySleepLock::new(),
144+
job_control_lock: TtySleepLock::new(),
144145
port: RwLock::new(None),
145146
index,
146147
vc_index: AtomicUsize::new(usize::MAX),
@@ -263,6 +264,7 @@ impl TtyCore {
263264
}
264265

265266
pub fn tty_vhangup(tty: Arc<TtyCore>) {
267+
let _job_control_guard = tty.core().job_control_lock().lock();
266268
{
267269
let mut flags = tty.core().flags_write();
268270
if flags.intersects(TtyFlag::HUPPED | TtyFlag::HUPPING) {
@@ -276,10 +278,7 @@ impl TtyCore {
276278
// every pre-hangup open file description.
277279
tty.core().fasync_items.clear();
278280

279-
let (sid, pgid) = {
280-
let ctrl = tty.core().contorl_info_irqsave();
281-
(ctrl.session.clone(), ctrl.pgid.clone())
282-
};
281+
let pgid = TtyJobCtrlManager::remove_session_tty_job_locked(&tty, None);
283282

284283
if let Some(pgrp) = pgid {
285284
let _ = crate::ipc::kill::send_signal_to_pgid(&pgrp, Signal::SIGHUP);
@@ -290,13 +289,8 @@ impl TtyCore {
290289

291290
{
292291
let mut ctrl = tty.core().contorl_info_irqsave();
293-
ctrl.session = None;
294-
ctrl.pgid = None;
295292
ctrl.pktstatus = TtyPacketStatus::empty();
296293
}
297-
if let Some(sid) = sid {
298-
TtyJobCtrlManager::session_clear_tty(sid);
299-
}
300294

301295
{
302296
let mut flags = tty.core().flags_write();
@@ -666,6 +660,8 @@ pub struct TtyCoreData {
666660
write_wq: EventWaitQueue,
667661
/// 串行化整个 tty write 调用,等价于 Linux tty->atomic_write_lock。
668662
write_lock: TtySleepLock,
663+
/// Serializes controlling-session changes with hangup.
664+
job_control_lock: TtySleepLock,
669665
/// 端口
670666
port: RwLock<Option<Arc<dyn TtyPort>>>,
671667
/// 前台进程
@@ -875,6 +871,11 @@ impl TtyCoreData {
875871
&self.write_lock
876872
}
877873

874+
#[inline]
875+
pub fn job_control_lock(&self) -> &TtySleepLock {
876+
&self.job_control_lock
877+
}
878+
878879
#[inline]
879880
pub fn contorl_info_irqsave(&self) -> SpinLockGuard<'_, TtyControlInfo> {
880881
self.ctrl.lock_irqsave()
@@ -1094,15 +1095,21 @@ impl TtyOperation for TtyCore {
10941095
let subtype = tty.core().driver().tty_driver_sub_type();
10951096
let is_pty_slave_last = cnt == 1 && subtype == TtyDriverSubType::PtySlave;
10961097
let is_pty_master_last = cnt == 1 && subtype == TtyDriverSubType::PtyMaster;
1097-
if !self.core().count_valid() || is_pty_slave_last || is_pty_master_last {
1098+
let final_release = !self.core().count_valid();
1099+
if final_release || is_pty_slave_last || is_pty_master_last {
10981100
// log::debug!(
10991101
// "TtyCore close: ref count: {}, tty: {}",
11001102
// cnt,
11011103
// tty.core().name()
11021104
// );
11031105
let r = self.core().tty_driver.driver_funcs().close(tty.clone());
1104-
// 如果计数为0或者无效,表示tty已经关闭
1105-
TtyJobCtrlManager::remove_session_tty(&tty);
1106+
// Linux pty_close() runs when the last user fd closes even though
1107+
// the peer keeps the tty structure count at one. The controlling
1108+
// session remains attached in that case and may reopen /dev/tty;
1109+
// only a true final structure release clears it here.
1110+
if final_release {
1111+
let _ = TtyJobCtrlManager::remove_session_tty(&tty);
1112+
}
11061113
return r;
11071114
}
11081115
Ok(())

kernel/src/driver/tty/tty_device.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -347,22 +347,13 @@ impl IndexNode for TtyDevice {
347347
let driver = tty.core().driver();
348348
// 考虑 O_NOCTTY:显式指定则不设置控制终端;pty master 也不会成为控制终端。
349349
if !(mode.contains(FileFlags::O_NOCTTY)
350+
|| mode.is_write_only()
350351
|| (driver.tty_driver_type() == TtyDriverType::Pty
351352
&& driver.tty_driver_sub_type() == TtyDriverSubType::PtyMaster))
352353
{
353-
let pcb = ProcessManager::current_pcb();
354-
let pcb_tty = pcb.sig_info_irqsave().tty();
355-
let is_session_leader = pcb.sig_info_irqsave().is_session_leader;
356-
let tty_session_is_none = tty.core().contorl_info_irqsave().session.is_none();
357-
// Linux tty_open_proc_set_tty 语义:必须是会话首进程、尚无 controlling tty、
358-
// tty 当前未被会话占用,且本次 open 具备读权限(不是 O_WRONLY)。
359-
if is_session_leader
360-
&& pcb_tty.is_none()
361-
&& tty_session_is_none
362-
&& !mode.is_write_only()
363-
{
364-
TtyJobCtrlManager::proc_set_tty(tty);
365-
}
354+
// The helper revalidates and commits the controlling-tty state as
355+
// one membership/tty/signal transaction.
356+
TtyJobCtrlManager::proc_set_tty(tty);
366357
}
367358

368359
Ok(())

0 commit comments

Comments
 (0)