//! 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; // 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 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 don't queue: one /// pending instance each, like Linux). #[derive(Default)] pub struct Pending { pub set: u64, info: Vec<(u32, SigInfo)>, } impl Pending { /// Mark `sig` pending with this siginfo. fn raise(&mut self, sig: u32, info: SigInfo) { self.set |= bit(sig); self.info.retain(|(s, _)| *s != sig); self.info.push((sig, info)); } /// Take the lowest signal of `allowed` (SIGKILL first). 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 }; self.set &= !bit(sig); let info = match self.info.iter().position(|(s, _)| *s == sig) { Some(i) => self.info.swap_remove(i).1, None => SigInfo::new(sig, SI_KERNEL), }; Some((sig, info)) } fn discard(&mut self, mask: u64) { self.set &= !mask; } } /// 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, } impl ThreadSig { pub fn new(blocked: u64) -> ThreadSig { ThreadSig { blocked, pending: Pending::default(), reason: None, saved_mask: None } } /// 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) { if !frame.from_user() { return; } let Some(t) = thread::current_task() else { return }; 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, info, action, reason)) = next else { break }; 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), Some(_) => frame.rax = -(Errno::EINTR as i64) as u64, 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; // 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); } } /// 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 saved FPU/SSE state; 0 = none (see below) 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); /// Push a signal frame on the user stack and point `frame` at the handler. /// /// Note: we don't save the FPU/SSE registers (`fpstate` = 0). Our kernel /// doesn't use them and our programs' handlers don't touch floats, so they /// survive anyway; a full libc needs XSAVE state here (roadmap). 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), aligned so the handler starts with rsp % 16 == 8, as // if it had been called. let sp = ((frame.rsp.wrapping_sub(128).wrapping_sub(size)) & !0xf).wrapping_sub(8); // The mask sigreturn restores: the real one, if a ppoll & co. had // swapped in a temporary mask. let blocked = without_interrupts(|| { let ts = t.sig.lock(); ts.saved_mask.unwrap_or(ts.blocked) }); 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 }, ..Default::default() }; let rt = RtSigFrame { pretcode: action.restorer, uc: UContext { mcontext, sigmask: blocked, ..Default::default() }, info: *info, }; if !crate::mm::user_access_ok(sp, size, true) { return Err(()); } unsafe { core::ptr::write_unaligned(sp as *mut RtSigFrame, rt) }; 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(|| t.sig.lock().blocked = rt.uc.sigmask & !UNBLOCKABLE); } } // --------------------------------------------------------------------------- // 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; 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) }