xref: /PHP-7.0/Zend/zend_signal.c (revision 18f7e262)
1 /*
2   +----------------------------------------------------------------------+
3   | Zend Signal Handling                                                 |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2008 The PHP Group                                     |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Lucas Nealan <lucas@php.net>                                |
16   |          Arnaud Le Blanc <lbarnaud@php.net>                          |
17   +----------------------------------------------------------------------+
18 
19    This software was contributed to PHP by Facebook Inc. in 2008.
20 
21    Future revisions and derivatives of this source code must acknowledge
22    Facebook Inc. as the original contributor of this module by leaving
23    this note intact in the source code.
24 
25    All other licensing and usage conditions are those of the PHP Group.
26 */
27 
28  /* $Id$ */
29 
30 #define _GNU_SOURCE
31 #include <string.h>
32 
33 #include "zend.h"
34 #include "zend_globals.h"
35 
36 #ifdef HAVE_SIGNAL_H
37 #include <signal.h>
38 #endif
39 
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 
44 #ifdef ZEND_SIGNALS
45 
46 #include "zend_signal.h"
47 
48 #ifdef ZTS
49 ZEND_API int zend_signal_globals_id;
50 #else
51 zend_signal_globals_t zend_signal_globals;
52 #endif
53 
54 static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context);
55 static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*));
56 
57 #ifdef __CYGWIN__
58 #define TIMEOUT_SIG SIGALRM
59 #else
60 #define TIMEOUT_SIG SIGPROF
61 #endif
62 
63 static int zend_sigs[] = { TIMEOUT_SIG, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2 };
64 
65 #define SA_FLAGS_MASK ~(SA_NODEFER | SA_RESETHAND)
66 
67 /* True globals, written only at process startup */
68 static zend_signal_entry_t global_orig_handlers[NSIG];
69 static sigset_t            global_sigmask;
70 
71 /* {{{ zend_signal_handler_defer
72  *  Blocks signals if in critical section */
zend_signal_handler_defer(int signo,siginfo_t * siginfo,void * context)73 void zend_signal_handler_defer(int signo, siginfo_t *siginfo, void *context)
74 {
75 	int errno_save = errno;
76 	zend_signal_queue_t *queue, *qtmp;
77 	zend_bool is_handling_safe = 1;
78 
79 #ifdef ZTS
80 	ZEND_TSRMLS_CACHE_UPDATE();
81 	/* A signal could hit after TSRM shutdown, in this case globals are already freed. */
82 	if (NULL == TSRMLS_CACHE || NULL == TSRMG_BULK_STATIC(zend_signal_globals_id, zend_signal_globals_t *)) {
83 		is_handling_safe = 0;
84 	}
85 #endif
86 
87 	if (EXPECTED(is_handling_safe && SIGG(active))) {
88 		if (UNEXPECTED(SIGG(depth) == 0)) { /* try to handle signal */
89 			if (UNEXPECTED(SIGG(blocked))) {
90 				SIGG(blocked) = 0;
91 			}
92 			if (EXPECTED(SIGG(running) == 0)) {
93 				SIGG(running) = 1;
94 				zend_signal_handler(signo, siginfo, context);
95 
96 				queue = SIGG(phead);
97 				SIGG(phead) = NULL;
98 
99 				while (queue) {
100 					zend_signal_handler(queue->zend_signal.signo, queue->zend_signal.siginfo, queue->zend_signal.context);
101 					qtmp = queue->next;
102 					queue->next = SIGG(pavail);
103 					queue->zend_signal.signo = 0;
104 					SIGG(pavail) = queue;
105 					queue = qtmp;
106 				}
107 				SIGG(running) = 0;
108 			}
109 		} else { /* delay signal handling */
110 			SIGG(blocked) = 1; /* signal is blocked */
111 
112 			if ((queue = SIGG(pavail))) { /* if none available it's simply forgotton */
113 				SIGG(pavail) = queue->next;
114 				queue->zend_signal.signo = signo;
115 				queue->zend_signal.siginfo = siginfo;
116 				queue->zend_signal.context = context;
117 				queue->next = NULL;
118 
119 				if (SIGG(phead) && SIGG(ptail)) {
120 					SIGG(ptail)->next = queue;
121 				} else {
122 					SIGG(phead) = queue;
123 				}
124 				SIGG(ptail) = queue;
125 			}
126 #if ZEND_DEBUG
127 			else { /* this may not be safe to do, but could work and be useful */
128 				zend_output_debug_string(0, "zend_signal: not enough queue storage, lost signal (%d)", signo);
129 			}
130 #endif
131 		}
132 	} else {
133 		/* need to just run handler if we're inactive and getting a signal */
134 		zend_signal_handler(signo, siginfo, context);
135 	}
136 
137 	errno = errno_save;
138 } /* }}} */
139 
140 /* {{{ zend_signal_handler_unblock
141  * Handle deferred signal from HANDLE_UNBLOCK_ALARMS */
zend_signal_handler_unblock(void)142 ZEND_API void zend_signal_handler_unblock(void)
143 {
144 	zend_signal_queue_t *queue;
145 	zend_signal_t zend_signal;
146 
147 	if (EXPECTED(SIGG(active))) {
148 		SIGNAL_BEGIN_CRITICAL(); /* procmask to protect handler_defer as if it were called by the kernel */
149 		queue = SIGG(phead);
150 		SIGG(phead) = queue->next;
151 		zend_signal = queue->zend_signal;
152 		queue->next = SIGG(pavail);
153 		queue->zend_signal.signo = 0;
154 		SIGG(pavail) = queue;
155 
156 		zend_signal_handler_defer(zend_signal.signo, zend_signal.siginfo, zend_signal.context);
157 		SIGNAL_END_CRITICAL();
158 	}
159 }
160 /* }}} */
161 
162 /* {{{ zend_signal_handler
163  *  Call the previously registered handler for a signal
164  */
zend_signal_handler(int signo,siginfo_t * siginfo,void * context)165 static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context)
166 {
167 	int errno_save = errno;
168 	struct sigaction sa = {{0}};
169 	sigset_t sigset;
170 	zend_signal_entry_t p_sig;
171 #ifdef ZTS
172 	if (NULL == TSRMLS_CACHE || NULL == TSRMG_BULK_STATIC(zend_signal_globals_id, zend_signal_globals_t *)) {
173 		p_sig.flags = 0;
174 		p_sig.handler = SIG_DFL;
175 	} else
176 #endif
177 	p_sig = SIGG(handlers)[signo-1];
178 
179 	if (p_sig.handler == SIG_DFL) { /* raise default handler */
180 		if (sigaction(signo, NULL, &sa) == 0) {
181 			sa.sa_handler = SIG_DFL;
182 			sigemptyset(&sa.sa_mask);
183 
184 			sigemptyset(&sigset);
185 			sigaddset(&sigset, signo);
186 
187 			if (sigaction(signo, &sa, NULL) == 0) {
188 				/* throw away any blocked signals */
189 				sigprocmask(SIG_UNBLOCK, &sigset, NULL);
190 				raise(signo);
191 			}
192 		}
193 	} else if (p_sig.handler != SIG_IGN) { /* ignore SIG_IGN */
194 		if (p_sig.flags & SA_SIGINFO) {
195 			if (p_sig.flags & SA_RESETHAND) {
196 				SIGG(handlers)[signo-1].flags   = 0;
197 				SIGG(handlers)[signo-1].handler = SIG_DFL;
198 			}
199 			(*(void (*)(int, siginfo_t*, void*))p_sig.handler)(signo, siginfo, context);
200 		} else {
201 			(*(void (*)(int))p_sig.handler)(signo);
202 		}
203 	}
204 
205 	errno = errno_save;
206 } /* }}} */
207 
208 /* {{{ zend_sigaction
209  *  Register a signal handler that will be deferred in critical sections */
zend_sigaction(int signo,const struct sigaction * act,struct sigaction * oldact)210 ZEND_API int zend_sigaction(int signo, const struct sigaction *act, struct sigaction *oldact)
211 {
212 	struct sigaction sa = {{0}};
213 	sigset_t sigset;
214 
215 	if (oldact != NULL) {
216 		oldact->sa_flags   = SIGG(handlers)[signo-1].flags;
217 		oldact->sa_handler = (void *) SIGG(handlers)[signo-1].handler;
218 		oldact->sa_mask    = global_sigmask;
219 	}
220 	if (act != NULL) {
221 		SIGG(handlers)[signo-1].flags = act->sa_flags;
222 		if (act->sa_flags & SA_SIGINFO) {
223 			SIGG(handlers)[signo-1].handler = (void *) act->sa_sigaction;
224 		} else {
225 			SIGG(handlers)[signo-1].handler = (void *) act->sa_handler;
226 		}
227 
228 		sa.sa_flags     = SA_SIGINFO | (act->sa_flags & SA_FLAGS_MASK);
229 		sa.sa_sigaction = zend_signal_handler_defer;
230 		sa.sa_mask      = global_sigmask;
231 
232 		if (sigaction(signo, &sa, NULL) < 0) {
233 			zend_error_noreturn(E_ERROR, "Error installing signal handler for %d", signo);
234 		}
235 
236 		/* unsure this signal is not blocked */
237 		sigemptyset(&sigset);
238 		sigaddset(&sigset, signo);
239 		zend_sigprocmask(SIG_UNBLOCK, &sigset, NULL);
240 	}
241 
242 	return SUCCESS;
243 }
244 /* }}} */
245 
246 /* {{{ zend_signal
247  *  Register a signal handler that will be deferred in critical sections */
zend_signal(int signo,void (* handler)(int))248 ZEND_API int zend_signal(int signo, void (*handler)(int))
249 {
250 	struct sigaction sa = {{0}};
251 
252 	sa.sa_flags   = 0;
253 	sa.sa_handler = handler;
254 	sa.sa_mask    = global_sigmask;
255 
256 	return zend_sigaction(signo, &sa, NULL);
257 }
258 /* }}} */
259 
260 /* {{{ zend_signal_register
261  *  Set a handler for a signal we want to defer.
262  *  Previously set handler must have been saved before.
263  */
zend_signal_register(int signo,void (* handler)(int,siginfo_t *,void *))264 static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*))
265 {
266 	struct sigaction sa = {{0}};
267 
268 	if (sigaction(signo, NULL, &sa) == 0) {
269 		if ((sa.sa_flags & SA_SIGINFO) && sa.sa_sigaction == handler) {
270 			return FAILURE;
271 		}
272 
273 		SIGG(handlers)[signo-1].flags = sa.sa_flags;
274 		if (sa.sa_flags & SA_SIGINFO) {
275 			SIGG(handlers)[signo-1].handler = (void *)sa.sa_sigaction;
276 		} else {
277 			SIGG(handlers)[signo-1].handler = (void *)sa.sa_handler;
278 		}
279 
280 		sa.sa_flags     = SA_SIGINFO; /* we'll use a siginfo handler */
281 		sa.sa_sigaction = handler;
282 		sa.sa_mask      = global_sigmask;
283 
284 		if (sigaction(signo, &sa, NULL) < 0) {
285 			zend_error_noreturn(E_ERROR, "Error installing signal handler for %d", signo);
286 		}
287 
288 		return SUCCESS;
289 	}
290 	return FAILURE;
291 } /* }}} */
292 
293 /* {{{ zend_signal_activate
294  *  Install our signal handlers, per request */
zend_signal_activate(void)295 void zend_signal_activate(void)
296 {
297 	int x;
298 
299 	memcpy(&SIGG(handlers), &global_orig_handlers, sizeof(global_orig_handlers));
300 
301 	for (x = 0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) {
302 		zend_signal_register(zend_sigs[x], zend_signal_handler_defer);
303 	}
304 
305 	SIGG(active) = 1;
306 	SIGG(depth)  = 0;
307 } /* }}} */
308 
309 /* {{{ zend_signal_deactivate
310  * */
zend_signal_deactivate(void)311 void zend_signal_deactivate(void)
312 {
313 
314 	if (SIGG(check)) {
315 		int x;
316 		struct sigaction sa = {{0}};
317 		if (SIGG(depth) != 0) {
318 			zend_error(E_CORE_WARNING, "zend_signal: shutdown with non-zero blocking depth (%d)", SIGG(depth));
319 		}
320 		/* did anyone steal our installed handler */
321 		for (x = 0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) {
322 			sigaction(zend_sigs[x], NULL, &sa);
323 			if (sa.sa_sigaction != zend_signal_handler_defer) {
324 				zend_error(E_CORE_WARNING, "zend_signal: handler was replaced for signal (%d) after startup", zend_sigs[x]);
325 			}
326 		}
327 	}
328 
329 	SIGNAL_BEGIN_CRITICAL();
330 	SIGG(active) = 0;
331 	SIGG(running) = 0;
332 	SIGG(blocked) = 0;
333 	SIGG(depth) = 0;
334 	SIGNAL_END_CRITICAL();
335 }
336 /* }}} */
337 
zend_signal_globals_ctor(zend_signal_globals_t * zend_signal_globals)338 static void zend_signal_globals_ctor(zend_signal_globals_t *zend_signal_globals) /* {{{ */
339 {
340 	size_t x;
341 
342 	memset(zend_signal_globals, 0, sizeof(*zend_signal_globals));
343 
344 	for (x = 0; x < sizeof(zend_signal_globals->pstorage) / sizeof(*zend_signal_globals->pstorage); ++x) {
345 		zend_signal_queue_t *queue = &zend_signal_globals->pstorage[x];
346 		queue->zend_signal.signo = 0;
347 		queue->next = zend_signal_globals->pavail;
348 		zend_signal_globals->pavail = queue;
349 	}
350 }
351 /* }}} */
352 
zend_signal_init()353 void zend_signal_init() /* {{{ */
354 {
355 	int signo;
356 	struct sigaction sa = {{0}};
357 
358 	/* Save previously registered signal handlers into orig_handlers */
359 	memset(&global_orig_handlers, 0, sizeof(global_orig_handlers));
360 	for (signo = 1; signo < NSIG; ++signo) {
361 		if (sigaction(signo, NULL, &sa) == 0) {
362 			global_orig_handlers[signo-1].flags = sa.sa_flags;
363 			if (sa.sa_flags & SA_SIGINFO) {
364 				global_orig_handlers[signo-1].handler = (void *) sa.sa_sigaction;
365 			} else {
366 				global_orig_handlers[signo-1].handler = (void *) sa.sa_handler;
367 			}
368 		}
369 	}
370 }
371 /* }}} */
372 
373 /* {{{ zend_signal_startup
374  * alloc zend signal globals */
zend_signal_startup()375 void zend_signal_startup()
376 {
377 
378 #ifdef ZTS
379 	ts_allocate_id(&zend_signal_globals_id, sizeof(zend_signal_globals_t), (ts_allocate_ctor) zend_signal_globals_ctor, NULL);
380 #else
381 	zend_signal_globals_ctor(&zend_signal_globals);
382 #endif
383 
384 	/* Used to block signals during execution of signal handlers */
385 	sigfillset(&global_sigmask);
386 	sigdelset(&global_sigmask, SIGILL);
387 	sigdelset(&global_sigmask, SIGABRT);
388 	sigdelset(&global_sigmask, SIGFPE);
389 	sigdelset(&global_sigmask, SIGKILL);
390 	sigdelset(&global_sigmask, SIGSEGV);
391 	sigdelset(&global_sigmask, SIGCONT);
392 	sigdelset(&global_sigmask, SIGSTOP);
393 	sigdelset(&global_sigmask, SIGTSTP);
394 	sigdelset(&global_sigmask, SIGTTIN);
395 	sigdelset(&global_sigmask, SIGTTOU);
396 #ifdef SIGBUS
397 	sigdelset(&global_sigmask, SIGBUS);
398 #endif
399 #ifdef SIGSYS
400 	sigdelset(&global_sigmask, SIGSYS);
401 #endif
402 #ifdef SIGTRAP
403 	sigdelset(&global_sigmask, SIGTRAP);
404 #endif
405 
406 	zend_signal_init();
407 }
408 /* }}} */
409 
410 
411 #endif /* ZEND_SIGNALS */
412 
413 /*
414  * Local variables:
415  * tab-width: 4
416  * c-basic-offset: 4
417  * indent-tabs-mode: t
418  * End:
419  * vim600: fdm=marker
420  * vim: noet sw=4 ts=4
421  */
422