//! Signals: asynchronous notifications to a process. //! //! A signal is a small number (SIGINT = 2, SIGSEGV = 11, ...) sent to a //! process by the kernel (a bad memory access, Ctrl+C, a child exited, a //! write to a pipe nobody reads) or by another process (`kill`). For each //! signal a process chooses what happens (`rt_sigaction`): //! * the default action -- terminate, terminate "with core dump" (we don't //! write core files, so the same), ignore, stop or continue; //! * ignore it; //! * or run a *handler*: a function of the program, called in the middle //! of whatever it was doing, like a hardware interrupt but in user space. //! It can also *block* signals for a while (`rt_sigprocmask`): they stay //! pending until unblocked. SIGKILL and SIGSTOP can't be caught, blocked or //! ignored -- that's how you get rid of any process. //! //! **When is a signal acted on?** Sending only sets a bit in the target's //! `pending` set (and wakes it if it sleeps). The target acts on it the //! next time it returns from the kernel to user mode: after a syscall, a //! page fault, or a timer interrupt (`deliver`, called from trap.rs's //! return paths). A process blocked in a syscall (reading the keyboard, //! waiting for a child) is woken up; the syscall gives up with EINTR, or is //! transparently restarted if the handler was installed with SA_RESTART. //! //! **Running a handler** (`setup_frame`): we push a *signal frame* on the //! user's stack holding all its registers (`ucontext`), the blocked mask //! and a `siginfo` (who sent it, which address faulted...), then change the //! saved registers so that "returning" to user mode lands in the handler: //! //! rip = handler, rdi = signal number, rsi = &siginfo, rdx = &ucontext //! rsp = the frame, whose first word is the return address: `restorer` //! //! The restorer is a two-instruction function in the C library (`mov rax, //! 15; syscall` = rt_sigreturn). When the handler returns, it runs, and //! rt_sigreturn copies the saved registers back from the frame: the program //! continues exactly where it was interrupted. The frame layout is Linux's //! `struct rt_sigframe`, so real libcs (musl) will work unchanged. //! //! **Stop and continue** (job control): SIGSTOP / SIGTSTP (Ctrl+Z) stop a //! process -- its threads just sleep -- until SIGCONT. The parent learns of //! it from `wait4(WUNTRACED)` and SIGCHLD. //! //! **Threads.** Handlers (and so what a signal *does*) belong to the //! process, but each thread has its own *mask* and its own pending set. //! `kill` and Ctrl+C send to the process: whichever thread doesn't block //! it takes it. A fault (SIGSEGV) or `tgkill` targets one thread. A fatal //! signal kills all threads; a stop stops them all. use crate::errno::Errno; use crate::process::{self, ExitStatus, Process, Task}; use crate::thread::{self, WaitQueue}; use crate::trap::TrapFrame; use alloc::sync::Arc; use alloc::vec::Vec; use x86_64::instructions::interrupts::without_interrupts; pub const SIGHUP: u32 = 1; pub const SIGINT: u32 = 2; pub const SIGQUIT: u32 = 3; pub const SIGILL: u32 = 4; pub const SIGTRAP: u32 = 5; pub const SIGABRT: u32 = 6; pub const SIGBUS: u32 = 7; pub const SIGFPE: u32 = 8; pub const SIGKILL: u32 = 9; pub const SIGUSR1: u32 = 10; pub const SIGSEGV: u32 = 11; pub const SIGUSR2: u32 = 12; pub const SIGPIPE: u32 = 13; pub const SIGALRM: u32 = 14; pub const SIGTERM: u32 = 15; pub const SIGCHLD: u32 = 17; pub const SIGCONT: u32 = 18; pub const SIGSTOP: u32 = 19; pub const SIGTSTP: u32 = 20; pub const SIGTTIN: u32 = 21; pub const SIGTTOU: u32 = 22; pub const SIGURG: u32 = 23; pub const SIGXCPU: u32 = 24; pub const SIGXFSZ: u32 = 25; pub const SIGWINCH: u32 = 28; pub const SIGSYS: u32 = 31; /// Signals 1..=64 (32..64 are the "real-time" ones: no default meaning). pub const NSIG: u32 = 64; /// Names for `kill -l`, messages and the shell. pub fn name(sig: u32) -> &'static str { const NAMES: [&str; 32] = [ "SIG0", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO", "SIGPWR", "SIGSYS", ]; NAMES.get(sig as usize).copied().unwrap_or("SIGRT") } /// What happens to a signal nobody installed a handler for. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DefaultAction { Terminate, /// Terminate + write a core file (we don't write one). Core, Ignore, Stop, Continue, } pub fn default_action(sig: u32) -> DefaultAction { use DefaultAction::*; match sig { SIGCHLD | SIGURG | SIGWINCH => Ignore, SIGCONT => Continue, SIGSTOP | SIGTSTP | SIGTTIN | SIGTTOU => Stop, SIGQUIT | SIGILL | SIGTRAP | SIGABRT | SIGBUS | SIGFPE | SIGSEGV | SIGXCPU | SIGXFSZ | SIGSYS => Core, _ => Terminate, } } // sigaction handler values and flags (Linux's). pub const SIG_DFL: u64 = 0; pub const SIG_IGN: u64 = 1; pub const SA_NOCLDSTOP: u64 = 0x1; pub const SA_NOCLDWAIT: u64 = 0x2; pub const SA_SIGINFO: u64 = 0x4; pub const SA_RESTORER: u64 = 0x0400_0000; pub const SA_ONSTACK: u64 = 0x0800_0000; pub const SA_RESTART: u64 = 0x1000_0000; pub const SA_NODEFER: u64 = 0x4000_0000; pub const SA_RESETHAND: u64 = 0x8000_0000; /// Every flag we act on. rt_sigaction stores only these (Linux 5.11+ does /// the same), so a program can set e.g. SA_UNSUPPORTED 0x400 and read the /// action back to learn which flags this kernel knows. pub const SA_KNOWN: u64 = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_RESTORER | SA_ONSTACK | SA_RESTART | SA_NODEFER | SA_RESETHAND; // sigaltstack's ss_flags (stage 16). pub const SS_ONSTACK: u32 = 1; // (read only) we're running on it right now pub const SS_DISABLE: u32 = 2; // there is none pub const SS_AUTODISARM: u32 = 1 << 31; // forget it while a handler runs on it /// The smallest alternate stack sigaltstack accepts (ENOMEM below). pub const MINSIGSTKSZ: u64 = 2048; /// First real-time signal. Real-time signals queue: sending one 3 times /// delivers it 3 times, each with its own siginfo (stage 16). pub const SIGRTMIN: u32 = 32; /// At most this many queued signals per pending set (sigqueue: EAGAIN beyond). const MAX_QUEUED: usize = 1024; // rt_sigprocmask `how`. pub const SIG_BLOCK: u64 = 0; pub const SIG_UNBLOCK: u64 = 1; pub const SIG_SETMASK: u64 = 2; // siginfo codes. pub const SI_USER: i32 = 0; pub const SI_KERNEL: i32 = 0x80; pub const SI_QUEUE: i32 = -1; // sigqueue() pub const SI_TKILL: i32 = -6; // tkill / tgkill pub const SEGV_MAPERR: i32 = 1; // no mapping at that address pub const SEGV_ACCERR: i32 = 2; // mapped, but not allowed (e.g. write to read-only) pub const CLD_EXITED: i32 = 1; pub const CLD_KILLED: i32 = 2; pub const CLD_DUMPED: i32 = 3; pub const CLD_STOPPED: i32 = 5; pub const CLD_CONTINUED: i32 = 6; /// The set bit for signal `sig` in a 64-bit signal set (signal 1 = bit 0). pub const fn bit(sig: u32) -> u64 { 1 << (sig - 1) } /// Never blockable. const UNBLOCKABLE: u64 = bit(SIGKILL) | bit(SIGSTOP); /// The kernel's `struct sigaction` (what rt_sigaction reads and writes; /// the field order differs from the libc one). #[repr(C)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub struct SigAction { pub handler: u64, pub flags: u64, pub restorer: u64, pub mask: u64, } /// Linux's `siginfo_t`: 128 bytes. Which fields follow the first three /// depends on the signal: kill() -> sender pid + uid; SIGCHLD -> child pid, /// uid, exit status; SIGSEGV -> faulting address. #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct SigInfo { pub signo: i32, pub errno: i32, pub code: i32, _pad: i32, /// si_pid (i32) + si_uid (u32), or si_addr; then si_status (i32). pub fields: [u64; 14], } impl SigInfo { pub fn new(sig: u32, code: i32) -> SigInfo { SigInfo { signo: sig as i32, errno: 0, code, _pad: 0, fields: [0; 14] } } /// Sent by process `pid` with kill(). pub fn from_pid(sig: u32, pid: u64) -> SigInfo { let mut info = SigInfo::new(sig, if pid == 0 { SI_KERNEL } else { SI_USER }); info.fields[0] = pid & 0xffff_ffff; // si_pid, si_uid = 0 (root) info } /// A fault at `addr` (SIGSEGV, SIGBUS, SIGFPE, SIGILL). pub fn fault(sig: u32, code: i32, addr: u64) -> SigInfo { let mut info = SigInfo::new(sig, code); info.fields[0] = addr; info } /// SIGCHLD: child `pid` exited / was killed / stopped / continued. pub fn child(code: i32, pid: u64, status: i32) -> SigInfo { let mut info = SigInfo::new(SIGCHLD, code); info.fields[0] = pid & 0xffff_ffff; info.fields[1] = status as u32 as u64; info } } /// What the parent's wait4(WUNTRACED / WCONTINUED) can report besides exits. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Report { Stopped(u32), Continued, } /// Pending signals with their siginfo. Standard signals (1-31) don't /// queue: one pending instance each, like Linux. Real-time ones (32-64) /// do, in the order they were sent (stage 16). #[derive(Default)] pub struct Pending { pub set: u64, info: Vec<(u32, SigInfo)>, } impl Pending { /// Mark `sig` pending with this siginfo. False if the queue is full /// (a real-time signal that couldn't be queued). fn raise(&mut self, sig: u32, info: SigInfo) -> bool { if sig < SIGRTMIN { self.info.retain(|(s, _)| *s != sig); } else if self.info.len() >= MAX_QUEUED { return false; } self.set |= bit(sig); self.info.push((sig, info)); true } /// Take the lowest signal of `allowed` (SIGKILL first); for a queued /// real-time signal, its oldest instance. fn take(&mut self, allowed: u64) -> Option<(u32, SigInfo)> { let set = self.set & allowed; if set == 0 { return None; } let sig = if set & bit(SIGKILL) != 0 { SIGKILL } else { set.trailing_zeros() + 1 }; let info = match self.info.iter().position(|(s, _)| *s == sig) { Some(i) => self.info.remove(i).1, None => SigInfo::new(sig, SI_KERNEL), }; if !self.info.iter().any(|(s, _)| *s == sig) { self.set &= !bit(sig); } Some((sig, info)) } fn discard(&mut self, mask: u64) { self.set &= !mask; self.info.retain(|(s, _)| bit(*s) & mask == 0); } } /// A thread's alternate signal stack (sigaltstack, stage 16): handlers /// installed with SA_ONSTACK run on it -- the way a program can still /// handle SIGSEGV after overflowing its normal stack. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AltStack { pub sp: u64, // lowest address pub size: u64, // bytes /// SS_DISABLE, or 0 / SS_AUTODISARM for a usable one. pub flags: u32, } impl AltStack { pub const DISABLED: AltStack = AltStack { sp: 0, size: 0, flags: SS_DISABLE }; fn enabled(&self) -> bool { self.flags & SS_DISABLE == 0 } /// Is `sp` on it? (Linux's test: the stack grows down from sp + size.) pub fn contains(&self, sp: u64) -> bool { self.enabled() && sp > self.sp && sp - self.sp <= self.size } /// stack_t.ss_flags as sigaltstack reports it for a thread at `sp`. fn report_flags(&self, sp: u64) -> u32 { if !self.enabled() { SS_DISABLE } else if self.contains(sp) { SS_ONSTACK | (self.flags & SS_AUTODISARM) } else { self.flags & SS_AUTODISARM } } } /// One thread's signal state (`Task::sig`, locked with interrupts off). pub struct ThreadSig { pub blocked: u64, /// Signals sent to this thread only (faults, tgkill). pub pending: Pending, /// Why the kernel forced a signal (a fault), for the kill message. reason: Option<&'static str>, /// The real mask while ppoll/pselect6/epoll_pwait run with a temporary /// one (see `set_temporary_mask`). saved_mask: Option, /// sigaltstack (stage 16). pub altstack: AltStack, } impl ThreadSig { pub fn new(blocked: u64) -> ThreadSig { ThreadSig::with_altstack(blocked, AltStack::DISABLED) } pub fn with_altstack(blocked: u64, altstack: AltStack) -> ThreadSig { ThreadSig { blocked, pending: Pending::default(), reason: None, saved_mask: None, altstack } } /// The signals this thread may take now. fn allowed(&self) -> u64 { !(self.blocked & !UNBLOCKABLE) } } /// A process's signal state. Locked with interrupts off (`Process::signals`). pub struct SignalState { /// Signals sent to the whole process (kill, Ctrl+C, SIGCHLD). pub pending: Pending, actions: [SigAction; NSIG as usize], pub stopped: bool, /// A stop/continue the parent hasn't collected with wait4 yet. pub report: Option, } impl SignalState { pub fn new() -> SignalState { SignalState { pending: Pending::default(), actions: [SigAction::default(); NSIG as usize], stopped: false, report: None } } /// fork: the child inherits the handlers (and, see process.rs, the /// calling thread's mask), but no pending signals. pub fn fork(&self) -> SignalState { SignalState { actions: self.actions, ..SignalState::new() } } /// execve: the program with the handlers is gone, so caught signals go /// back to the default. Ignored ones stay ignored (that's how `nohup` /// works), and so do the mask and pending signals. pub fn exec(&mut self) { for action in self.actions.iter_mut() { if action.handler != SIG_IGN { *action = SigAction::default(); } } } pub fn action(&self, sig: u32) -> SigAction { self.actions[sig as usize - 1] } /// Would this signal be thrown away right now? fn ignored(&self, sig: u32) -> bool { let handler = self.action(sig).handler; handler == SIG_IGN || (handler == SIG_DFL && matches!(default_action(sig), DefaultAction::Ignore | DefaultAction::Continue)) } } /// Threads of a stopped process wait here for SIGCONT / SIGKILL. static STOPPED: WaitQueue = WaitQueue::new(); /// Make stopped threads look at their state again (process.rs: a thread /// group is exiting). pub fn wake_stopped() { STOPPED.wake_all(); } /// Signals that make a stopped process continue / that stop it. const STOP_SIGNALS: u64 = bit(SIGSTOP) | bit(SIGTSTP) | bit(SIGTTIN) | bit(SIGTTOU); /// Send signal `sig` to `p`. Returns at once; `p` acts on it on its way /// back to user mode. pub fn send(p: &Arc, sig: u32, info: SigInfo) { if p.status().is_some() || !(1..=NSIG).contains(&sig) { return; // already exited (a zombie): nothing to do } let tasks = p.tasks(); let continued = without_interrupts(|| { let mut s = p.signals.lock(); let mut continued = false; if sig == SIGCONT || sig == SIGKILL { // Continue (even if SIGCONT itself is ignored or blocked), and // forget pending stops. s.pending.discard(STOP_SIGNALS); for t in &tasks { t.sig.lock().pending.discard(STOP_SIGNALS); } if s.stopped { s.stopped = false; s.report = Some(Report::Continued); continued = true; } } if default_action(sig) == DefaultAction::Stop { s.pending.discard(bit(SIGCONT)); // a stop cancels a pending continue } // Ignored signals are dropped right away -- unless blocked: the // program may unblock it after installing a handler. let blocked = tasks.iter().any(|t| t.sig.lock().blocked & bit(sig) != 0); if sig == SIGKILL || !s.ignored(sig) || blocked { s.pending.raise(sig, info); } continued }); if continued { STOPPED.wake_all(); notify_parent(p, CLD_CONTINUED, SIGCONT as i32); } // If it's blocked in a syscall, wake it so it notices (see // thread::WaitQueue::wait_until_intr). If it's running, it'll notice at // its next return to user mode -- at the latest at the next timer tick. p.wake(); STOPPED.wake_all(); // a stopped one needs to see SIGKILL } /// Send `sig` to one thread (tgkill). Its effect (handler, death, stop) /// is still the process's; only who takes it differs. pub fn send_thread(t: &Task, sig: u32, info: SigInfo) { if t.process.status().is_some() || !(1..=NSIG).contains(&sig) { return; } if sig == SIGKILL || sig == SIGCONT || default_action(sig) == DefaultAction::Stop { // These act on the whole process anyway. if let Some(p) = process::get(t.process.pid) { return send(&p, sig, info); } } without_interrupts(|| { let s = t.process.signals.lock(); let mut ts = t.sig.lock(); if !s.ignored(sig) || ts.blocked & bit(sig) != 0 { ts.pending.raise(sig, info); } }); t.wake(); } /// Send to every process in process group `pgid`. ESRCH if it's empty. pub fn send_group(pgid: u64, sig: u32, info: SigInfo) -> Result<(), Errno> { let members = process::group_members(pgid); if members.is_empty() { return Err(Errno::ESRCH); } for p in &members { send(p, sig, info); } Ok(()) } /// The current process caused a fault (SIGSEGV, SIGFPE, SIGILL, SIGBUS). /// It can't be ignored or blocked -- the program can't just continue at /// the faulting instruction -- so if it is, the default action (death) /// applies, like Linux's force_sig. pub fn force(sig: u32, info: SigInfo, reason: &'static str) { let Some(t) = thread::current_task() else { return }; without_interrupts(|| { let mut s = t.process.signals.lock(); let mut ts = t.sig.lock(); if s.action(sig).handler == SIG_IGN || ts.blocked & bit(sig) != 0 { s.actions[sig as usize - 1] = SigAction::default(); ts.blocked &= !bit(sig); } ts.pending.raise(sig, info); ts.reason = Some(reason); }); } /// Should the current thread stop sleeping in a syscall: a signal it can /// take, its process stopping, or being told to exit? (Called from /// thread.rs with interrupts off.) pub fn interrupted() -> bool { let Some(t) = thread::current_task() else { return false }; // kernel threads don't get signals if t.killed.load(core::sync::atomic::Ordering::Relaxed) || t.process.exiting().is_some() { return true; } without_interrupts(|| { let s = t.process.signals.lock(); let ts = t.sig.lock(); s.stopped || (s.pending.set | ts.pending.set) & ts.allowed() != 0 }) } /// Tell `child`'s parent that it stopped/continued/exited: wake its wait4 /// and send it SIGCHLD. pub fn notify_parent(child: &Process, code: i32, status: i32) { let Some(parent) = process::get(child.parent()) else { return }; parent.child_wait.wake_all(); let nocldstop = without_interrupts(|| parent.signals.lock().action(SIGCHLD).flags & SA_NOCLDSTOP != 0); if nocldstop && (code == CLD_STOPPED || code == CLD_CONTINUED) { return; } send(&parent, SIGCHLD, SigInfo::child(code, child.pid, status)); } /// The system-call results that mean "interrupted by a signal". fn restart_code(rax: u64) -> Option { match Errno::from_ret(rax as i64) { Some(e @ (Errno::ERESTARTSYS | Errno::ERESTARTNOHAND)) => Some(e), _ => None, } } /// Make the interrupted syscall run again when we return to user mode: /// restore its number and back up rip over the 2-byte `syscall` instruction. fn restart(frame: &mut TrapFrame) { frame.rax = frame.error; // the syscall number (see trap.rs) frame.rip -= 2; } /// On the way back to user mode: act on pending signals. Runs handlers /// (by rewriting `frame`), applies default actions (which may end the /// process right here), and sorts out interrupted syscalls. pub fn deliver(frame: &mut TrapFrame) { deliver_outcome(frame); } /// `deliver`, also saying what became of an interrupted syscall (for the /// `--trace` line, stage 17): `Some(true)` = it will run again, /// `Some(false)` = the program sees EINTR, `None` = nothing was interrupted. pub fn deliver_outcome(frame: &mut TrapFrame) -> Option { if !frame.from_user() { return None; } let Some(t) = thread::current_task() else { return None }; let mut outcome = None; let p = t.process.clone(); // Writing the signal frame may fault user pages in: interrupts on. x86_64::instructions::interrupts::enable(); loop { // Another thread called exit_group / execve, or died of a signal. if t.killed.load(core::sync::atomic::Ordering::Acquire) || p.exiting().is_some() { drop((t, p)); process::exit_thread(ExitStatus::Exited(0)); } // Another thread took a stop signal: we stop too. if without_interrupts(|| p.signals.lock().stopped) { wait_while_stopped(&t); continue; } let next = without_interrupts(|| { let mut s = p.signals.lock(); let mut ts = t.sig.lock(); let allowed = ts.allowed(); let (sig, info) = ts.pending.take(allowed).or_else(|| s.pending.take(allowed))?; Some((sig, info, s.action(sig), ts.reason.take())) }); let Some((sig, mut info, action, reason)) = next else { break }; crate::timer::delivered(p.pid, &mut info); // a POSIX timer's: latch its overrun count match action.handler { SIG_IGN if sig != SIGKILL && sig != SIGSTOP => continue, SIG_IGN | SIG_DFL => match default_action(sig) { DefaultAction::Ignore | DefaultAction::Continue => continue, DefaultAction::Stop if sig == SIGKILL => unreachable!(), DefaultAction::Stop => stop(&t, sig), DefaultAction::Terminate | DefaultAction::Core => { let why = reason.unwrap_or(name(sig)); drop((t, p)); process::exit_group(ExitStatus::Killed(sig, why)); } }, _ => { // A handler. First the interrupted syscall: give up with // EINTR, or restart it after the handler (SA_RESTART). if frame.is_syscall() { match restart_code(frame.rax) { Some(Errno::ERESTARTSYS) if action.flags & SA_RESTART != 0 => { restart(frame); outcome = Some(true); } Some(_) => { frame.rax = -(Errno::EINTR as i64) as u64; outcome = Some(false); } None => {} } frame.vector = 0; // dealt with: not a syscall return any more } if setup_frame(&t, frame, sig, &info, &action).is_err() { // Couldn't write the frame (stack overflowed or bad // restorer): like Linux, the process dies of SIGSEGV. drop((t, p)); process::exit_group(ExitStatus::Killed(SIGSEGV, "bad signal frame")); } without_interrupts(|| { let mut s = p.signals.lock(); let mut ts = t.sig.lock(); ts.saved_mask = None; // (it's in the frame: sigreturn restores it) ts.blocked |= action.mask & !UNBLOCKABLE; if action.flags & SA_NODEFER == 0 { ts.blocked |= bit(sig); // no nesting of the same signal } if action.flags & SA_RESETHAND != 0 { s.actions[sig as usize - 1] = SigAction::default(); } }); return outcome; // one handler at a time; the next on its return } } } // No handler ran: the temporary mask of a ppoll & co. can go, and an // interrupted syscall simply runs again. restore_temporary_mask(); if frame.is_syscall() && restart_code(frame.rax).is_some() { restart(frame); outcome = Some(true); } outcome } /// ppoll / pselect6 / epoll_pwait / (sigsuspend-like) waits: block exactly /// `mask` until `restore_temporary_mask`. If a signal interrupts the wait, /// the syscall leaves the temporary mask in place and `deliver` restores /// the real one -- after the handler, through the signal frame -- so the /// signal that ended the wait can't be blocked again before it's handled. pub fn set_temporary_mask(mask: u64) { if let Some(t) = thread::current_task() { without_interrupts(|| { let mut ts = t.sig.lock(); let old = ts.blocked; ts.saved_mask.get_or_insert(old); ts.blocked = mask & !UNBLOCKABLE; }); } } /// Put the real mask back (no-op without a temporary one). pub fn restore_temporary_mask() { if let Some(t) = thread::current_task() { without_interrupts(|| { let mut ts = t.sig.lock(); if let Some(mask) = ts.saved_mask.take() { ts.blocked = mask; } }); } } /// Stop the current process (all its threads) until SIGCONT (or SIGKILL). fn stop(t: &Arc, sig: u32) { let p = &t.process; without_interrupts(|| { let mut s = p.signals.lock(); s.stopped = true; s.report = Some(Report::Stopped(sig)); }); crate::serial_println!("[signal] pid {} stopped by {}", p.pid, name(sig)); p.wake(); // the other threads stop in deliver notify_parent(p, CLD_STOPPED, sig as i32); wait_while_stopped(t); } /// Sleep until the process is continued, killed, or told to exit. fn wait_while_stopped(t: &Task) { let p = &t.process; STOPPED.wait_until(|| { let s = p.signals.lock(); let killed = (s.pending.set | t.sig.lock().pending.set) & bit(SIGKILL) != 0; let exiting = t.killed.load(core::sync::atomic::Ordering::Relaxed) || p.exiting().is_some(); (!s.stopped || killed || exiting).then_some(()) }); } // --------------------------------------------------------------------------- // The signal frame (Linux's struct rt_sigframe, x86_64) // --------------------------------------------------------------------------- /// The saved registers (`struct sigcontext`; this order is Linux's ABI). #[repr(C)] #[derive(Clone, Copy, Default)] struct SigContext { r8: u64, r9: u64, r10: u64, r11: u64, r12: u64, r13: u64, r14: u64, r15: u64, rdi: u64, rsi: u64, rbp: u64, rbx: u64, rdx: u64, rax: u64, rcx: u64, rsp: u64, rip: u64, eflags: u64, cs_gs_fs_ss: u64, // four 16-bit selectors err: u64, trapno: u64, oldmask: u64, cr2: u64, fpstate: u64, // pointer to the saved FPU/SSE/AVX state (below the frame); 0 = none reserved: [u64; 8], } #[repr(C)] #[derive(Clone, Copy, Default)] struct UContext { flags: u64, link: u64, stack: [u64; 3], // stack_t: ss_sp, ss_flags (+pad), ss_size mcontext: SigContext, sigmask: u64, } #[repr(C)] #[derive(Clone, Copy)] struct RtSigFrame { /// Return address of the handler: the restorer (-> rt_sigreturn). pretcode: u64, uc: UContext, info: SigInfo, } /// User-settable RFLAGS bits (carry, parity, aux, zero, sign, trap, /// direction, overflow, alignment check, resume). The rest (interrupt /// enable, I/O privilege level...) only the kernel decides. const USER_RFLAGS: u64 = 0x0004_0DD5 | (1 << 16); // The FPU/SSE/AVX state in a signal frame (Linux's layout, stage 15.1). // It sits above the rt_sigframe, 64-byte aligned, and // `uc.mcontext.fpstate` points at it. Its first 512 bytes are the FXSAVE // format; bytes 464..512 (which FXSAVE leaves unused) hold // `struct _fpx_sw_bytes`, saying whether an XSAVE area follows: // // u32 magic1 = FP_XSTATE_MAGIC1 u32 extended_size (state + 4) // u64 xfeatures (= XCR0) u32 xstate_size + 7 u32 padding // // and FP_XSTATE_MAGIC2 is stored right after the XSAVE area. With only // FXSAVE, magic1 is 0. A handler may read and even change the saved // values: sigreturn loads whatever is there (after checking it). const FP_XSTATE_MAGIC1: u32 = 0x4650_5853; const FP_XSTATE_MAGIC2: u32 = 0x4650_5845; const SW_BYTES: usize = 464; /// uc_flags: an XSAVE area is there (UC_FP_XSTATE), and the two flags /// Linux always sets on x86_64 (UC_SIGCONTEXT_SS | UC_STRICT_RESTORE_SS). const UC_FP_XSTATE: u64 = 1; const UC_SS_FLAGS: u64 = 2 | 4; /// Bytes the FPU state takes in a signal frame. fn fpstate_size() -> u64 { if crate::fpu::uses_xsave() { crate::fpu::size() as u64 + 4 } else { 512 } } /// Push a signal frame on the user stack and point `frame` at the handler. /// The stack, from higher to lower addresses: /// /// [interrupted code's stack] [128-byte red zone] [FPU state, 64-aligned] /// [rt_sigframe: pretcode, ucontext, siginfo] <- rsp of the handler fn setup_frame(t: &Task, frame: &mut TrapFrame, sig: u32, info: &SigInfo, action: &SigAction) -> Result<(), ()> { if action.flags & SA_RESTORER == 0 || action.restorer == 0 { return Err(()); // nowhere to return to (x86_64 requires a restorer) } let size = core::mem::size_of::() as u64; // Below the 128-byte "red zone" (leaf functions may use it without // moving rsp) goes the FPU state, then the frame, aligned so the // handler starts with rsp % 16 == 8, as if it had been called. let fp_size = fpstate_size(); // The mask sigreturn restores: the real one, if a ppoll & co. had // swapped in a temporary mask. And the sigaltstack (stage 16): an // SA_ONSTACK handler starts at its top -- unless we're on it already // (a nested signal), then we just keep going down it. let (blocked, alt) = without_interrupts(|| { let mut ts = t.sig.lock(); let alt = ts.altstack; if alt.flags & SS_AUTODISARM != 0 { ts.altstack = AltStack::DISABLED; // sigreturn puts it back (from uc_stack) } (ts.saved_mask.unwrap_or(ts.blocked), alt) }); let on_alt = action.flags & SA_ONSTACK != 0 && alt.enabled() && !alt.contains(frame.rsp); let top = if on_alt { alt.sp + alt.size } else { frame.rsp.wrapping_sub(128) }; let fp = (top.wrapping_sub(fp_size)) & !63; let sp = ((fp.wrapping_sub(size)) & !0xf).wrapping_sub(8); // uc_stack: the sigaltstack as it was (sigreturn restores it from here). let uc_stack = [alt.sp, alt.report_flags(frame.rsp) as u64, alt.size]; let s = crate::gdt::selectors(); let mcontext = SigContext { r8: frame.r8, r9: frame.r9, r10: frame.r10, r11: frame.r11, r12: frame.r12, r13: frame.r13, r14: frame.r14, r15: frame.r15, rdi: frame.rdi, rsi: frame.rsi, rbp: frame.rbp, rbx: frame.rbx, rdx: frame.rdx, rax: frame.rax, rcx: frame.rcx, rsp: frame.rsp, rip: frame.rip, eflags: frame.rflags, cs_gs_fs_ss: s.user_code.0 as u64 | (s.user_data.0 as u64) << 48, err: if frame.vector == 14 { frame.error } else { 0 }, trapno: frame.vector, oldmask: blocked, cr2: if sig == SIGSEGV || sig == SIGBUS { info.fields[0] } else { 0 }, fpstate: fp, ..Default::default() }; let xsave = crate::fpu::uses_xsave(); let rt = RtSigFrame { pretcode: action.restorer, uc: UContext { flags: UC_SS_FLAGS | if xsave { UC_FP_XSTATE } else { 0 }, stack: uc_stack, mcontext, sigmask: blocked, ..Default::default() }, info: *info, }; if !crate::mm::user_access_ok(sp, fp + fp_size - sp, true) { return Err(()); } // The FPU registers right now are still the interrupted code's (the // kernel never touches them): save them into the frame. let mut state = crate::fpu::save_live(); let n = crate::fpu::size(); let sw = &mut state.0[SW_BYTES..512]; sw.fill(0); if xsave { sw[0..4].copy_from_slice(&FP_XSTATE_MAGIC1.to_le_bytes()); sw[4..8].copy_from_slice(&(n as u32 + 4).to_le_bytes()); sw[8..16].copy_from_slice(&crate::fpu::xfeatures().to_le_bytes()); sw[16..20].copy_from_slice(&(n as u32).to_le_bytes()); state.0[n..n + 4].copy_from_slice(&FP_XSTATE_MAGIC2.to_le_bytes()); } unsafe { core::ptr::copy_nonoverlapping(state.0.as_ptr(), fp as *mut u8, fp_size as usize); core::ptr::write_unaligned(sp as *mut RtSigFrame, rt); } // Like Linux, the handler starts with clean FPU/SSE registers (MXCSR at // its default, so its float maths can't trap because the interrupted // code had unmasked an exception). crate::fpu::reset_live(); frame.rsp = sp; frame.rip = action.handler; frame.rdi = sig as u64; frame.rsi = sp + core::mem::offset_of!(RtSigFrame, info) as u64; frame.rdx = sp + core::mem::offset_of!(RtSigFrame, uc) as u64; frame.rax = 0; frame.rflags &= !(1 << 8 | 1 << 10); // no single-step, direction flag clear Ok(()) } /// rt_sigreturn (15): the handler returned into the restorer. The frame /// starts 8 bytes below rsp (`ret` popped `pretcode`). Put back the saved /// registers and mask -- after checking them: the program may have changed /// the frame (that's allowed and used), but must not get kernel privileges /// or a kernel rip out of it. pub fn sys_rt_sigreturn(frame: &mut TrapFrame) { let sp = frame.rsp.wrapping_sub(8); let size = core::mem::size_of::() as u64; if !crate::mm::user_access_ok(sp, size, false) { process::exit_group(ExitStatus::Killed(SIGSEGV, "bad sigreturn frame")); } let rt: RtSigFrame = unsafe { core::ptr::read_unaligned(sp as *const RtSigFrame) }; let m = rt.uc.mcontext; let user = crate::memory::USER_START..crate::memory::USER_END; if !user.contains(&m.rip) || !(user.start..=user.end).contains(&m.rsp) { process::exit_group(ExitStatus::Killed(SIGSEGV, "bad sigreturn frame")); } let s = crate::gdt::selectors(); *frame = TrapFrame { r8: m.r8, r9: m.r9, r10: m.r10, r11: m.r11, r12: m.r12, r13: m.r13, r14: m.r14, r15: m.r15, rdi: m.rdi, rsi: m.rsi, rbp: m.rbp, rbx: m.rbx, rdx: m.rdx, rax: m.rax, rcx: m.rcx, rsp: m.rsp, rip: m.rip, rflags: 0x202 | (m.eflags & USER_RFLAGS), cs: s.user_code.0 as u64, ss: s.user_data.0 as u64, vector: 0, // not a syscall return: rax is the restored value error: 0, }; if let Some(t) = thread::current_task() { without_interrupts(|| { let mut ts = t.sig.lock(); ts.blocked = rt.uc.sigmask & !UNBLOCKABLE; // Put the sigaltstack back as uc_stack says, like Linux (that's // how SS_AUTODISARM re-arms; errors, e.g. still on it, are ignored). let [ss_sp, ss_flags, ss_size] = rt.uc.stack; let _ = set_altstack(&mut ts, ss_sp, ss_flags as u32, ss_size, frame.rsp); }); } if restore_fpstate(m.fpstate).is_err() { process::exit_group(ExitStatus::Killed(SIGSEGV, "bad sigreturn FPU state")); } } /// sigreturn's FPU part: load the state `fpstate` points to (0 = clean /// registers, like Linux). An XSAVE area is used only if both magic numbers /// are there; otherwise just the 512-byte FXSAVE part (the AVX halves are /// then cleared). Err if it's unreadable or has an impossible MXCSR. fn restore_fpstate(fpstate: u64) -> Result<(), ()> { use crate::fpu; if fpstate == 0 { fpu::reset_live(); return Ok(()); } let mut state = fpu::FpuState([0; fpu::AREA_SIZE]); let legacy = crate::syscall::user_slice(fpstate, 512).map_err(|_| ())?; state.0[..512].copy_from_slice(legacy); let word = |b: &[u8], at: usize| u32::from_le_bytes(b[at..at + 4].try_into().unwrap()); let n = fpu::size(); let mut extended = false; if fpu::uses_xsave() && word(&state.0, SW_BYTES) == FP_XSTATE_MAGIC1 && word(&state.0, SW_BYTES + 16) as usize == n { if let Ok(all) = crate::syscall::user_slice(fpstate, n as u64 + 4) { if word(all, n) == FP_XSTATE_MAGIC2 { state.0[..n].copy_from_slice(&all[..n]); extended = true; } } } if fpu::uses_xsave() && !extended { // An FXSAVE-only frame: x87 + SSE from it, every other feature // back to its initial state (XSAVE header: just bits 0 and 1). state.0[512..fpu::AREA_SIZE].fill(0); state.0[512..520].copy_from_slice(&(fpu::XFEATURE_X87 | fpu::XFEATURE_SSE).to_le_bytes()); } if !fpu::sanitize(&mut state) { return Err(()); } fpu::restore(&state); Ok(()) } // --------------------------------------------------------------------------- // Syscalls // --------------------------------------------------------------------------- fn check_sig(sig: u64) -> Result { if (1..=NSIG as u64).contains(&sig) { Ok(sig as u32) } else { Err(Errno::EINVAL) } } /// rt_sigaction(sig, *new, *old, sigsetsize = 8). pub fn sys_rt_sigaction(p: &Process, sig: u64, new: Option, set_size: u64) -> Result { let sig = check_sig(sig)?; if set_size != 8 || (new.is_some() && (sig == SIGKILL || sig == SIGSTOP)) { return Err(Errno::EINVAL); } Ok(without_interrupts(|| { let mut s = p.signals.lock(); let old = s.action(sig); if let Some(mut new) = new { new.mask &= !UNBLOCKABLE; new.flags &= SA_KNOWN; // (see SA_KNOWN) s.actions[sig as usize - 1] = new; // Setting a signal to "ignored" discards it if pending (POSIX). if s.ignored(sig) { s.pending.discard(bit(sig)); } } old })) } /// rt_sigprocmask(how, *set, *old, sigsetsize = 8): the calling thread's /// mask. Returns the old mask. pub fn sys_rt_sigprocmask(t: &Task, how: u64, set: Option, set_size: u64) -> Result { if set_size != 8 { return Err(Errno::EINVAL); } without_interrupts(|| { let mut s = t.sig.lock(); let old = s.blocked; if let Some(set) = set { s.blocked = match how { SIG_BLOCK => old | set, SIG_UNBLOCK => old & !set, SIG_SETMASK => set, _ => return Err(Errno::EINVAL), } & !UNBLOCKABLE; } Ok(old) }) } /// kill(pid, sig): pid > 0 one process, 0 = my process group, -1 = every /// process but me, < -1 = process group -pid. sig 0 = just check it exists. pub fn sys_kill(me: &Process, pid: i64, sig: u64) -> Result { if sig > NSIG as u64 { return Err(Errno::EINVAL); } let sig = sig as u32; let targets: Vec> = match pid { 0 => process::group_members(me.pgid()), -1 => process::all().into_iter().filter(|p| p.pid != me.pid).collect(), p if p < -1 => process::group_members(p.unsigned_abs()), p => process::get(p as u64).into_iter().collect(), }; let targets: Vec<_> = targets.into_iter().filter(|p| p.status().is_none()).collect(); if targets.is_empty() { return Err(Errno::ESRCH); } if sig != 0 { for t in &targets { send(t, sig, SigInfo::from_pid(sig, me.pid)); } } Ok(0) } // --- Stage 16: the rest of the rt_sig* family, and sigaltstack ------------- /// Change a thread's sigaltstack (sigaltstack's "new" half, also used by /// sigreturn). `user_sp`: where the thread's stack pointer is. fn set_altstack(ts: &mut ThreadSig, ss_sp: u64, ss_flags: u32, ss_size: u64, user_sp: u64) -> Result<(), Errno> { if ts.altstack.contains(user_sp) { return Err(Errno::EPERM); // can't change it while running on it } match ss_flags & !SS_AUTODISARM { SS_DISABLE => ts.altstack = AltStack::DISABLED, 0 | SS_ONSTACK => { if ss_size < MINSIGSTKSZ { return Err(Errno::ENOMEM); } ts.altstack = AltStack { sp: ss_sp, size: ss_size, flags: ss_flags & SS_AUTODISARM }; } _ => return Err(Errno::EINVAL), } Ok(()) } /// sigaltstack(*new, *old): returns the old one as (ss_sp, ss_flags, /// ss_size) -- ss_flags says SS_ONSTACK if we're on it, SS_DISABLE if /// there's none -- then sets `new` (if given). pub fn sys_sigaltstack(t: &Task, new: Option<(u64, u32, u64)>, user_sp: u64) -> Result<(u64, u32, u64), Errno> { without_interrupts(|| { let mut ts = t.sig.lock(); let a = ts.altstack; let old = (a.sp, a.report_flags(user_sp), a.size); if let Some((sp, flags, size)) = new { set_altstack(&mut ts, sp, flags, size, user_sp)?; } Ok(old) }) } /// rt_sigpending: signals that are pending *and blocked* (the others would /// have been delivered already), for this thread or its whole process. pub fn pending_blocked(t: &Task) -> u64 { without_interrupts(|| { let s = t.process.signals.lock(); let ts = t.sig.lock(); (s.pending.set | ts.pending.set) & ts.saved_mask.unwrap_or(ts.blocked) }) } /// rt_sigtimedwait's dequeue: take a pending signal of `set` (the /// thread's own first, then the process's), without running any handler. pub fn take_waited(t: &Task, set: u64) -> Option<(u32, SigInfo)> { let (sig, mut info) = without_interrupts(|| { let mut s = t.process.signals.lock(); let mut ts = t.sig.lock(); ts.pending.take(set).or_else(|| s.pending.take(set)) })?; crate::timer::delivered(t.process.pid, &mut info); Some((sig, info)) } /// Is POSIX timer `id`'s signal `sig` still pending in `p` (for the /// process or one of its threads)? timer.rs counts overruns meanwhile. pub fn timer_pending(p: &Process, sig: u32, id: i32) -> bool { let is_it = |pending: &Pending| pending.info.iter().any(|(s, i)| *s == sig && i.code == crate::timer::SI_TIMER && i.fields[0] as u32 == id as u32); let tasks = p.tasks(); without_interrupts(|| is_it(&p.signals.lock().pending) || tasks.iter().any(|t| is_it(&t.sig.lock().pending))) } /// Is the process's queue of pending signals full (sigqueue: EAGAIN)? pub fn queue_full(p: &Process) -> bool { without_interrupts(|| p.signals.lock().pending.info.len() >= MAX_QUEUED) } /// All signals but SIGKILL / SIGSTOP (rt_sigtimedwait can't wait for those). pub const fn blockable(set: u64) -> u64 { set & !UNBLOCKABLE }