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