xref: /openbmc/linux/tools/testing/selftests/sigaltstack/sas.c (revision 28efb0046512e8a13ed9f9bdf0d68d10bbfbe9cf)
1 /*
2  * Stas Sergeev <stsp@users.sourceforge.net>
3  *
4  * test sigaltstack(SS_ONSTACK | SS_AUTODISARM)
5  * If that succeeds, then swapcontext() can be used inside sighandler safely.
6  *
7  */
8 
9 #define _GNU_SOURCE
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/mman.h>
14 #include <ucontext.h>
15 #include <alloca.h>
16 #include <string.h>
17 #include <assert.h>
18 #include <errno.h>
19 
20 #include "../kselftest.h"
21 
22 #ifndef SS_AUTODISARM
23 #define SS_AUTODISARM  (1U << 31)
24 #endif
25 
26 static void *sstack, *ustack;
27 static ucontext_t uc, sc;
28 static const char *msg = "[OK]\tStack preserved";
29 static const char *msg2 = "[FAIL]\tStack corrupted";
30 struct stk_data {
31 	char msg[128];
32 	int flag;
33 };
34 
35 void my_usr1(int sig, siginfo_t *si, void *u)
36 {
37 	char *aa;
38 	int err;
39 	stack_t stk;
40 	struct stk_data *p;
41 
42 	register unsigned long sp asm("sp");
43 
44 	if (sp < (unsigned long)sstack ||
45 			sp >= (unsigned long)sstack + SIGSTKSZ) {
46 		ksft_exit_fail_msg("SP is not on sigaltstack\n");
47 	}
48 	/* put some data on stack. other sighandler will try to overwrite it */
49 	aa = alloca(1024);
50 	assert(aa);
51 	p = (struct stk_data *)(aa + 512);
52 	strcpy(p->msg, msg);
53 	p->flag = 1;
54 	ksft_print_msg("[RUN]\tsignal USR1\n");
55 	err = sigaltstack(NULL, &stk);
56 	if (err) {
57 		ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
58 		exit(EXIT_FAILURE);
59 	}
60 	if (stk.ss_flags != SS_DISABLE)
61 		ksft_test_result_fail("tss_flags=%x, should be SS_DISABLE\n",
62 				stk.ss_flags);
63 	else
64 		ksft_test_result_pass(
65 				"sigaltstack is disabled in sighandler\n");
66 	swapcontext(&sc, &uc);
67 	ksft_print_msg("%s\n", p->msg);
68 	if (!p->flag) {
69 		ksft_exit_skip("[RUN]\tAborting\n");
70 		exit(EXIT_FAILURE);
71 	}
72 }
73 
74 void my_usr2(int sig, siginfo_t *si, void *u)
75 {
76 	char *aa;
77 	struct stk_data *p;
78 
79 	ksft_print_msg("[RUN]\tsignal USR2\n");
80 	aa = alloca(1024);
81 	/* dont run valgrind on this */
82 	/* try to find the data stored by previous sighandler */
83 	p = memmem(aa, 1024, msg, strlen(msg));
84 	if (p) {
85 		ksft_test_result_fail("sigaltstack re-used\n");
86 		/* corrupt the data */
87 		strcpy(p->msg, msg2);
88 		/* tell other sighandler that his data is corrupted */
89 		p->flag = 0;
90 	}
91 }
92 
93 static void switch_fn(void)
94 {
95 	ksft_print_msg("[RUN]\tswitched to user ctx\n");
96 	raise(SIGUSR2);
97 	setcontext(&sc);
98 }
99 
100 int main(void)
101 {
102 	struct sigaction act;
103 	stack_t stk;
104 	int err;
105 
106 	ksft_print_header();
107 
108 	sigemptyset(&act.sa_mask);
109 	act.sa_flags = SA_ONSTACK | SA_SIGINFO;
110 	act.sa_sigaction = my_usr1;
111 	sigaction(SIGUSR1, &act, NULL);
112 	act.sa_sigaction = my_usr2;
113 	sigaction(SIGUSR2, &act, NULL);
114 	sstack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,
115 		      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
116 	if (sstack == MAP_FAILED) {
117 		ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));
118 		return EXIT_FAILURE;
119 	}
120 
121 	err = sigaltstack(NULL, &stk);
122 	if (err) {
123 		ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
124 		exit(EXIT_FAILURE);
125 	}
126 	if (stk.ss_flags == SS_DISABLE) {
127 		ksft_test_result_pass(
128 				"Initial sigaltstack state was SS_DISABLE\n");
129 	} else {
130 		ksft_exit_fail_msg("Initial sigaltstack state was %x; "
131 		       "should have been SS_DISABLE\n", stk.ss_flags);
132 		return EXIT_FAILURE;
133 	}
134 
135 	stk.ss_sp = sstack;
136 	stk.ss_size = SIGSTKSZ;
137 	stk.ss_flags = SS_ONSTACK | SS_AUTODISARM;
138 	err = sigaltstack(&stk, NULL);
139 	if (err) {
140 		if (errno == EINVAL) {
141 			ksft_exit_skip(
142 				"[NOTE]\tThe running kernel doesn't support SS_AUTODISARM\n");
143 			/*
144 			 * If test cases for the !SS_AUTODISARM variant were
145 			 * added, we could still run them.  We don't have any
146 			 * test cases like that yet, so just exit and report
147 			 * success.
148 			 */
149 			return 0;
150 		} else {
151 			ksft_exit_fail_msg(
152 				"sigaltstack(SS_ONSTACK | SS_AUTODISARM)  %s\n",
153 					strerror(errno));
154 			return EXIT_FAILURE;
155 		}
156 	}
157 
158 	ustack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,
159 		      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
160 	if (ustack == MAP_FAILED) {
161 		ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));
162 		return EXIT_FAILURE;
163 	}
164 	getcontext(&uc);
165 	uc.uc_link = NULL;
166 	uc.uc_stack.ss_sp = ustack;
167 	uc.uc_stack.ss_size = SIGSTKSZ;
168 	makecontext(&uc, switch_fn, 0);
169 	raise(SIGUSR1);
170 
171 	err = sigaltstack(NULL, &stk);
172 	if (err) {
173 		ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
174 		exit(EXIT_FAILURE);
175 	}
176 	if (stk.ss_flags != SS_AUTODISARM) {
177 		ksft_exit_fail_msg("ss_flags=%x, should be SS_AUTODISARM\n",
178 				stk.ss_flags);
179 		exit(EXIT_FAILURE);
180 	}
181 	ksft_test_result_pass(
182 			"sigaltstack is still SS_AUTODISARM after signal\n");
183 
184 	ksft_exit_pass();
185 	return 0;
186 }
187