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