1 /*
2  * %CopyrightBegin%
3 
4  *
5  * Copyright Ericsson AB 2001-2020. All Rights Reserved.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * %CopyrightEnd%
20  */
21 /*
22  * hipe_x86_signal.c
23  *
24  * Erlang code compiled to x86 native code uses the x86 %esp as its
25  * stack pointer. This improves performance in several ways:
26  * - It permits the use of the x86 call and ret instructions, which
27  *   reduces code volume and improves branch prediction.
28  * - It avoids stealing a gp register to act as a stack pointer.
29  *
30  * Unix signal handlers are by default delivered onto the current
31  * stack, i.e. %esp. This is a problem since our native-code stacks
32  * are small and may not have room for the Unix signal handler.
33  *
34  * There is a way to redirect signal handlers to an "alternate" signal
35  * stack by using the SA_ONSTACK flag with the sigaction() library call.
36  * Unfortunately, this has to be specified explicitly for each signal,
37  * and it is difficult to enforce given the presence of libraries.
38  *
39  * Our solution is to override the C library's signal handler setup
40  * procedure with our own which enforces the SA_ONSTACK flag.
41  */
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45 #include <signal.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include "sys.h"
49 #include "erl_alloc.h"
50 #include "hipe_signal.h"
51 
52 #if defined(__GLIBC__) && __GLIBC__ == 2 && (__GLIBC_MINOR__ >= 3)
53 /*
54  * __libc_sigaction() is the core routine.
55  * Without libpthread, sigaction() and __sigaction() are both aliases
56  * for __libc_sigaction().
57  * libpthread redefines __sigaction() as a non-trivial wrapper around
58  * __libc_sigaction(), and makes sigaction() an alias for __sigaction().
59  * glibc has internal calls to both sigaction() and __sigaction().
60  *
61  * Overriding __libc_sigaction() would be ideal, but doing so breaks
62  * libpthread (threads hang).
63  *
64  * Overriding __sigaction(), using dlsym RTLD_NEXT to find glibc's
65  * version of __sigaction(), works with glibc-2.2.4 and 2.2.5.
66  * Unfortunately, this solution doesn't work with earlier versions,
67  * including glibc-2.2.2 and glibc-2.1.92 (2.2 despite its name):
68  * 2.2.2 SIGSEGVs in dlsym RTLD_NEXT (known glibc bug), and 2.1.92
69  * SIGSEGVs inexplicably in two test cases in the HiPE test suite.
70  *
71  * Instead we only override sigaction() and call __sigaction()
72  * directly. This should work for HiPE/x86 as long as only the Posix
73  * signal interface is used, i.e. there are no calls to simulated
74  * old BSD or SysV interfaces.
75  * glibc's internal calls to __sigaction() appear to be mostly safe.
76  * hipe_signal_init() fixes some unsafe ones, e.g. the SIGPROF handler.
77  */
78 #ifndef __USE_GNU
79 #define __USE_GNU		/* to un-hide RTLD_NEXT */
80 #endif
81 #define NEXT_SIGACTION "__sigaction"
82 #define LIBC_SIGACTION __sigaction
83 #define OVERRIDE_SIGACTION
84 #endif	/* glibc >= 2.3 */
85 
86 /* Is there no standard identifier for Darwin/MacOSX ? */
87 #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
88 #define __DARWIN__ 1
89 #endif
90 
91 #if defined(__DARWIN__)
92 /*
93  * Assumes Mac OS X >= 10.3 (dlsym operations not available in 10.2 and
94  * earlier).
95  *
96  * The code below assumes that is is part of the main image (earlier
97  * in the load order than libSystem and certainly before any dylib
98  * that might use sigaction) -- a standard RTLD_NEXT caveat.
99  *
100  * _sigaction lives in /usr/lib/libSystem.B.dylib and can be found
101  * with the standard dlsym(RTLD_NEXT) call. The proviso on Mac OS X
102  * being that the symbol for dlsym doesn't include a leading '_'.
103  *
104  * The other _sigaction, _sigaction_no_bind I don't understand the purpose
105  * of and don't modify.
106  */
107 #define NEXT_SIGACTION "sigaction"
108 #define LIBC_SIGACTION _sigaction
109 #undef OVERRIDE_SIGACTION
110 #define _NSIG NSIG
111 #endif /* __DARWIN__ */
112 
113 #if defined(__sun__)
114 /*
115  * Assume Solaris/x86 2.8.
116  * There is a number of sigaction() procedures in libc:
117  * * sigaction(): weak reference to _sigaction().
118  * * _sigaction(): apparently a simple wrapper around __sigaction().
119  * * __sigaction(): apparently the procedure doing the actual system call.
120  * * _libc_sigaction(): apparently some thread-related wrapper, which ends
121  *   up calling __sigaction().
122  * The threads library redefines sigaction() and _sigaction() to its
123  * own wrapper, which checks for and restricts access to threads-related
124  * signals. The wrapper appears to eventually call libc's __sigaction().
125  *
126  * We catch and override _sigaction() since overriding __sigaction()
127  * causes fatal errors in some cases.
128  *
129  * When linked with thread support, there are calls to sigaction() before
130  * our init routine has had a chance to find _sigaction()'s address.
131  * This forces us to initialise at the first call.
132  */
133 #define NEXT_SIGACTION "_sigaction"
134 #define LIBC_SIGACTION _sigaction
135 #define OVERRIDE_SIGACTION
136 #define _NSIG NSIG
137 #endif /* __sun__ */
138 
139 #if defined(__FreeBSD__)
140 /*
141  * This is a copy of Darwin code for FreeBSD.
142  * CAVEAT: detailed semantics are not verified yet.
143  */
144 #define NEXT_SIGACTION "sigaction"
145 #define LIBC_SIGACTION _sigaction
146 #undef OVERRIDE_SIGACTION
147 #define _NSIG NSIG
148 #endif /* __FreeBSD__ */
149 
150 #if defined(__NetBSD__)
151 /*
152  * Note: This is only stub code to allow the build to succeed.
153  * Whether this actually provides the needed overrides for safe
154  * signal delivery or not is unknown.
155  */
156 #undef NEXT_SIGACTION
157 #undef OVERRIDE_SIGACTION
158 #endif /* __NetBSD__ */
159 
160 #if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__))
161 /*
162  * Unknown libc -- assume musl, which does not allow safe signals
163  */
164 #error "HiPE does not work without a libc that can guarantee that sigaltstack works"
165 #endif	/* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */
166 
167 #if defined(NEXT_SIGACTION)
168 /*
169  * Initialize a function pointer to the libc core sigaction routine,
170  * to be used by our wrappers.
171  */
172 #include <dlfcn.h>
173 static int (*next_sigaction)(int, const struct sigaction*, struct sigaction*);
do_init(void)174 static void do_init(void)
175 {
176     next_sigaction = dlsym(RTLD_NEXT, NEXT_SIGACTION);
177     if (next_sigaction != 0)
178 	return;
179     perror("dlsym");
180     abort();
181 }
182 #define INIT()	do { if (!next_sigaction) do_init(); } while (0)
183 #else	/* !defined(NEXT_SIGACTION) */
184 #define INIT()	do { } while (0)
185 #endif	/* !defined(NEXT_SIGACTION) */
186 
187 #if defined(NEXT_SIGACTION)
188 /*
189  * This is our wrapper for sigaction(). sigaction() can be called before
190  * hipe_signal_init() has been executed, especially when threads support
191  * has been linked with the executable. Therefore, we must initialise
192  * next_sigaction() dynamically, the first time it's needed.
193  */
my_sigaction(int signum,const struct sigaction * act,struct sigaction * oldact)194 static int my_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
195 {
196     struct sigaction newact;
197 
198     INIT();
199 
200     if (act &&
201 	act->sa_handler != SIG_DFL &&
202 	act->sa_handler != SIG_IGN &&
203 	!(act->sa_flags & SA_ONSTACK)) {
204 	newact = *act;
205 	newact.sa_flags |= SA_ONSTACK;
206 	act = &newact;
207     }
208     return next_sigaction(signum, act, oldact);
209 }
210 #endif
211 
212 #if defined(LIBC_SIGACTION)
213 /*
214  * This overrides the C library's core sigaction() procedure, catching
215  * all its internal calls.
216  */
217 extern int LIBC_SIGACTION(int, const struct sigaction*, struct sigaction*);
LIBC_SIGACTION(int signum,const struct sigaction * act,struct sigaction * oldact)218 int LIBC_SIGACTION(int signum, const struct sigaction *act, struct sigaction *oldact)
219 {
220     return my_sigaction(signum, act, oldact);
221 }
222 #endif
223 
224 #if defined(OVERRIDE_SIGACTION)
225 /*
226  * This catches the application's own sigaction() calls.
227  */
sigaction(int signum,const struct sigaction * act,struct sigaction * oldact)228 int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
229 {
230     return my_sigaction(signum, act, oldact);
231 }
232 #endif
233 
234 /*
235  * Set alternate signal stack for the invoking thread.
236  */
hipe_sigaltstack(void * ss_sp)237 static void hipe_sigaltstack(void *ss_sp)
238 {
239     stack_t ss;
240 
241     ss.ss_sp = ss_sp;
242     ss.ss_flags = 0;
243     ss.ss_size = SIGSTKSZ;
244     if (sigaltstack(&ss, NULL) < 0) {
245 	perror("sigaltstack");
246 	abort();
247     }
248 }
249 
250 /*
251  * Set up alternate signal stack for an Erlang process scheduler thread.
252  */
hipe_thread_signal_init(void)253 void hipe_thread_signal_init(void)
254 {
255     /* Stack don't really need to be cache aligned.
256        We use it to suppress false leak report from valgrind */
257     hipe_sigaltstack(erts_alloc_permanent_cache_aligned(ERTS_ALC_T_HIPE_LL, SIGSTKSZ));
258 }
259 
260 /*
261  * Set up alternate signal stack for the main thread,
262  * unless this is a multithreaded runtime system.
263  */
hipe_sigaltstack_init(void)264 static void hipe_sigaltstack_init(void)
265 {
266 }
267 
268 /*
269  * 1. Set up alternate signal stack for the main thread.
270  * 2. Add SA_ONSTACK to existing user-defined signal handlers.
271  */
hipe_signal_init(void)272 void hipe_signal_init(void)
273 {
274     struct sigaction sa;
275     int i;
276 
277     INIT();
278 
279     hipe_sigaltstack_init();
280 
281     for (i = 1; i < _NSIG; ++i) {
282 	if (sigaction(i, NULL, &sa)) {
283 	    /* This will fail with EINVAL on Solaris if 'i' is one of the
284 	       thread library's private signals. We DO catch the initial
285 	       setup of these signals, so things MAY be OK anyway. */
286 	    continue;
287 	}
288 	if (sa.sa_handler == SIG_DFL ||
289 	    sa.sa_handler == SIG_IGN ||
290 	    (sa.sa_flags & SA_ONSTACK))
291 	    continue;
292 	sa.sa_flags |= SA_ONSTACK;
293 	if (sigaction(i, &sa, NULL)) {
294 #ifdef SIGCANCEL
295 	    /* Solaris 9 x86 refuses to let us modify SIGCANCEL. */
296 	    if (i == SIGCANCEL)
297 		continue;
298 #endif
299 	    perror("sigaction");
300 	    abort();
301 	}
302     }
303 }
304