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 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 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 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 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 %u\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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.