xref: /minix/minix/tests/test79.c (revision 433d6423)
1 /* Tests for PM signal handling robustness - by D.C. van Moolenbroek */
2 /*
3  * The signal handling code must not rely on priorities assigned to services,
4  * and so, this test (like any test!) must also pass if PM and/or VFS are not
5  * given a fixed high priority.  A good way to verify this is to let PM and VFS
6  * be scheduled by SCHED rather than KERNEL, and to give them the same priority
7  * as (or slightly lower than) normal user processes.  Note that if VFS is
8  * configured to use a priority *far lower* than user processes, starvation may
9  * cause this test not to complete in some scenarios.  In that case, Ctrl+C
10  * should still be able to kill the test.
11  */
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <signal.h>
15 #include <sys/wait.h>
16 #include <sys/time.h>
17 #include <sys/utsname.h>
18 
19 #define ITERATIONS 1
20 
21 #include "common.h"
22 
23 #define NR_SIGNALS	20000
24 
25 #define MAX_SIGNALERS	3
26 
27 static const int signaler_sig[MAX_SIGNALERS] = { SIGUSR1, SIGUSR2, SIGHUP };
28 static pid_t signaler_pid[MAX_SIGNALERS];
29 static int sig_counter;
30 
31 enum {
32 	JOB_RUN = 0,
33 	JOB_CALL_PM,
34 	JOB_CALL_VFS,
35 	JOB_SET_MASK,
36 	JOB_BLOCK_PM,
37 	JOB_BLOCK_VFS,
38 	JOB_CALL_PM_VFS,
39 	JOB_FORK,
40 	NR_JOBS
41 };
42 
43 #define OPT_NEST	0x1
44 #define OPT_ALARM	0x2
45 #define OPT_ALL		0x3
46 
47 struct link {
48 	pid_t pid;
49 	int sndfd;
50 	int rcvfd;
51 };
52 
53 /*
54  * Spawn a child process, with a pair of pipes to talk to it bidirectionally.
55  */
56 static void
spawn(struct link * link,void (* proc)(struct link *))57 spawn(struct link *link, void (*proc)(struct link *))
58 {
59 	int up[2], dn[2];
60 
61 	fflush(stdout);
62 	fflush(stderr);
63 
64 	if (pipe(up) != 0) e(0);
65 	if (pipe(dn) != 0) e(0);
66 
67 	link->pid = fork();
68 
69 	switch (link->pid) {
70 	case 0:
71 		close(up[1]);
72 		close(dn[0]);
73 
74 		link->rcvfd = up[0];
75 		link->sndfd = dn[1];
76 
77 		errct = 0;
78 
79 		proc(link);
80 
81 		/* Close our pipe FDs on exit, so that we can make zombies. */
82 		exit(errct);
83 	case -1:
84 		e(0);
85 		break;
86 	}
87 
88 	close(up[0]);
89 	close(dn[1]);
90 
91 	link->sndfd = up[1];
92 	link->rcvfd = dn[0];
93 }
94 
95 /*
96  * Wait for a child process to terminate, and clean up.
97  */
98 static void
collect(struct link * link)99 collect(struct link *link)
100 {
101 	int status;
102 
103 	close(link->sndfd);
104 	close(link->rcvfd);
105 
106 	if (waitpid(link->pid, &status, 0) <= 0) e(0);
107 
108 	if (!WIFEXITED(status)) e(0);
109 	else errct += WEXITSTATUS(status);
110 }
111 
112 /*
113  * Forcibly terminate a child process, and clean up.
114  */
115 static void
terminate(struct link * link)116 terminate(struct link *link)
117 {
118 	int status;
119 
120 	if (kill(link->pid, SIGKILL) != 0) e(0);
121 
122 	close(link->sndfd);
123 	close(link->rcvfd);
124 
125 	if (waitpid(link->pid, &status, 0) <= 0) e(0);
126 
127 	if (WIFSIGNALED(status)) {
128 		if (WTERMSIG(status) != SIGKILL) e(0);
129 	} else {
130 		if (!WIFEXITED(status)) e(0);
131 		else errct += WEXITSTATUS(status);
132 	}
133 }
134 
135 /*
136  * Send an integer value to the child or parent.
137  */
138 static void
snd(struct link * link,int val)139 snd(struct link *link, int val)
140 {
141 	if (write(link->sndfd, (void *) &val, sizeof(val)) != sizeof(val))
142 		e(0);
143 }
144 
145 /*
146  * Receive an integer value from the child or parent, or -1 on EOF.
147  */
148 static int
rcv(struct link * link)149 rcv(struct link *link)
150 {
151 	int r, val;
152 
153 	if ((r = read(link->rcvfd, (void *) &val, sizeof(val))) == 0)
154 		return -1;
155 
156 	if (r != sizeof(val)) e(0);
157 
158 	return val;
159 }
160 
161 /*
162  * Set a signal handler for a particular signal, blocking either all or no
163  * signals when the signal handler is invoked.
164  */
165 static void
set_handler(int sig,void (* proc)(int),int block)166 set_handler(int sig, void (*proc)(int), int block)
167 {
168 	struct sigaction act;
169 
170 	memset(&act, 0, sizeof(act));
171 	if (block) sigfillset(&act.sa_mask);
172 	act.sa_handler = proc;
173 
174 	if (sigaction(sig, &act, NULL) != 0) e(0);
175 }
176 
177 /*
178  * Generic signal handler for the worker process.
179  */
180 static void
worker_handler(int sig)181 worker_handler(int sig)
182 {
183 	int i;
184 
185 	switch (sig) {
186 	case SIGUSR1:
187 	case SIGUSR2:
188 	case SIGHUP:
189 		for (i = 0; i < MAX_SIGNALERS; i++) {
190 			if (signaler_sig[i] != sig) continue;
191 
192 			if (signaler_pid[i] == -1) e(0);
193 			else if (kill(signaler_pid[i], SIGUSR1) != 0) e(0);
194 			break;
195 		}
196 		if (i == MAX_SIGNALERS) e(0);
197 		break;
198 	case SIGTERM:
199 		exit(errct);
200 		break;
201 	case SIGALRM:
202 		/* Do nothing. */
203 		break;
204 	default:
205 		e(0);
206 	}
207 }
208 
209 /*
210  * Procedure for the worker process.  Sets up its own environment using
211  * information sent to it by the parent, sends an acknowledgement to the
212  * parent, and loops executing the job given to it until a SIGTERM comes in.
213  */
214 static void __dead
worker_proc(struct link * parent)215 worker_proc(struct link *parent)
216 {
217 	struct utsname name;
218 	struct itimerval it;
219 	struct timeval tv;
220 	sigset_t set, oset;
221 	uid_t uid;
222 	int i, job, options;
223 
224 	job = rcv(parent);
225 	options = rcv(parent);
226 
227 	for (i = 0; i < MAX_SIGNALERS; i++) {
228 		set_handler(signaler_sig[i], worker_handler,
229 		    !(options & OPT_NEST));
230 
231 		signaler_pid[i] = rcv(parent);
232 	}
233 
234 	set_handler(SIGTERM, worker_handler, 1 /* block */);
235 	set_handler(SIGALRM, worker_handler, !(options & OPT_NEST));
236 
237 	snd(parent, 0);
238 
239 	if (options & OPT_ALARM) {
240 		/* The timer would kill wimpy platforms such as ARM. */
241 		if (uname(&name) < 0) e(0);
242 		if (strcmp(name.machine, "arm")) {
243 			it.it_value.tv_sec = 0;
244 			it.it_value.tv_usec = 1;
245 			it.it_interval.tv_sec = 0;
246 			it.it_interval.tv_usec = 1;
247 			if (setitimer(ITIMER_REAL, &it, NULL) != 0) e(0);
248 		}
249 	}
250 
251 	switch (job) {
252 	case JOB_RUN:
253 		for (;;);
254 		break;
255 	case JOB_CALL_PM:
256 		/*
257 		 * Part of the complication of the current system in PM comes
258 		 * from the fact that when a process is being stopped, it might
259 		 * already have started sending a message.  That message will
260 		 * arrive at its destination regardless of the process's run
261 		 * state.  PM must avoid setting up a signal handler (and
262 		 * changing the process's signal mask as part of that) if such
263 		 * a message is still in transit, because that message might,
264 		 * for example, query (or even change) the signal mask.
265 		 */
266 		for (;;) {
267 			if (sigprocmask(SIG_BLOCK, NULL, &set) != 0) e(0);
268 			if (sigismember(&set, SIGUSR1)) e(0);
269 		}
270 		break;
271 	case JOB_CALL_VFS:
272 		for (;;) {
273 			tv.tv_sec = 0;
274 			tv.tv_usec = 0;
275 			select(0, NULL, NULL, NULL, &tv);
276 		}
277 		break;
278 	case JOB_SET_MASK:
279 		for (;;) {
280 			sigfillset(&set);
281 			if (sigprocmask(SIG_SETMASK, &set, &oset) != 0) e(0);
282 			if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) e(0);
283 		}
284 		break;
285 	case JOB_BLOCK_PM:
286 		for (;;) {
287 			sigemptyset(&set);
288 			sigsuspend(&set);
289 		}
290 		break;
291 	case JOB_BLOCK_VFS:
292 		for (;;)
293 			select(0, NULL, NULL, NULL, NULL);
294 		break;
295 	case JOB_CALL_PM_VFS:
296 		uid = getuid();
297 		for (;;)
298 			setuid(uid);
299 		break;
300 	case JOB_FORK:
301 		/*
302 		 * The child exits immediately; the parent kills the child
303 		 * immediately.  The outcome mostly depends on scheduling.
304 		 * Varying process priorities may yield different tests.
305 		 */
306 		for (;;) {
307 			pid_t pid = fork();
308 			switch (pid) {
309 			case 0:
310 				exit(0);
311 			case -1:
312 				e(1);
313 				break;
314 			default:
315 				kill(pid, SIGKILL);
316 				if (wait(NULL) != pid) e(0);
317 			}
318 		}
319 		break;
320 	default:
321 		e(0);
322 		exit(1);
323 	}
324 }
325 
326 /*
327  * Signal handler procedure for the signaler processes, counting the number of
328  * signals received from the worker process.
329  */
330 static void
signaler_handler(int sig)331 signaler_handler(int sig)
332 {
333 	sig_counter++;
334 }
335 
336 /*
337  * Procedure for the signaler processes.  Gets the pid of the worker process
338  * and the signal to use, and then repeatedly sends that signal to the worker
339  * process, waiting for a SIGUSR1 signal back from the worker before
340  * continuing.  This signal ping-pong is repeated for a set number of times.
341  */
342 static void
signaler_proc(struct link * parent)343 signaler_proc(struct link *parent)
344 {
345 	sigset_t set, oset;
346 	pid_t pid;
347 	int i, sig, nr;
348 
349 	pid = rcv(parent);
350 	sig = rcv(parent);
351 	nr = rcv(parent);
352 	sig_counter = 0;
353 
354 	sigfillset(&set);
355 	if (sigprocmask(SIG_SETMASK, &set, &oset) != 0) e(0);
356 
357 	set_handler(SIGUSR1, signaler_handler, 1 /*block*/);
358 
359 	for (i = 0; nr == 0 || i < nr; i++) {
360 		if (sig_counter != i) e(0);
361 
362 		if (kill(pid, sig) != 0 && nr > 0) e(0);
363 
364 		sigsuspend(&oset);
365 	}
366 
367 	if (sig_counter != nr) e(0);
368 }
369 
370 /*
371  * Set up the worker and signaler processes, wait for the signaler processes to
372  * do their work and terminate, and then terminate the worker process.
373  */
374 static void
sub79a(int job,int signalers,int options)375 sub79a(int job, int signalers, int options)
376 {
377 	struct link worker, signaler[MAX_SIGNALERS];
378 	int i;
379 
380 	spawn(&worker, worker_proc);
381 
382 	snd(&worker, job);
383 	snd(&worker, options);
384 
385 	for (i = 0; i < signalers; i++) {
386 		spawn(&signaler[i], signaler_proc);
387 
388 		snd(&worker, signaler[i].pid);
389 	}
390 	for (; i < MAX_SIGNALERS; i++)
391 		snd(&worker, -1);
392 
393 	if (rcv(&worker) != 0) e(0);
394 
395 	for (i = 0; i < signalers; i++) {
396 		snd(&signaler[i], worker.pid);
397 		snd(&signaler[i], signaler_sig[i]);
398 		snd(&signaler[i], NR_SIGNALS);
399 	}
400 
401 	for (i = 0; i < signalers; i++)
402 		collect(&signaler[i]);
403 
404 	if (kill(worker.pid, SIGTERM) != 0) e(0);
405 
406 	collect(&worker);
407 }
408 
409 /*
410  * Stress test for signal handling.  One worker process gets signals from up to
411  * three signaler processes while performing one of a number of jobs.  It
412  * replies to each signal by signaling the source, thus creating a ping-pong
413  * effect for each of the signaler processes.  The signal ping-ponging is
414  * supposed to be reliable, and the most important aspect of the test is that
415  * no signals get lost.  The test is performed a number of times, varying the
416  * job executed by the worker process, the number of signalers, whether signals
417  * are blocked while executing a signal handler in the worker, and whether the
418  * worker process has a timer running at high frequency.
419  */
420 static void
test79a(void)421 test79a(void)
422 {
423 	int job, signalers, options;
424 
425 	subtest = 1;
426 
427 	for (options = 0; options <= OPT_ALL; options++)
428 		for (signalers = 1; signalers <= MAX_SIGNALERS; signalers++)
429 			for (job = 0; job < NR_JOBS; job++)
430 				sub79a(job, signalers, options);
431 }
432 
433 /*
434  * Set up the worker process and optionally a signaler process, wait for a
435  * predetermined amount of time, and then kill all the child processes.
436  */
437 static void
sub79b(int job,int use_signaler,int options)438 sub79b(int job, int use_signaler, int options)
439 {
440 	struct link worker, signaler;
441 	struct timeval tv;
442 	int i;
443 
444 	spawn(&worker, worker_proc);
445 
446 	snd(&worker, job);
447 	snd(&worker, options);
448 
449 	if ((i = use_signaler) != 0) {
450 		spawn(&signaler, signaler_proc);
451 
452 		snd(&worker, signaler.pid);
453 	}
454 	for (; i < MAX_SIGNALERS; i++)
455 		snd(&worker, -1);
456 
457 	if (rcv(&worker) != 0) e(0);
458 
459 	if (use_signaler) {
460 		snd(&signaler, worker.pid);
461 		snd(&signaler, signaler_sig[0]);
462 		snd(&signaler, 0);
463 	}
464 
465 	/* Use select() so that we can verify we don't get signals. */
466 	tv.tv_sec = 0;
467 	tv.tv_usec = 100000;
468 	if (select(0, NULL, NULL, NULL, &tv) != 0) e(0);
469 
470 	terminate(&worker);
471 
472 	if (use_signaler)
473 		terminate(&signaler);
474 }
475 
476 /*
477  * This test is similar to the previous one, except that we now kill the worker
478  * process after a while.  This should trigger various process transitions to
479  * the exiting state.  Not much can be verified from this test program, but we
480  * intend to trigger as many internal state verification statements of PM
481  * itself as possible this way.  A signaler process is optional in this test,
482  * and if used, it will not stop after a predetermined number of signals.
483  */
484 static void
test79b(void)485 test79b(void)
486 {
487 	int job, signalers, options;
488 
489 	subtest = 2;
490 
491 	for (options = 0; options <= OPT_ALL; options++)
492 		for (signalers = 0; signalers <= 1; signalers++)
493 			for (job = 0; job < NR_JOBS; job++)
494 				sub79b(job, signalers, options);
495 
496 }
497 
498 /*
499  * PM signal handling robustness test program.
500  */
501 int
main(int argc,char ** argv)502 main(int argc, char **argv)
503 {
504 	int i, m;
505 
506 	start(79);
507 
508 	if (argc == 2)
509 		m = atoi(argv[1]);
510 	else
511 		m = 0xFF;
512 
513 	for (i = 0; i < ITERATIONS; i++) {
514 		if (m & 0x01) test79a();
515 		if (m & 0x02) test79b();
516 	}
517 
518 	quit();
519 }
520