1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
| #![no_std]
#![no_main]
#[macro_use]
#[path = "ulib.rs"]
mod ulib;
use core::sync::atomic::{AtomicI64, Ordering::SeqCst};
use ulib::*;
static HITS: AtomicI64 = AtomicI64::new(0);
static LAST_SIG: AtomicI64 = AtomicI64::new(0);
static MASK_IN_HANDLER: AtomicI64 = AtomicI64::new(0);
static CHLD_PID: AtomicI64 = AtomicI64::new(0);
static CHLD_STATUS: AtomicI64 = AtomicI64::new(0);
static CHLD_CODE: AtomicI64 = AtomicI64::new(0);
extern "C" fn on_signal(sig: i32) {
HITS.fetch_add(1, SeqCst);
LAST_SIG.store(sig as i64, SeqCst);
MASK_IN_HANDLER.store(sigprocmask(SIG_BLOCK, 0), SeqCst);
}
extern "C" fn on_child(_sig: i32, info: *const SigInfo, _ctx: *mut u8) {
let info = unsafe { &*info };
CHLD_PID.store(info.addr_or_pid as u32 as i64, SeqCst);
CHLD_STATUS.store(info.status as i64, SeqCst);
CHLD_CODE.store(info.code as i64, SeqCst);
}
#[inline(never)]
fn compute_with_signal(sig: i32) -> u64 {
let mut x = core::hint::black_box(0x1234_5678_u64);
let mut y = core::hint::black_box(0x0bad_cafe_u64);
for i in 0..1000u64 {
if i == 500 {
kill(getpid(), sig); }
x = x.wrapping_mul(31).wrapping_add(i);
y ^= x.rotate_left(7);
}
x ^ y
}
fn in_child(f: fn()) -> i32 {
let pid = fork();
if pid == 0 {
f();
exit(0);
}
wait4(pid, 0).map(|(_, s)| s).unwrap_or(-1)
}
fn main() -> i64 {
let mut c = Checks::new("sigtest");
sigaction_raw(SIGUSR2, SIG_IGN, 0, 0);
let without = compute_with_signal(SIGUSR2); c.check(HITS.load(SeqCst) == 0, "SIG_IGN: an ignored signal does nothing");
c.check(signal(SIGUSR1, on_signal) == 0, "rt_sigaction installs a handler");
let with = compute_with_signal(SIGUSR1);
c.check(HITS.load(SeqCst) == 1 && LAST_SIG.load(SeqCst) == SIGUSR1 as i64, "kill(getpid(), SIGUSR1) runs the handler once, with sig = 10");
c.check(with == without, "after rt_sigreturn the interrupted code computes the same result");
c.check(MASK_IN_HANDLER.load(SeqCst) as u64 & sigbit(SIGUSR1) != 0, "the signal is blocked while its handler runs");
c.check(sigprocmask(SIG_BLOCK, 0) as u64 & sigbit(SIGUSR1) == 0, "... and unblocked again after sigreturn");
sigprocmask(SIG_BLOCK, sigbit(SIGUSR1));
kill(getpid(), SIGUSR1);
c.check(HITS.load(SeqCst) == 1, "blocked: kill doesn't run the handler yet");
sigprocmask(SIG_UNBLOCK, sigbit(SIGUSR1));
c.check(HITS.load(SeqCst) == 2, "unblocking delivers the pending signal");
c.check(sigaction_raw(SIGKILL, SIG_IGN, 0, 0) == -22, "SIGKILL can't be caught or ignored (EINVAL)");
let s = in_child(|| {
kill(getpid(), SIGTERM);
});
c.check(wifsignaled(s) && wtermsig(s) == SIGTERM, "SIGTERM's default action terminates");
let s = in_child(|| {
kill(getpid(), SIGQUIT);
});
c.check(wifsignaled(s) && wtermsig(s) == SIGQUIT, "SIGQUIT (\"core\") terminates too (no core files here)");
let s = in_child(|| {
kill(getpid(), SIGCHLD); kill(getpid(), SIGURG_NUM);
});
c.check(wifexited(s) && wexitstatus(s) == 0, "SIGCHLD and SIGURG are ignored by default");
let s = in_child(|| {
let (r, w) = pipe().unwrap();
close(r);
write(w, b"x");
});
c.check(wifsignaled(s) && wtermsig(s) == SIGPIPE, "writing to a pipe without readers: SIGPIPE");
let s = in_child(|| {
sigaction_raw(SIGPIPE, SIG_IGN, 0, 0);
let (r, w) = pipe().unwrap();
close(r);
exit(if write(w, b"x") == -32 { 0 } else { 1 });
});
c.check(wifexited(s) && wexitstatus(s) == 0, "... with SIGPIPE ignored, write fails with EPIPE");
sigaction_info(SIGCHLD, on_child, SA_RESTART);
let pid = fork();
if pid == 0 {
exit(3);
}
while CHLD_PID.load(SeqCst) == 0 {
pause();
}
c.check(CHLD_PID.load(SeqCst) == pid && CHLD_STATUS.load(SeqCst) == 3 && CHLD_CODE.load(SeqCst) == 1, "SIGCHLD arrives with si_pid, status 3, CLD_EXITED");
c.check(wait(pid) == 3, "and the child can still be reaped");
sigaction_raw(SIGCHLD, SIG_DFL, 0, 0);
let pid = fork();
if pid == 0 {
loop {
sleep_ms(10);
}
}
kill(pid, SIGSTOP);
let r = wait4(pid, WUNTRACED);
c.check(matches!(r, Ok((p, s)) if p == pid && wifstopped(s) && wstopsig(s) == SIGSTOP), "SIGSTOP stops a child; wait4(WUNTRACED) reports it");
kill(pid, SIGCONT);
let r = wait4(pid, WCONTINUED);
c.check(matches!(r, Ok((_, s)) if wifcontinued(s)), "SIGCONT continues it; wait4(WCONTINUED) reports that");
kill(pid, SIGKILL);
let r = wait4(pid, 0);
c.check(matches!(r, Ok((_, s)) if wifsignaled(s) && wtermsig(s) == SIGKILL), "SIGKILL ends it");
for restart in [false, true] {
sigaction_raw(SIGUSR1, on_signal as usize, if restart { SA_RESTART } else { 0 }, 0);
let (r, w) = pipe().unwrap();
let me = getpid();
let pid = fork();
if pid == 0 {
sleep_ms(120);
kill(me, SIGUSR1); sleep_ms(120);
write(w, b"y");
exit(0);
}
let hits = HITS.load(SeqCst);
let mut b = [0u8; 1];
let n = read(r, &mut b);
if restart {
c.check(n == 1 && HITS.load(SeqCst) == hits + 1, "SA_RESTART: the handler runs, then read continues and gets the data");
} else {
c.check(n == -EINTR && HITS.load(SeqCst) == hits + 1, "no SA_RESTART: read returns EINTR after the handler");
}
wait(pid);
close(r);
close(w);
}
c.check(kill(99999, SIGTERM) == -ESRCH, "kill of a pid that doesn't exist: ESRCH");
c.check(kill(getpid(), 99) == -22, "kill with signal 99: EINVAL");
c.check(kill(getpid(), 0) == 0, "signal 0 only checks that the process exists");
let pid = fork();
if pid == 0 {
setpgid(0, 0);
loop {
pause();
}
}
setpgid(pid, pid);
c.check(kill(-pid, SIGTERM) == 0, "kill(-pgid) signals a whole process group");
let r = wait4(pid, 0);
c.check(matches!(r, Ok((_, s)) if wtermsig(s) == SIGTERM), "the group's member got it");
c.done()
}
const SIGURG_NUM: i32 = 23;
|