1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2024 ARM Ltd. 4 * 5 * Author: Dev Jain <dev.jain@arm.com> 6 * 7 * Test describing a clear distinction between signal states - delivered and 8 * blocked, and their relation with ucontext. 9 * 10 * A process can request blocking of a signal by masking it into its set of 11 * blocked signals; such a signal, when sent to the process by the kernel, 12 * will get blocked by the process and it may later unblock it and take an 13 * action. At that point, the signal will be delivered. 14 * 15 * We test the following functionalities of the kernel: 16 * 17 * ucontext_t describes the interrupted context of the thread; this implies 18 * that, in case of registering a handler and catching the corresponding 19 * signal, that state is before what was jumping into the handler. 20 * 21 * The thread's mask of blocked signals can be permanently changed, i.e, not 22 * just during the execution of the handler, by mangling with uc_sigmask 23 * from inside the handler. 24 * 25 * Assume that we block the set of signals, S1, by sigaction(), and say, the 26 * signal for which the handler was installed, is S2. When S2 is sent to the 27 * program, it will be considered "delivered", since we will act on the 28 * signal and jump to the handler. Any instances of S1 or S2 raised, while the 29 * program is executing inside the handler, will be blocked; they will be 30 * delivered immediately upon termination of the handler. 31 * 32 * For standard signals (also see real-time signals in the man page), multiple 33 * blocked instances of the same signal are not queued; such a signal will 34 * be delivered just once. 35 */ 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <signal.h> 40 #include <ucontext.h> 41 42 #include "../kselftest.h" 43 44 void handler_verify_ucontext(int signo, siginfo_t *info, void *uc) 45 { 46 int ret; 47 48 /* Kernel dumps ucontext with USR2 blocked */ 49 ret = sigismember(&(((ucontext_t *)uc)->uc_sigmask), SIGUSR2); 50 ksft_test_result(ret == 1, "USR2 blocked in ucontext\n"); 51 52 /* 53 * USR2 is blocked; can be delivered neither here, nor after 54 * exit from handler 55 */ 56 if (raise(SIGUSR2)) 57 ksft_exit_fail_perror("raise"); 58 } 59 60 void handler_segv(int signo, siginfo_t *info, void *uc) 61 { 62 /* 63 * Three cases possible: 64 * 1. Program already terminated due to segmentation fault. 65 * 2. SEGV was blocked even after returning from handler_usr. 66 * 3. SEGV was delivered on returning from handler_usr. 67 * The last option must happen. 68 */ 69 ksft_test_result_pass("SEGV delivered\n"); 70 } 71 72 static int cnt; 73 74 void handler_usr(int signo, siginfo_t *info, void *uc) 75 { 76 int ret; 77 78 /* 79 * Break out of infinite recursion caused by raise(SIGUSR1) invoked 80 * from inside the handler 81 */ 82 ++cnt; 83 if (cnt > 1) 84 return; 85 86 /* SEGV blocked during handler execution, delivered on return */ 87 if (raise(SIGSEGV)) 88 ksft_exit_fail_perror("raise"); 89 90 ksft_print_msg("SEGV bypassed successfully\n"); 91 92 /* 93 * Signal responsible for handler invocation is blocked by default; 94 * delivered on return, leading to recursion 95 */ 96 if (raise(SIGUSR1)) 97 ksft_exit_fail_perror("raise"); 98 99 ksft_test_result(cnt == 1, 100 "USR1 is blocked, cannot invoke handler right now\n"); 101 102 /* Raise USR1 again; only one instance must be delivered upon exit */ 103 if (raise(SIGUSR1)) 104 ksft_exit_fail_perror("raise"); 105 106 /* SEGV has been blocked in sa_mask, but ucontext is empty */ 107 ret = sigismember(&(((ucontext_t *)uc)->uc_sigmask), SIGSEGV); 108 ksft_test_result(ret == 0, "SEGV not blocked in ucontext\n"); 109 110 /* USR1 has been blocked, but ucontext is empty */ 111 ret = sigismember(&(((ucontext_t *)uc)->uc_sigmask), SIGUSR1); 112 ksft_test_result(ret == 0, "USR1 not blocked in ucontext\n"); 113 114 /* 115 * Mangle ucontext; this will be copied back into ¤t->blocked 116 * on return from the handler. 117 */ 118 if (sigaddset(&((ucontext_t *)uc)->uc_sigmask, SIGUSR2)) 119 ksft_exit_fail_perror("sigaddset"); 120 } 121 122 int main(int argc, char *argv[]) 123 { 124 struct sigaction act, act2; 125 sigset_t set, oldset; 126 127 ksft_print_header(); 128 ksft_set_plan(7); 129 130 act.sa_flags = SA_SIGINFO; 131 act.sa_sigaction = &handler_usr; 132 133 /* Add SEGV to blocked mask */ 134 if (sigemptyset(&act.sa_mask) || sigaddset(&act.sa_mask, SIGSEGV) 135 || (sigismember(&act.sa_mask, SIGSEGV) != 1)) 136 ksft_exit_fail_msg("Cannot add SEGV to blocked mask\n"); 137 138 if (sigaction(SIGUSR1, &act, NULL)) 139 ksft_exit_fail_perror("Cannot install handler"); 140 141 act2.sa_flags = SA_SIGINFO; 142 act2.sa_sigaction = &handler_segv; 143 144 if (sigaction(SIGSEGV, &act2, NULL)) 145 ksft_exit_fail_perror("Cannot install handler"); 146 147 /* Invoke handler */ 148 if (raise(SIGUSR1)) 149 ksft_exit_fail_perror("raise"); 150 151 /* USR1 must not be queued */ 152 ksft_test_result(cnt == 2, "handler invoked only twice\n"); 153 154 /* Mangled ucontext implies USR2 is blocked for current thread */ 155 if (raise(SIGUSR2)) 156 ksft_exit_fail_perror("raise"); 157 158 ksft_print_msg("USR2 bypassed successfully\n"); 159 160 act.sa_sigaction = &handler_verify_ucontext; 161 if (sigaction(SIGUSR1, &act, NULL)) 162 ksft_exit_fail_perror("Cannot install handler"); 163 164 if (raise(SIGUSR1)) 165 ksft_exit_fail_perror("raise"); 166 167 /* 168 * Raising USR2 in handler_verify_ucontext is redundant since it 169 * is blocked 170 */ 171 ksft_print_msg("USR2 still blocked on return from handler\n"); 172 173 /* Confirm USR2 blockage by sigprocmask() too */ 174 if (sigemptyset(&set)) 175 ksft_exit_fail_perror("sigemptyset"); 176 177 if (sigprocmask(SIG_BLOCK, &set, &oldset)) 178 ksft_exit_fail_perror("sigprocmask"); 179 180 ksft_test_result(sigismember(&oldset, SIGUSR2) == 1, 181 "USR2 present in ¤t->blocked\n"); 182 183 ksft_finished(); 184 } 185