xref: /linux/tools/testing/selftests/signal/mangle_uc_sigmask.c (revision 79d2e1919a2728ef49d938eb20ebd5903c14dfb0)
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 &current->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 &current->blocked\n");
182 
183 	ksft_finished();
184 }
185