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