| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use crate::task::{IntervalTimerHandle, ThreadGroupReadGuard, WaitQueue, WaiterRef}; |
| use starnix_sync::{InterruptibleEvent, RwLock}; |
| use starnix_uapi::errors::Errno; |
| use starnix_uapi::signals::{SigSet, Signal, UncheckedSignal, UNBLOCKABLE_SIGNALS}; |
| use starnix_uapi::union::struct_with_union_into_bytes; |
| use starnix_uapi::user_address::UserAddress; |
| use starnix_uapi::{ |
| __sifields__bindgen_ty_1, __sifields__bindgen_ty_2, __sifields__bindgen_ty_4, |
| __sifields__bindgen_ty_7, c_int, c_uint, error, pid_t, sigaction, sigaltstack, sigevent, |
| siginfo_t, sigval_t, uapi, uid_t, SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, SIGEV_THREAD_ID, |
| SIG_DFL, SIG_IGN, SI_KERNEL, SI_MAX_SIZE, |
| }; |
| use std::cmp::Ordering; |
| use std::collections::VecDeque; |
| use std::sync::Arc; |
| use zerocopy::{AsBytes, FromBytes, FromZeros, NoCell}; |
| |
| /// `SignalActions` contains a `sigaction` for each valid signal. |
| #[derive(Debug)] |
| pub struct SignalActions { |
| actions: RwLock<[sigaction; Signal::NUM_SIGNALS as usize + 1]>, |
| } |
| |
| impl SignalActions { |
| /// Returns a collection of `sigaction`s that contains default values for each signal. |
| pub fn default() -> Arc<SignalActions> { |
| Arc::new(SignalActions { |
| actions: RwLock::new([sigaction::default(); Signal::NUM_SIGNALS as usize + 1]), |
| }) |
| } |
| |
| /// Returns the `sigaction` that is currently set for `signal`. |
| pub fn get(&self, signal: Signal) -> sigaction { |
| // This is safe, since the actions always contain a value for each signal. |
| self.actions.read()[signal.number() as usize] |
| } |
| |
| /// Update the action for `signal`. Returns the previously configured action. |
| pub fn set(&self, signal: Signal, new_action: sigaction) -> sigaction { |
| let mut actions = self.actions.write(); |
| let old_action = actions[signal.number() as usize]; |
| actions[signal.number() as usize] = new_action; |
| old_action |
| } |
| |
| pub fn fork(&self) -> Arc<SignalActions> { |
| Arc::new(SignalActions { actions: RwLock::new(*self.actions.read()) }) |
| } |
| |
| pub fn reset_for_exec(&self) { |
| for action in self.actions.write().iter_mut() { |
| if action.sa_handler != SIG_DFL && action.sa_handler != SIG_IGN { |
| action.sa_handler = SIG_DFL; |
| } |
| } |
| } |
| } |
| |
| /// Whether, and how, this task is blocked. This enum can be extended with new |
| /// variants to optimize different kinds of waiting. |
| #[derive(Debug, Clone)] |
| pub enum RunState { |
| /// This task is not blocked. |
| /// |
| /// The task might be running in userspace or kernel. |
| Running, |
| |
| /// This thread is blocked in a `Waiter`. |
| Waiter(WaiterRef), |
| |
| /// This thread is blocked in an `InterruptibleEvent`. |
| Event(Arc<InterruptibleEvent>), |
| } |
| |
| impl Default for RunState { |
| fn default() -> Self { |
| RunState::Running |
| } |
| } |
| |
| impl RunState { |
| /// Whether this task is blocked. |
| /// |
| /// If the task is blocked, you can break the task out of the wait using the `wake` function. |
| pub fn is_blocked(&self) -> bool { |
| match self { |
| RunState::Running => false, |
| RunState::Waiter(waiter) => waiter.is_valid(), |
| RunState::Event(_) => true, |
| } |
| } |
| |
| /// Unblock the task by interrupting whatever wait the task is blocked upon. |
| pub fn wake(&self) { |
| match self { |
| RunState::Running => (), |
| RunState::Waiter(waiter) => waiter.interrupt(), |
| RunState::Event(event) => event.interrupt(), |
| } |
| } |
| } |
| |
| impl PartialEq<RunState> for RunState { |
| fn eq(&self, other: &RunState) -> bool { |
| match (self, other) { |
| (RunState::Running, RunState::Running) => true, |
| (RunState::Waiter(lhs), RunState::Waiter(rhs)) => lhs == rhs, |
| (RunState::Event(lhs), RunState::Event(rhs)) => Arc::ptr_eq(lhs, rhs), |
| _ => false, |
| } |
| } |
| } |
| |
| #[derive(Default)] |
| pub struct QueuedSignals { |
| /// The queue of standard signals for the task. |
| queue: VecDeque<SignalInfo>, |
| |
| /// Real-time signals queued for the task. Unlike standard signals there may be more than one |
| /// instance of the same real-time signal in the queue. POSIX requires real-time signals |
| /// with lower values to be delivered first. `enqueue()` ensures proper ordering when adding |
| /// new elements. There are no ordering requirements for standard signals. We always dequeue |
| /// standard signals first. This matches Linux behavior. |
| rt_queue: VecDeque<SignalInfo>, |
| } |
| |
| impl QueuedSignals { |
| pub fn enqueue(&mut self, siginfo: SignalInfo) { |
| if siginfo.signal.is_real_time() { |
| // Real-time signals are stored in `rt_queue` in the order they will be delivered, |
| // i.e. they sorted by the signal number. Signals with the same number must be |
| // delivered in the order they were queued. Use binary search to find the right |
| // position to insert the signal. Note that the comparator return `Less` when the |
| // signal is the same. |
| let pos = self |
| .rt_queue |
| .binary_search_by(|v| { |
| if v.signal.number() <= siginfo.signal.number() { |
| Ordering::Less |
| } else { |
| Ordering::Greater |
| } |
| }) |
| .expect_err("Invalid result from binary_search_by()"); |
| self.rt_queue.insert(pos, siginfo); |
| } else { |
| // Don't queue duplicate standard signals. |
| if self.queue.iter().any(move |info| info.signal == siginfo.signal) { |
| return; |
| } |
| self.queue.push_back(siginfo); |
| }; |
| } |
| |
| /// Used by ptrace to provide a replacement for the signal that might have been |
| /// delivered when the task entered signal-delivery-stop. |
| pub fn jump_queue(&mut self, siginfo: SignalInfo) { |
| self.queue.push_front(siginfo); |
| } |
| |
| /// Finds the next queued signal where the given function returns true, removes it from the |
| /// queue, and returns it. |
| pub fn take_next_where<F>(&mut self, predicate: F) -> Option<SignalInfo> |
| where |
| F: Fn(&SignalInfo) -> bool, |
| { |
| // Find the first signal passing `predicate`, prioritizing standard signals. |
| if let Some(index) = self.queue.iter().position(&predicate) { |
| return self.queue.remove(index); |
| } |
| if let Some(index) = self.rt_queue.iter().position(predicate) { |
| return self.rt_queue.remove(index); |
| } |
| None |
| } |
| |
| pub fn is_empty(&self) -> bool { |
| self.queue.is_empty() && self.rt_queue.is_empty() |
| } |
| |
| /// Returns whether any signals are queued and not blocked by the given mask. |
| pub fn is_any_allowed_by_mask(&self, mask: SigSet) -> bool { |
| self.iter().any(|sig| !mask.has_signal(sig.signal)) |
| } |
| |
| /// Returns an iterator over all the pending signals. |
| fn iter(&self) -> impl Iterator<Item = &SignalInfo> { |
| [&self.queue, &self.rt_queue].into_iter().flatten() |
| } |
| |
| /// Iterates over queued signals with the given number. |
| fn iter_queued_by_number(&self, signal: Signal) -> impl Iterator<Item = &SignalInfo> { |
| self.iter().filter(move |info| info.signal == signal) |
| } |
| |
| /// Returns the set of currently pending signals, both standard and real time. |
| pub fn pending(&self) -> SigSet { |
| self.iter().fold(SigSet::default(), |pending, signal| pending | signal.signal.into()) |
| } |
| |
| /// Tests whether a signal with the given number is in the queue. |
| pub fn has_queued(&self, signal: Signal) -> bool { |
| self.iter_queued_by_number(signal).next().is_some() |
| } |
| |
| pub fn num_queued(&self) -> usize { |
| self.queue.len() + self.rt_queue.len() |
| } |
| |
| #[cfg(test)] |
| pub fn queued_count(&self, signal: Signal) -> usize { |
| self.iter_queued_by_number(signal).count() |
| } |
| } |
| |
| /// Per-task signal handling state. |
| #[derive(Default)] |
| pub struct SignalState { |
| // See https://man7.org/linux/man-pages/man2/sigaltstack.2.html |
| pub alt_stack: Option<sigaltstack>, |
| |
| /// Wait queue for signalfd and sigtimedwait. Signaled whenever a signal is added to the queue. |
| pub signal_wait: WaitQueue, |
| |
| /// A handle for interrupting this task, if any. |
| pub run_state: RunState, |
| |
| /// The signal mask of the task. |
| /// |
| /// It is the set of signals whose delivery is currently blocked for the caller. |
| /// See https://man7.org/linux/man-pages/man7/signal.7.html |
| mask: SigSet, |
| |
| /// The signal mask that should be restored by the signal handling machinery, after dequeuing |
| /// a signal. |
| /// |
| /// Some syscalls apply a temporary signal mask by setting `SignalState.mask` during the wait. |
| /// This means that the mask must be set to the temporary mask when the signal is dequeued, |
| /// which is done by the syscall dispatch loop before returning to userspace. After the signal |
| /// is dequeued `mask` can be reset to `saved_mask`. |
| saved_mask: Option<SigSet>, |
| |
| /// The queue of signals for the task. |
| queue: QueuedSignals, |
| } |
| |
| impl SignalState { |
| pub fn with_mask(mask: SigSet) -> Self { |
| Self { mask, ..Default::default() } |
| } |
| |
| /// Sets the signal mask of the state, and returns the old signal mask. |
| pub fn set_mask(&mut self, signal_mask: SigSet) -> SigSet { |
| let old_mask = self.mask; |
| self.mask = signal_mask & !UNBLOCKABLE_SIGNALS; |
| old_mask |
| } |
| |
| /// Sets the signal mask of the state temporarily, until the signal machinery has completed its |
| /// next dequeue operation. This can be used by syscalls that want to change the signal mask |
| /// during a wait, but want the signal mask to be reset before returning back to userspace after |
| /// the wait. |
| pub fn set_temporary_mask(&mut self, signal_mask: SigSet) { |
| assert!(self.saved_mask.is_none()); |
| self.saved_mask = Some(self.mask); |
| self.mask = signal_mask & !UNBLOCKABLE_SIGNALS; |
| } |
| |
| /// Restores the signal mask to what it was before the previous call to `set_temporary_mask`. |
| /// If there is no saved mask, the mask is left alone. |
| pub fn restore_mask(&mut self) { |
| if let Some(mask) = self.saved_mask { |
| self.mask = mask; |
| self.saved_mask = None; |
| } |
| } |
| |
| pub fn mask(&self) -> SigSet { |
| self.mask |
| } |
| |
| pub fn saved_mask(&self) -> Option<SigSet> { |
| self.saved_mask |
| } |
| |
| pub fn enqueue(&mut self, siginfo: SignalInfo) { |
| self.queue.enqueue(siginfo); |
| self.signal_wait.notify_all(); |
| } |
| |
| /// Used by ptrace to provide a replacement for the signal that might have been |
| /// delivered when the task entered signal-delivery-stop. |
| pub fn jump_queue(&mut self, siginfo: SignalInfo) { |
| self.queue.jump_queue(siginfo); |
| self.signal_wait.notify_all(); |
| } |
| |
| pub fn is_empty(&self) -> bool { |
| self.queue.is_empty() |
| } |
| |
| /// Finds the next queued signal where the given function returns true, removes it from the |
| /// queue, and returns it. |
| pub fn take_next_where<F>(&mut self, predicate: F) -> Option<SignalInfo> |
| where |
| F: Fn(&SignalInfo) -> bool, |
| { |
| self.queue.take_next_where(predicate) |
| } |
| |
| /// Returns whether any signals are pending (queued and not blocked). |
| pub fn is_any_pending(&self) -> bool { |
| self.queue.is_any_allowed_by_mask(self.mask) |
| } |
| |
| /// Returns whether any signals are queued and not blocked by the given mask. |
| pub fn is_any_allowed_by_mask(&self, mask: SigSet) -> bool { |
| self.queue.is_any_allowed_by_mask(mask) |
| } |
| |
| pub fn pending(&self) -> SigSet { |
| self.queue.pending() |
| } |
| |
| /// Tests whether a signal with the given number is in the queue. |
| pub fn has_queued(&self, signal: Signal) -> bool { |
| self.queue.has_queued(signal) |
| } |
| |
| pub fn num_queued(&self) -> usize { |
| self.queue.num_queued() |
| } |
| |
| #[cfg(test)] |
| pub fn queued_count(&self, signal: Signal) -> usize { |
| self.queue.queued_count(signal) |
| } |
| } |
| |
| #[derive(Clone, Debug, Default, Eq, PartialEq, AsBytes, FromZeros, FromBytes, NoCell)] |
| #[repr(C)] |
| pub struct SignalInfoHeader { |
| pub signo: u32, |
| pub errno: i32, |
| pub code: i32, |
| pub _pad: i32, |
| } |
| |
| pub const SI_HEADER_SIZE: usize = std::mem::size_of::<SignalInfoHeader>(); |
| |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub struct SignalInfo { |
| pub signal: Signal, |
| pub errno: i32, |
| pub code: i32, |
| pub detail: SignalDetail, |
| pub force: bool, |
| } |
| |
| impl SignalInfo { |
| pub fn default(signal: Signal) -> Self { |
| Self::new(signal, SI_KERNEL as i32, SignalDetail::default()) |
| } |
| |
| pub fn new(signal: Signal, code: i32, detail: SignalDetail) -> Self { |
| Self { signal, errno: 0, code, detail, force: false } |
| } |
| |
| // TODO(tbodt): Add a bound requiring siginfo_t to be FromBytes. This will help ensure the |
| // Linux side won't get an invalid siginfo_t. |
| pub fn as_siginfo_bytes(&self) -> [u8; std::mem::size_of::<siginfo_t>()] { |
| macro_rules! make_siginfo { |
| ($self:ident $(, $( $sifield:ident ).*, $value:expr)?) => { |
| struct_with_union_into_bytes!(siginfo_t { |
| __bindgen_anon_1.__bindgen_anon_1.si_signo: $self.signal.number() as i32, |
| __bindgen_anon_1.__bindgen_anon_1.si_errno: $self.errno, |
| __bindgen_anon_1.__bindgen_anon_1.si_code: $self.code, |
| $( |
| __bindgen_anon_1.__bindgen_anon_1._sifields.$( $sifield ).*: $value, |
| )? |
| }) |
| }; |
| } |
| |
| match self.detail { |
| SignalDetail::None => make_siginfo!(self), |
| SignalDetail::Kill { pid, uid } => { |
| make_siginfo!(self, _kill, __sifields__bindgen_ty_1 { _pid: pid, _uid: uid }) |
| } |
| SignalDetail::SIGCHLD { pid, uid, status } => make_siginfo!( |
| self, |
| _sigchld, |
| __sifields__bindgen_ty_4 { |
| _pid: pid, |
| _uid: uid, |
| _status: status, |
| ..Default::default() |
| } |
| ), |
| SignalDetail::SigFault { addr } => { |
| make_siginfo!(self, _sigfault._addr, linux_uapi::uaddr { addr }) |
| } |
| SignalDetail::SIGSYS { call_addr, syscall, arch } => make_siginfo!( |
| self, |
| _sigsys, |
| __sifields__bindgen_ty_7 { |
| _call_addr: call_addr.into(), |
| _syscall: syscall as c_int, |
| _arch: arch as c_uint, |
| } |
| ), |
| SignalDetail::Timer { ref timer } => { |
| let sigval: uapi::sigval = if timer.signal_event.notify == SignalEventNotify::None { |
| Default::default() |
| } else { |
| timer.signal_event.value.into() |
| }; |
| |
| make_siginfo!( |
| self, |
| _timer, |
| __sifields__bindgen_ty_2 { |
| _tid: timer.timer_id, |
| _overrun: timer.overrun_cur(), |
| _sigval: sigval, |
| ..Default::default() |
| } |
| ) |
| } |
| SignalDetail::Raw { data } => { |
| let header = SignalInfoHeader { |
| signo: self.signal.number(), |
| errno: self.errno, |
| code: self.code, |
| _pad: 0, |
| }; |
| let mut array: [u8; SI_MAX_SIZE as usize] = [0; SI_MAX_SIZE as usize]; |
| header.write_to(&mut array[..SI_HEADER_SIZE]); |
| array[SI_HEADER_SIZE..SI_MAX_SIZE as usize].copy_from_slice(&data); |
| array |
| } |
| } |
| } |
| } |
| |
| #[derive(Clone, Debug, Eq, PartialEq)] |
| pub enum SignalDetail { |
| None, |
| Kill { |
| pid: pid_t, |
| uid: uid_t, |
| }, |
| SIGCHLD { |
| pid: pid_t, |
| uid: uid_t, |
| status: i32, |
| }, |
| SigFault { |
| addr: u64, |
| }, |
| SIGSYS { |
| call_addr: UserAddress, |
| syscall: i32, |
| arch: u32, |
| }, |
| /// POSIX timer |
| Timer { |
| /// Timer where the signal comes from. |
| /// |
| /// Required fields in `uapi::siginfo_t` should be enquired from the timer only when needed. |
| /// Because `overrun` counts might change when the signal is waiting in the queue. |
| timer: IntervalTimerHandle, |
| }, |
| Raw { |
| data: [u8; SI_MAX_SIZE as usize - SI_HEADER_SIZE], |
| }, |
| } |
| |
| impl Default for SignalDetail { |
| fn default() -> Self { |
| Self::None |
| } |
| } |
| |
| #[derive(Debug)] |
| pub struct SignalEvent { |
| pub value: SignalEventValue, |
| pub signo: Option<Signal>, |
| pub notify: SignalEventNotify, |
| } |
| |
| impl SignalEvent { |
| pub fn new(value: SignalEventValue, signo: Signal, notify: SignalEventNotify) -> Self { |
| Self { value, signo: Some(signo), notify } |
| } |
| |
| pub fn none() -> Self { |
| Self { value: Default::default(), signo: None, notify: SignalEventNotify::None } |
| } |
| |
| pub fn is_valid(&self, thread_group: &ThreadGroupReadGuard<'_>) -> bool { |
| if self.notify != SignalEventNotify::None && self.signo.is_none() { |
| return false; |
| } |
| |
| if let SignalEventNotify::ThreadId(tid) = self.notify { |
| if !thread_group.contains_task(tid) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| } |
| |
| impl TryFrom<sigevent> for SignalEvent { |
| type Error = Errno; |
| |
| fn try_from(value: sigevent) -> Result<Self, Self::Error> { |
| // SAFETY: _sigev_un was created with FromBytes so it's safe to access any variant |
| // because all variants must be valid with all bit patterns. |
| let notify = match value.sigev_notify as u32 { |
| SIGEV_SIGNAL => SignalEventNotify::Signal, |
| SIGEV_NONE => SignalEventNotify::None, |
| SIGEV_THREAD => unsafe { |
| SignalEventNotify::Thread { |
| function: value._sigev_un._sigev_thread._function.into(), |
| attribute: value._sigev_un._sigev_thread._attribute.into(), |
| } |
| }, |
| SIGEV_THREAD_ID => SignalEventNotify::ThreadId(unsafe { value._sigev_un._tid }), |
| _ => return error!(EINVAL), |
| }; |
| |
| Ok(match notify { |
| SignalEventNotify::None => SignalEvent::none(), |
| _ => SignalEvent::new( |
| value.sigev_value.into(), |
| UncheckedSignal::new(value.sigev_signo as u64).try_into()?, |
| notify, |
| ), |
| }) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| /// Specifies how notification is to be performed. |
| pub enum SignalEventNotify { |
| /// Notify the process by sending the signal specified in `SignalInfo::Signal`. |
| Signal, |
| /// Don't do anything when the event occurs. |
| None, |
| /// Notify the process by invoking the `function` as if it were the start function of |
| /// a new thread. |
| Thread { function: UserAddress, attribute: UserAddress }, |
| /// Similar to `SignalNotify::Signal`, but the signal is targeted at the thread ID. |
| ThreadId(pid_t), |
| } |
| |
| impl From<SignalEventNotify> for i32 { |
| fn from(value: SignalEventNotify) -> Self { |
| match value { |
| SignalEventNotify::Signal => SIGEV_SIGNAL as i32, |
| SignalEventNotify::None => SIGEV_NONE as i32, |
| SignalEventNotify::Thread { .. } => SIGEV_THREAD as i32, |
| SignalEventNotify::ThreadId(_) => SIGEV_THREAD_ID as i32, |
| } |
| } |
| } |
| |
| /// Data passed with signal event notification. |
| #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] |
| pub struct SignalEventValue(pub u64); |
| |
| impl From<sigval_t> for SignalEventValue { |
| fn from(value: sigval_t) -> Self { |
| SignalEventValue(unsafe { value._bindgen_opaque_blob }) |
| } |
| } |
| |
| impl From<SignalEventValue> for sigval_t { |
| fn from(value: SignalEventValue) -> Self { |
| Self { _bindgen_opaque_blob: value.0 } |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use starnix_uapi::signals::{SIGCHLD, SIGPWR}; |
| use starnix_uapi::CLD_EXITED; |
| |
| #[::fuchsia::test] |
| fn test_signal() { |
| assert!(Signal::try_from(UncheckedSignal::from(0)).is_err()); |
| assert!(Signal::try_from(UncheckedSignal::from(1)).is_ok()); |
| assert!(Signal::try_from(UncheckedSignal::from(Signal::NUM_SIGNALS)).is_ok()); |
| assert!(Signal::try_from(UncheckedSignal::from(Signal::NUM_SIGNALS + 1)).is_err()); |
| assert!(!SIGCHLD.is_real_time()); |
| assert!(Signal::try_from(UncheckedSignal::from(uapi::SIGRTMIN + 12)) |
| .unwrap() |
| .is_real_time()); |
| assert_eq!(format!("{SIGPWR}"), "signal 30: SIGPWR"); |
| assert_eq!( |
| format!("{}", Signal::try_from(UncheckedSignal::from(uapi::SIGRTMIN + 10)).unwrap()), |
| "signal 42: SIGRTMIN+10" |
| ); |
| } |
| |
| #[::fuchsia::test] |
| fn test_siginfo_bytes() { |
| let mut sigchld_bytes = |
| vec![17, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, 200, 1, 0, 0, 2]; |
| sigchld_bytes.resize(std::mem::size_of::<siginfo_t>(), 0); |
| assert_eq!( |
| &SignalInfo::new( |
| SIGCHLD, |
| CLD_EXITED as i32, |
| SignalDetail::SIGCHLD { pid: 123, uid: 456, status: 2 } |
| ) |
| .as_siginfo_bytes(), |
| sigchld_bytes.as_slice() |
| ); |
| } |
| } |