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 ksft_set_plan(3); 113 114 sigemptyset(&act.sa_mask); 115 act.sa_flags = SA_ONSTACK | SA_SIGINFO; 116 act.sa_sigaction = my_usr1; 117 sigaction(SIGUSR1, &act, NULL); 118 act.sa_sigaction = my_usr2; 119 sigaction(SIGUSR2, &act, NULL); 120 sstack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, 121 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); 122 if (sstack == MAP_FAILED) { 123 ksft_exit_fail_msg("mmap() - %s\n", strerror(errno)); 124 return EXIT_FAILURE; 125 } 126 127 err = sigaltstack(NULL, &stk); 128 if (err) { 129 ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno)); 130 exit(EXIT_FAILURE); 131 } 132 if (stk.ss_flags == SS_DISABLE) { 133 ksft_test_result_pass( 134 "Initial sigaltstack state was SS_DISABLE\n"); 135 } else { 136 ksft_exit_fail_msg("Initial sigaltstack state was %x; " 137 "should have been SS_DISABLE\n", stk.ss_flags); 138 return EXIT_FAILURE; 139 } 140 141 stk.ss_sp = sstack; 142 stk.ss_size = SIGSTKSZ; 143 stk.ss_flags = SS_ONSTACK | SS_AUTODISARM; 144 err = sigaltstack(&stk, NULL); 145 if (err) { 146 if (errno == EINVAL) { 147 ksft_exit_skip( 148 "[NOTE]\tThe running kernel doesn't support SS_AUTODISARM\n"); 149 /* 150 * If test cases for the !SS_AUTODISARM variant were 151 * added, we could still run them. We don't have any 152 * test cases like that yet, so just exit and report 153 * success. 154 */ 155 return 0; 156 } else { 157 ksft_exit_fail_msg( 158 "sigaltstack(SS_ONSTACK | SS_AUTODISARM) %s\n", 159 strerror(errno)); 160 return EXIT_FAILURE; 161 } 162 } 163 164 ustack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, 165 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); 166 if (ustack == MAP_FAILED) { 167 ksft_exit_fail_msg("mmap() - %s\n", strerror(errno)); 168 return EXIT_FAILURE; 169 } 170 getcontext(&uc); 171 uc.uc_link = NULL; 172 uc.uc_stack.ss_sp = ustack; 173 uc.uc_stack.ss_size = SIGSTKSZ; 174 makecontext(&uc, switch_fn, 0); 175 raise(SIGUSR1); 176 177 err = sigaltstack(NULL, &stk); 178 if (err) { 179 ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno)); 180 exit(EXIT_FAILURE); 181 } 182 if (stk.ss_flags != SS_AUTODISARM) { 183 ksft_exit_fail_msg("ss_flags=%x, should be SS_AUTODISARM\n", 184 stk.ss_flags); 185 exit(EXIT_FAILURE); 186 } 187 ksft_test_result_pass( 188 "sigaltstack is still SS_AUTODISARM after signal\n"); 189 190 ksft_exit_pass(); 191 return 0; 192 } 193