1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #define _GNU_SOURCE
4 #include <signal.h>
5 #include <stdio.h>
6 #include <stdbool.h>
7 #include <string.h>
8 #include <err.h>
9 #include <errno.h>
10 #include <limits.h>
11 #include <sys/mman.h>
12 #include <sys/auxv.h>
13 #include <sys/prctl.h>
14 #include <sys/resource.h>
15 #include <setjmp.h>
16 
17 /* sigaltstack()-enforced minimum stack */
18 #define ENFORCED_MINSIGSTKSZ	2048
19 
20 #ifndef AT_MINSIGSTKSZ
21 #  define AT_MINSIGSTKSZ	51
22 #endif
23 
24 static int nerrs;
25 
26 static bool sigalrm_expected;
27 
28 static unsigned long at_minstack_size;
29 
sethandler(int sig,void (* handler)(int,siginfo_t *,void *),int flags)30 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
31 		       int flags)
32 {
33 	struct sigaction sa;
34 
35 	memset(&sa, 0, sizeof(sa));
36 	sa.sa_sigaction = handler;
37 	sa.sa_flags = SA_SIGINFO | flags;
38 	sigemptyset(&sa.sa_mask);
39 	if (sigaction(sig, &sa, 0))
40 		err(1, "sigaction");
41 }
42 
clearhandler(int sig)43 static void clearhandler(int sig)
44 {
45 	struct sigaction sa;
46 
47 	memset(&sa, 0, sizeof(sa));
48 	sa.sa_handler = SIG_DFL;
49 	sigemptyset(&sa.sa_mask);
50 	if (sigaction(sig, &sa, 0))
51 		err(1, "sigaction");
52 }
53 
setup_altstack(void * start,unsigned long size)54 static int setup_altstack(void *start, unsigned long size)
55 {
56 	stack_t ss;
57 
58 	memset(&ss, 0, sizeof(ss));
59 	ss.ss_size = size;
60 	ss.ss_sp = start;
61 
62 	return sigaltstack(&ss, NULL);
63 }
64 
65 static jmp_buf jmpbuf;
66 
sigsegv(int sig,siginfo_t * info,void * ctx_void)67 static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
68 {
69 	if (sigalrm_expected) {
70 		printf("[FAIL]\tWrong signal delivered: SIGSEGV (expected SIGALRM).");
71 		nerrs++;
72 	} else {
73 		printf("[OK]\tSIGSEGV signal delivered.\n");
74 	}
75 
76 	siglongjmp(jmpbuf, 1);
77 }
78 
sigalrm(int sig,siginfo_t * info,void * ctx_void)79 static void sigalrm(int sig, siginfo_t *info, void *ctx_void)
80 {
81 	if (!sigalrm_expected) {
82 		printf("[FAIL]\tWrong signal delivered: SIGALRM (expected SIGSEGV).");
83 		nerrs++;
84 	} else {
85 		printf("[OK]\tSIGALRM signal delivered.\n");
86 	}
87 }
88 
test_sigaltstack(void * altstack,unsigned long size)89 static void test_sigaltstack(void *altstack, unsigned long size)
90 {
91 	if (setup_altstack(altstack, size))
92 		err(1, "sigaltstack()");
93 
94 	sigalrm_expected = (size > at_minstack_size) ? true : false;
95 
96 	sethandler(SIGSEGV, sigsegv, 0);
97 	sethandler(SIGALRM, sigalrm, SA_ONSTACK);
98 
99 	if (!sigsetjmp(jmpbuf, 1)) {
100 		printf("[RUN]\tTest an alternate signal stack of %ssufficient size.\n",
101 		       sigalrm_expected ? "" : "in");
102 		printf("\tRaise SIGALRM. %s is expected to be delivered.\n",
103 		       sigalrm_expected ? "It" : "SIGSEGV");
104 		raise(SIGALRM);
105 	}
106 
107 	clearhandler(SIGALRM);
108 	clearhandler(SIGSEGV);
109 }
110 
main(void)111 int main(void)
112 {
113 	void *altstack;
114 
115 	at_minstack_size = getauxval(AT_MINSIGSTKSZ);
116 
117 	altstack = mmap(NULL, at_minstack_size + SIGSTKSZ, PROT_READ | PROT_WRITE,
118 			MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
119 	if (altstack == MAP_FAILED)
120 		err(1, "mmap()");
121 
122 	if ((ENFORCED_MINSIGSTKSZ + 1) < at_minstack_size)
123 		test_sigaltstack(altstack, ENFORCED_MINSIGSTKSZ + 1);
124 
125 	test_sigaltstack(altstack, at_minstack_size + SIGSTKSZ);
126 
127 	return nerrs == 0 ? 0 : 1;
128 }
129