Skip to main content

starnix_uapi/
signals.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::errors::{Errno, errno, error};
6use crate::sigset_t;
7use linux_uapi as uapi;
8use static_assertions::assert_eq_size;
9use std::fmt;
10use std::ops::{BitAnd, BitOr, Not};
11use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
12
13pub const UNBLOCKABLE_SIGNALS: SigSet = SigSet(SIGKILL.mask() | SIGSTOP.mask());
14
15/// An unchecked signal represents a signal that has not been through verification, and may
16/// represent an invalid signal number.
17#[derive(Debug, Copy, Clone, Eq, PartialEq)]
18pub struct UncheckedSignal(u64);
19
20impl UncheckedSignal {
21    pub fn new(value: u64) -> UncheckedSignal {
22        UncheckedSignal(value)
23    }
24
25    pub fn is_zero(self) -> bool {
26        self.0 == 0
27    }
28
29    pub fn raw(self) -> u64 {
30        self.0
31    }
32}
33impl From<Signal> for UncheckedSignal {
34    fn from(signal: Signal) -> UncheckedSignal {
35        UncheckedSignal(signal.number as u64)
36    }
37}
38impl From<u32> for UncheckedSignal {
39    fn from(value: u32) -> UncheckedSignal {
40        UncheckedSignal(value as u64)
41    }
42}
43
44/// The `Signal` struct represents a valid signal.
45#[derive(Copy, Clone, PartialEq, Eq, Hash)]
46pub struct Signal {
47    number: u32,
48}
49
50impl Signal {
51    /// The signal number, guaranteed to be a value between 1..=NUM_SIGNALS.
52    pub fn number(&self) -> u32 {
53        self.number
54    }
55
56    /// Returns the bitmask for this signal number.
57    pub const fn mask(&self) -> u64 {
58        1 << (self.number - 1)
59    }
60
61    /// Returns true if the signal is a real-time signal.
62    pub fn is_real_time(&self) -> bool {
63        self.number >= super::SIGRTMIN
64    }
65
66    /// Returns true if this signal can't be blocked. This means either SIGKILL or SIGSTOP.
67    pub fn is_unblockable(&self) -> bool {
68        UNBLOCKABLE_SIGNALS.has_signal(*self)
69    }
70
71    /// Used exclusively for PTRACE_O_TRACESYSGOOD
72    pub fn set_ptrace_syscall_bit(&mut self) {
73        self.number |= 0x80
74    }
75
76    pub fn name(&self) -> &'static str {
77        if self.is_real_time() {
78            return "real time signal";
79        }
80        match self.number {
81            uapi::SIGHUP => "SIGHUP",
82            uapi::SIGINT => "SIGINT",
83            uapi::SIGQUIT => "SIGQUIT",
84            uapi::SIGILL => "SIGILL",
85            uapi::SIGTRAP => "SIGTRAP",
86            uapi::SIGABRT => "SIGABRT",
87            uapi::SIGBUS => "SIGBUS",
88            uapi::SIGFPE => "SIGFPE",
89            uapi::SIGKILL => "SIGKILL",
90            uapi::SIGUSR1 => "SIGUSR1",
91            uapi::SIGSEGV => "SIGSEGV",
92            uapi::SIGUSR2 => "SIGUSR2",
93            uapi::SIGPIPE => "SIGPIPE",
94            uapi::SIGALRM => "SIGALRM",
95            uapi::SIGTERM => "SIGTERM",
96            uapi::SIGSTKFLT => "SIGSTKFLT",
97            uapi::SIGCHLD => "SIGCHLD",
98            uapi::SIGCONT => "SIGCONT",
99            uapi::SIGSTOP => "SIGSTOP",
100            uapi::SIGTSTP => "SIGTSTP",
101            uapi::SIGTTIN => "SIGTTIN",
102            uapi::SIGTTOU => "SIGTTOU",
103            uapi::SIGURG => "SIGURG",
104            uapi::SIGXCPU => "SIGXCPU",
105            uapi::SIGXFSZ => "SIGXFSZ",
106            uapi::SIGVTALRM => "SIGVTALRM",
107            uapi::SIGPROF => "SIGPROF",
108            uapi::SIGWINCH => "SIGWINCH",
109            uapi::SIGIO => "SIGIO",
110            uapi::SIGPWR => "SIGPWR",
111            uapi::SIGSYS => "SIGSYS",
112            _ => "unknown signal",
113        }
114    }
115
116    /// The number of signals, also the highest valid signal number.
117    pub const NUM_SIGNALS: u32 = 64;
118}
119
120pub const SIGHUP: Signal = Signal { number: uapi::SIGHUP };
121pub const SIGINT: Signal = Signal { number: uapi::SIGINT };
122pub const SIGQUIT: Signal = Signal { number: uapi::SIGQUIT };
123pub const SIGILL: Signal = Signal { number: uapi::SIGILL };
124pub const SIGTRAP: Signal = Signal { number: uapi::SIGTRAP };
125pub const SIGABRT: Signal = Signal { number: uapi::SIGABRT };
126#[allow(dead_code)]
127pub const SIGIOT: Signal = Signal { number: uapi::SIGIOT };
128pub const SIGBUS: Signal = Signal { number: uapi::SIGBUS };
129pub const SIGFPE: Signal = Signal { number: uapi::SIGFPE };
130pub const SIGKILL: Signal = Signal { number: uapi::SIGKILL };
131pub const SIGUSR1: Signal = Signal { number: uapi::SIGUSR1 };
132pub const SIGSEGV: Signal = Signal { number: uapi::SIGSEGV };
133pub const SIGUSR2: Signal = Signal { number: uapi::SIGUSR2 };
134pub const SIGPIPE: Signal = Signal { number: uapi::SIGPIPE };
135pub const SIGALRM: Signal = Signal { number: uapi::SIGALRM };
136pub const SIGTERM: Signal = Signal { number: uapi::SIGTERM };
137pub const SIGSTKFLT: Signal = Signal { number: uapi::SIGSTKFLT };
138pub const SIGCHLD: Signal = Signal { number: uapi::SIGCHLD };
139pub const SIGCONT: Signal = Signal { number: uapi::SIGCONT };
140pub const SIGSTOP: Signal = Signal { number: uapi::SIGSTOP };
141pub const SIGTSTP: Signal = Signal { number: uapi::SIGTSTP };
142pub const SIGTTIN: Signal = Signal { number: uapi::SIGTTIN };
143pub const SIGTTOU: Signal = Signal { number: uapi::SIGTTOU };
144pub const SIGURG: Signal = Signal { number: uapi::SIGURG };
145pub const SIGXCPU: Signal = Signal { number: uapi::SIGXCPU };
146pub const SIGXFSZ: Signal = Signal { number: uapi::SIGXFSZ };
147pub const SIGVTALRM: Signal = Signal { number: uapi::SIGVTALRM };
148pub const SIGPROF: Signal = Signal { number: uapi::SIGPROF };
149pub const SIGWINCH: Signal = Signal { number: uapi::SIGWINCH };
150pub const SIGIO: Signal = Signal { number: uapi::SIGIO };
151pub const SIGPWR: Signal = Signal { number: uapi::SIGPWR };
152pub const SIGSYS: Signal = Signal { number: uapi::SIGSYS };
153#[allow(dead_code)]
154pub const SIGRTMIN: Signal = Signal { number: super::SIGRTMIN };
155
156impl TryFrom<UncheckedSignal> for Signal {
157    type Error = Errno;
158
159    fn try_from(value: UncheckedSignal) -> Result<Self, Self::Error> {
160        let value = u32::try_from(value.0).map_err(|_| errno!(EINVAL))?;
161        if (1..=Signal::NUM_SIGNALS).contains(&value) {
162            Ok(Signal { number: value })
163        } else {
164            error!(EINVAL)
165        }
166    }
167}
168
169impl fmt::Debug for Signal {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        write!(f, "{self}")
172    }
173}
174
175impl fmt::Display for Signal {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        if self.is_real_time() {
178            write!(f, "SIGRTMIN+{}({})", self.number - super::SIGRTMIN, self.number)
179        } else {
180            write!(f, "{}({})", self.name(), self.number)
181        }
182    }
183}
184
185/// POSIX defines sigset_t as either a numeric or structure type (so the number of signals can
186/// exceed the bits in a machine word). The programmer is supposed to modify this bitfield using
187/// sigaddset(), etc. rather than setting bits directly so client code can be agnostic to the
188/// definition.
189///
190///  * On x64, sigset_t is a typedef for "unsigned long".
191///  * On ARM64, sigset_t is a structure containing one "unsigned long" member.
192///
193/// To keep the Starnix code agnostic to the definition, this SigSet type provides a wrapper
194/// with a uniform interface for the things we need.
195///
196/// The layout of this object is designed to be identical to the layout of the current
197/// architecture's sigset_t type so UserRef<SigSet> can be used for system calls.
198#[repr(transparent)]
199#[derive(
200    Debug, Copy, Clone, Default, IntoBytes, Eq, KnownLayout, FromBytes, Immutable, PartialEq,
201)]
202pub struct SigSet(pub std::os::raw::c_ulong);
203assert_eq_size!(SigSet, sigset_t);
204
205impl SigSet {
206    /// Returns whether this signal set contains the given signal.
207    pub fn has_signal(&self, signal: Signal) -> bool {
208        (self.0 & (signal.mask() as std::os::raw::c_ulong)) != 0
209    }
210
211    /// Returns whether this signal set has any signals in common with another.
212    pub fn intersects(&self, other: &Self) -> bool {
213        (self.0 & other.0) != 0
214    }
215}
216
217impl From<sigset_t> for SigSet {
218    fn from(value: sigset_t) -> Self {
219        // `transmute()` is safe here because this is a POD value of the same size (see
220        // assert above).
221        #[allow(
222            clippy::undocumented_unsafe_blocks,
223            reason = "Force documented unsafe blocks in Starnix"
224        )]
225        SigSet(unsafe { std::mem::transmute(value) })
226    }
227}
228
229impl From<SigSet> for sigset_t {
230    fn from(val: SigSet) -> Self {
231        // `transmute()` is safe here because this is a POD value of the same size (see
232        // assert above).
233        #[allow(
234            clippy::undocumented_unsafe_blocks,
235            reason = "Force documented unsafe blocks in Starnix"
236        )]
237        unsafe {
238            std::mem::transmute(val.0)
239        }
240    }
241}
242
243impl BitAnd for SigSet {
244    type Output = Self;
245
246    // rhs is the "right-hand side" of the expression `a & b`
247    fn bitand(self, rhs: Self) -> Self::Output {
248        Self(self.0 & rhs.0)
249    }
250}
251
252impl BitOr for SigSet {
253    type Output = Self;
254
255    // rhs is the "right-hand side" of the expression `a | b`
256    fn bitor(self, rhs: Self) -> Self::Output {
257        Self(self.0 | rhs.0)
258    }
259}
260
261impl Not for SigSet {
262    type Output = Self;
263
264    fn not(self) -> Self::Output {
265        SigSet(!self.0)
266    }
267}
268
269impl From<Signal> for SigSet {
270    /// Constructs a sigset consisting of one signal value.
271    fn from(value: Signal) -> Self {
272        SigSet(value.mask() as std::os::raw::c_ulong)
273    }
274}
275
276pub fn sigaltstack_contains_pointer(stack: &uapi::sigaltstack, ptr: u64) -> bool {
277    let min = stack.ss_sp.addr as u64;
278    let max = (stack.ss_sp.addr as u64).saturating_add(stack.ss_size as u64);
279    ptr >= min && ptr <= max
280}