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 28static void *sstack, *ustack; 29static ucontext_t uc, sc; 30static const char *msg = "[OK]\tStack preserved"; 31static const char *msg2 = "[FAIL]\tStack corrupted"; 32struct stk_data { 33 char msg[128]; 34 int flag; 35}; 36 37void 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_fail_msg("[RUN]\tAborting\n"); 70 exit(EXIT_FAILURE); 71 } 72} 73 74void 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 93static void switch_fn(void) 94{ 95 ksft_print_msg("[RUN]\tswitched to user ctx\n"); 96 raise(SIGUSR2); 97 setcontext(&sc); 98} 99 100int main(void) 101{ 102 struct sigaction act; 103 stack_t stk; 104 int err; 105 106 ksft_print_header(); 107 ksft_set_plan(3); 108 109 sigemptyset(&act.sa_mask); 110 act.sa_flags = SA_ONSTACK | SA_SIGINFO; 111 act.sa_sigaction = my_usr1; 112 sigaction(SIGUSR1, &act, NULL); 113 act.sa_sigaction = my_usr2; 114 sigaction(SIGUSR2, &act, NULL); 115 sstack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, 116 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); 117 if (sstack == MAP_FAILED) { 118 ksft_exit_fail_msg("mmap() - %s\n", strerror(errno)); 119 return EXIT_FAILURE; 120 } 121 122 err = sigaltstack(NULL, &stk); 123 if (err) { 124 ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno)); 125 exit(EXIT_FAILURE); 126 } 127 if (stk.ss_flags == SS_DISABLE) { 128 ksft_test_result_pass( 129 "Initial sigaltstack state was SS_DISABLE\n"); 130 } else { 131 ksft_exit_fail_msg("Initial sigaltstack state was %x; " 132 "should have been SS_DISABLE\n", stk.ss_flags); 133 return EXIT_FAILURE; 134 } 135 136 stk.ss_sp = sstack; 137 stk.ss_size = SIGSTKSZ; 138 stk.ss_flags = SS_ONSTACK | SS_AUTODISARM; 139 err = sigaltstack(&stk, NULL); 140 if (err) { 141 if (errno == EINVAL) { 142 ksft_test_result_skip( 143 "[NOTE]\tThe running kernel doesn't support SS_AUTODISARM\n"); 144 /* 145 * If test cases for the !SS_AUTODISARM variant were 146 * added, we could still run them. We don't have any 147 * test cases like that yet, so just exit and report 148 * success. 149 */ 150 return 0; 151 } else { 152 ksft_exit_fail_msg( 153 "sigaltstack(SS_ONSTACK | SS_AUTODISARM) %s\n", 154 strerror(errno)); 155 return EXIT_FAILURE; 156 } 157 } 158 159 ustack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, 160 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); 161 if (ustack == MAP_FAILED) { 162 ksft_exit_fail_msg("mmap() - %s\n", strerror(errno)); 163 return EXIT_FAILURE; 164 } 165 getcontext(&uc); 166 uc.uc_link = NULL; 167 uc.uc_stack.ss_sp = ustack; 168 uc.uc_stack.ss_size = SIGSTKSZ; 169 makecontext(&uc, switch_fn, 0); 170 raise(SIGUSR1); 171 172 err = sigaltstack(NULL, &stk); 173 if (err) { 174 ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno)); 175 exit(EXIT_FAILURE); 176 } 177 if (stk.ss_flags != SS_AUTODISARM) { 178 ksft_exit_fail_msg("ss_flags=%x, should be SS_AUTODISARM\n", 179 stk.ss_flags); 180 exit(EXIT_FAILURE); 181 } 182 ksft_test_result_pass( 183 "sigaltstack is still SS_AUTODISARM after signal\n"); 184 185 ksft_exit_pass(); 186 return 0; 187} 188