GNU Linux-libre 4.19.286-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 #include "current_stack_pointer.h"
23
24 #ifndef SS_AUTODISARM
25 #define SS_AUTODISARM  (1U << 31)
26 #endif
27
28 static void *sstack, *ustack;
29 static ucontext_t uc, sc;
30 static const char *msg = "[OK]\tStack preserved";
31 static const char *msg2 = "[FAIL]\tStack corrupted";
32 struct stk_data {
33         char msg[128];
34         int flag;
35 };
36
37 void my_usr1(int sig, siginfo_t *si, void *u)
38 {
39         char *aa;
40         int err;
41         stack_t stk;
42         struct stk_data *p;
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 }