xref: /PHP-8.1/Zend/zend_signal.c (revision 06ae7500)
1 /*
2   +----------------------------------------------------------------------+
3   | Zend Signal Handling                                                 |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 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   | https://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 #define _GNU_SOURCE
29 #include <string.h>
30 
31 #include "zend.h"
32 #include "zend_globals.h"
33 #include <signal.h>
34 
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 
39 #ifdef ZEND_SIGNALS
40 
41 #include "zend_signal.h"
42 
43 #ifdef ZTS
44 ZEND_API int zend_signal_globals_id;
45 ZEND_API size_t zend_signal_globals_offset;
46 #else
47 ZEND_API zend_signal_globals_t zend_signal_globals;
48 #endif /* not ZTS */
49 
50 #define SIGNAL_BEGIN_CRITICAL() \
51 	sigset_t oldmask; \
52 	zend_sigprocmask(SIG_BLOCK, &global_sigmask, &oldmask);
53 #define SIGNAL_END_CRITICAL() \
54 	zend_sigprocmask(SIG_SETMASK, &oldmask, NULL);
55 
56 #ifdef ZTS
57 # define zend_sigprocmask(signo, set, oldset) tsrm_sigmask((signo), (set), (oldset))
58 #else
59 # define zend_sigprocmask(signo, set, oldset) sigprocmask((signo), (set), (oldset))
60 #endif
61 
62 static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context);
63 static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*));
64 
65 #if defined(__CYGWIN__) || defined(__PASE__)
66 /* Matches zend_execute_API.c; these platforms don't support ITIMER_PROF. */
67 #define TIMEOUT_SIG SIGALRM
68 #else
69 #define TIMEOUT_SIG SIGPROF
70 #endif
71 
72 static int zend_sigs[] = { TIMEOUT_SIG, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2 };
73 
74 #define SA_FLAGS_MASK ~(SA_NODEFER | SA_RESETHAND)
75 
76 /* True globals, written only at process startup */
77 static zend_signal_entry_t global_orig_handlers[NSIG];
78 static sigset_t            global_sigmask;
79 
80 /* {{{ zend_signal_handler_defer
81  *  Blocks signals if in critical section */
zend_signal_handler_defer(int signo,siginfo_t * siginfo,void * context)82 void zend_signal_handler_defer(int signo, siginfo_t *siginfo, void *context)
83 {
84 	int errno_save = errno;
85 	zend_signal_queue_t *queue, *qtmp;
86 
87 #ifdef ZTS
88 	/* A signal could hit after TSRM shutdown, in this case globals are already freed.
89 	 * Or it could be delivered to a thread that didn't execute PHP yet.
90 	 * In the latter case we act as if SIGG(active) is false. */
91 	if (tsrm_is_shutdown() || !tsrm_get_ls_cache()) {
92 		/* Forward to default handler handler */
93 		zend_signal_handler(signo, siginfo, context);
94 		return;
95 	}
96 #endif
97 
98 	if (EXPECTED(SIGG(active))) {
99 		if (UNEXPECTED(SIGG(depth) == 0)) { /* try to handle signal */
100 			if (UNEXPECTED(SIGG(blocked))) {
101 				SIGG(blocked) = 0;
102 			}
103 			if (EXPECTED(SIGG(running) == 0)) {
104 				SIGG(running) = 1;
105 				zend_signal_handler(signo, siginfo, context);
106 
107 				queue = SIGG(phead);
108 				SIGG(phead) = NULL;
109 
110 				while (queue) {
111 					zend_signal_handler(queue->zend_signal.signo, queue->zend_signal.siginfo, queue->zend_signal.context);
112 					qtmp = queue->next;
113 					queue->next = SIGG(pavail);
114 					queue->zend_signal.signo = 0;
115 					SIGG(pavail) = queue;
116 					queue = qtmp;
117 				}
118 				SIGG(running) = 0;
119 			}
120 		} else { /* delay signal handling */
121 			SIGG(blocked) = 1; /* signal is blocked */
122 
123 			if ((queue = SIGG(pavail))) { /* if none available it's simply forgotten */
124 				SIGG(pavail) = queue->next;
125 				queue->zend_signal.signo = signo;
126 				queue->zend_signal.siginfo = siginfo;
127 				queue->zend_signal.context = context;
128 				queue->next = NULL;
129 
130 				if (SIGG(phead) && SIGG(ptail)) {
131 					SIGG(ptail)->next = queue;
132 				} else {
133 					SIGG(phead) = queue;
134 				}
135 				SIGG(ptail) = queue;
136 			}
137 #if ZEND_DEBUG
138 			else { /* this may not be safe to do, but could work and be useful */
139 				zend_output_debug_string(0, "zend_signal: not enough queue storage, lost signal (%d)", signo);
140 			}
141 #endif
142 		}
143 	} else {
144 		/* need to just run handler if we're inactive and getting a signal */
145 		zend_signal_handler(signo, siginfo, context);
146 	}
147 
148 	errno = errno_save;
149 } /* }}} */
150 
151 /* {{{ zend_signal_handler_unblock
152  * Handle deferred signal from HANDLE_UNBLOCK_ALARMS */
zend_signal_handler_unblock(void)153 ZEND_API void zend_signal_handler_unblock(void)
154 {
155 	zend_signal_queue_t *queue;
156 	zend_signal_t zend_signal;
157 
158 	if (EXPECTED(SIGG(active))) {
159 		SIGNAL_BEGIN_CRITICAL(); /* procmask to protect handler_defer as if it were called by the kernel */
160 		queue = SIGG(phead);
161 		SIGG(phead) = queue->next;
162 		zend_signal = queue->zend_signal;
163 		queue->next = SIGG(pavail);
164 		queue->zend_signal.signo = 0;
165 		SIGG(pavail) = queue;
166 
167 		zend_signal_handler_defer(zend_signal.signo, zend_signal.siginfo, zend_signal.context);
168 		SIGNAL_END_CRITICAL();
169 	}
170 }
171 /* }}} */
172 
173 /* {{{ zend_signal_handler
174  *  Call the previously registered handler for a signal
175  */
zend_signal_handler(int signo,siginfo_t * siginfo,void * context)176 static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context)
177 {
178 	int errno_save = errno;
179 	struct sigaction sa;
180 	sigset_t sigset;
181 	zend_signal_entry_t p_sig;
182 #ifdef ZTS
183 	if (tsrm_is_shutdown() || !tsrm_get_ls_cache()) {
184 		p_sig.flags = 0;
185 		p_sig.handler = SIG_DFL;
186 	} else
187 #endif
188 	p_sig = SIGG(handlers)[signo-1];
189 
190 	if (p_sig.handler == SIG_DFL) { /* raise default handler */
191 		if (sigaction(signo, NULL, &sa) == 0) {
192 			sa.sa_handler = SIG_DFL;
193 			sigemptyset(&sa.sa_mask);
194 
195 			sigemptyset(&sigset);
196 			sigaddset(&sigset, signo);
197 
198 			if (sigaction(signo, &sa, NULL) == 0) {
199 				/* throw away any blocked signals */
200 				zend_sigprocmask(SIG_UNBLOCK, &sigset, NULL);
201 #ifdef ZTS
202 # define RAISE_ERROR "raise() failed\n"
203 				if (raise(signo) != 0) {
204 					/* On some systems raise() fails with errno 3: No such process */
205 					kill(getpid(), signo);
206 				}
207 #else
208 				kill(getpid(), signo);
209 #endif
210 			}
211 		}
212 	} else if (p_sig.handler != SIG_IGN) {
213 		if (p_sig.flags & SA_SIGINFO) {
214 			if (p_sig.flags & SA_RESETHAND) {
215 				SIGG(handlers)[signo-1].flags   = 0;
216 				SIGG(handlers)[signo-1].handler = SIG_DFL;
217 			}
218 			(*(void (*)(int, siginfo_t*, void*))p_sig.handler)(signo, siginfo, context);
219 		} else {
220 			(*(void (*)(int))p_sig.handler)(signo);
221 		}
222 	}
223 
224 	errno = errno_save;
225 } /* }}} */
226 
227 /* {{{ zend_sigaction
228  *  Register a signal handler that will be deferred in critical sections */
zend_sigaction(int signo,const struct sigaction * act,struct sigaction * oldact)229 ZEND_API void zend_sigaction(int signo, const struct sigaction *act, struct sigaction *oldact)
230 {
231 	struct sigaction sa;
232 	sigset_t sigset;
233 
234 	if (oldact != NULL) {
235 		oldact->sa_flags   = SIGG(handlers)[signo-1].flags;
236 		oldact->sa_handler = (void *) SIGG(handlers)[signo-1].handler;
237 		oldact->sa_mask    = global_sigmask;
238 	}
239 	if (act != NULL) {
240 		SIGG(handlers)[signo-1].flags = act->sa_flags;
241 		if (act->sa_flags & SA_SIGINFO) {
242 			SIGG(handlers)[signo-1].handler = (void *) act->sa_sigaction;
243 		} else {
244 			SIGG(handlers)[signo-1].handler = (void *) act->sa_handler;
245 		}
246 
247 		memset(&sa, 0, sizeof(sa));
248 		if (SIGG(handlers)[signo-1].handler == (void *) SIG_IGN) {
249 			sa.sa_sigaction = (void *) SIG_IGN;
250 		} else {
251 			sa.sa_flags     = SA_SIGINFO | (act->sa_flags & SA_FLAGS_MASK);
252 			sa.sa_sigaction = zend_signal_handler_defer;
253 			sa.sa_mask      = global_sigmask;
254 		}
255 
256 		if (sigaction(signo, &sa, NULL) < 0) {
257 			zend_error_noreturn(E_ERROR, "Error installing signal handler for %d", signo);
258 		}
259 
260 		/* unsure this signal is not blocked */
261 		sigemptyset(&sigset);
262 		sigaddset(&sigset, signo);
263 		zend_sigprocmask(SIG_UNBLOCK, &sigset, NULL);
264 	}
265 }
266 /* }}} */
267 
268 /* {{{ zend_signal
269  *  Register a signal handler that will be deferred in critical sections */
zend_signal(int signo,void (* handler)(int))270 ZEND_API void zend_signal(int signo, void (*handler)(int))
271 {
272 	struct sigaction sa;
273 
274 	memset(&sa, 0, sizeof(sa));
275 	sa.sa_flags   = 0;
276 	sa.sa_handler = handler;
277 	sa.sa_mask    = global_sigmask;
278 
279 	zend_sigaction(signo, &sa, NULL);
280 }
281 /* }}} */
282 
283 /* {{{ zend_signal_register
284  *  Set a handler for a signal we want to defer.
285  *  Previously set handler must have been saved before.
286  */
zend_signal_register(int signo,void (* handler)(int,siginfo_t *,void *))287 static zend_result zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*))
288 {
289 	struct sigaction sa;
290 
291 	if (sigaction(signo, NULL, &sa) == 0) {
292 		if ((sa.sa_flags & SA_SIGINFO) && sa.sa_sigaction == handler) {
293 			return FAILURE;
294 		}
295 
296 		SIGG(handlers)[signo-1].flags = sa.sa_flags;
297 		if (sa.sa_flags & SA_SIGINFO) {
298 			SIGG(handlers)[signo-1].handler = (void *)sa.sa_sigaction;
299 		} else {
300 			SIGG(handlers)[signo-1].handler = (void *)sa.sa_handler;
301 		}
302 
303 		sa.sa_flags     = SA_SIGINFO; /* we'll use a siginfo handler */
304 		sa.sa_sigaction = handler;
305 		sa.sa_mask      = global_sigmask;
306 
307 		if (sigaction(signo, &sa, NULL) < 0) {
308 			zend_error_noreturn(E_ERROR, "Error installing signal handler for %d", signo);
309 		}
310 
311 		return SUCCESS;
312 	}
313 	return FAILURE;
314 } /* }}} */
315 
316 /* {{{ zend_signal_activate
317  *  Install our signal handlers, per request */
zend_signal_activate(void)318 void zend_signal_activate(void)
319 {
320 	size_t x;
321 
322 	memcpy(&SIGG(handlers), &global_orig_handlers, sizeof(global_orig_handlers));
323 
324 	if (SIGG(reset)) {
325 		for (x = 0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) {
326 			zend_signal_register(zend_sigs[x], zend_signal_handler_defer);
327 		}
328 	}
329 
330 	SIGG(active) = 1;
331 	SIGG(depth)  = 0;
332 } /* }}} */
333 
334 /* {{{ zend_signal_deactivate */
zend_signal_deactivate(void)335 void zend_signal_deactivate(void)
336 {
337 	if (SIGG(check)) {
338 		size_t x;
339 		struct sigaction sa;
340 
341 		if (SIGG(depth) != 0) {
342 			zend_error(E_CORE_WARNING, "zend_signal: shutdown with non-zero blocking depth (%d)", SIGG(depth));
343 		}
344 
345 		/* did anyone steal our installed handler */
346 		for (x = 0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) {
347 			sigaction(zend_sigs[x], NULL, &sa);
348 			if (sa.sa_sigaction != zend_signal_handler_defer &&
349 					sa.sa_sigaction != (void *) SIG_IGN) {
350 				zend_error(E_CORE_WARNING, "zend_signal: handler was replaced for signal (%d) after startup", zend_sigs[x]);
351 			}
352 		}
353 	}
354 
355 	/* After active=0 is set, signal handlers will be called directly and other
356 	 * state that is reset below will not be accessed. */
357 	*((volatile int *) &SIGG(active)) = 0;
358 
359 	SIGG(running) = 0;
360 	SIGG(blocked) = 0;
361 	SIGG(depth) = 0;
362 
363 	/* If there are any queued signals because of a missed unblock, drop them. */
364 	if (SIGG(phead) && SIGG(ptail)) {
365 		SIGG(ptail)->next = SIGG(pavail);
366 		SIGG(pavail) = SIGG(phead);
367 		SIGG(phead) = NULL;
368 		SIGG(ptail) = NULL;
369 	}
370 }
371 /* }}} */
372 
zend_signal_globals_ctor(zend_signal_globals_t * zend_signal_globals)373 static void zend_signal_globals_ctor(zend_signal_globals_t *zend_signal_globals) /* {{{ */
374 {
375 	size_t x;
376 
377 	memset(zend_signal_globals, 0, sizeof(*zend_signal_globals));
378 	zend_signal_globals->reset = 1;
379 
380 	for (x = 0; x < sizeof(zend_signal_globals->pstorage) / sizeof(*zend_signal_globals->pstorage); ++x) {
381 		zend_signal_queue_t *queue = &zend_signal_globals->pstorage[x];
382 		queue->zend_signal.signo = 0;
383 		queue->next = zend_signal_globals->pavail;
384 		zend_signal_globals->pavail = queue;
385 	}
386 }
387 /* }}} */
388 
zend_signal_init(void)389 void zend_signal_init(void) /* {{{ */
390 {
391 	int signo;
392 	struct sigaction sa;
393 
394 	/* Save previously registered signal handlers into orig_handlers */
395 	memset(&global_orig_handlers, 0, sizeof(global_orig_handlers));
396 	for (signo = 1; signo < NSIG; ++signo) {
397 		if (sigaction(signo, NULL, &sa) == 0) {
398 			global_orig_handlers[signo-1].flags = sa.sa_flags;
399 			if (sa.sa_flags & SA_SIGINFO) {
400 				global_orig_handlers[signo-1].handler = (void *) sa.sa_sigaction;
401 			} else {
402 				global_orig_handlers[signo-1].handler = (void *) sa.sa_handler;
403 			}
404 		}
405 	}
406 }
407 /* }}} */
408 
409 /* {{{ zend_signal_startup
410  * alloc zend signal globals */
zend_signal_startup(void)411 ZEND_API void zend_signal_startup(void)
412 {
413 
414 #ifdef ZTS
415 	ts_allocate_fast_id(&zend_signal_globals_id, &zend_signal_globals_offset, sizeof(zend_signal_globals_t), (ts_allocate_ctor) zend_signal_globals_ctor, NULL);
416 #else
417 	zend_signal_globals_ctor(&zend_signal_globals);
418 #endif
419 
420 	/* Used to block signals during execution of signal handlers */
421 	sigfillset(&global_sigmask);
422 	sigdelset(&global_sigmask, SIGILL);
423 	sigdelset(&global_sigmask, SIGABRT);
424 	sigdelset(&global_sigmask, SIGFPE);
425 	sigdelset(&global_sigmask, SIGKILL);
426 	sigdelset(&global_sigmask, SIGSEGV);
427 	sigdelset(&global_sigmask, SIGCONT);
428 	sigdelset(&global_sigmask, SIGSTOP);
429 	sigdelset(&global_sigmask, SIGTSTP);
430 	sigdelset(&global_sigmask, SIGTTIN);
431 	sigdelset(&global_sigmask, SIGTTOU);
432 #ifdef SIGBUS
433 	sigdelset(&global_sigmask, SIGBUS);
434 #endif
435 #ifdef SIGSYS
436 	sigdelset(&global_sigmask, SIGSYS);
437 #endif
438 #ifdef SIGTRAP
439 	sigdelset(&global_sigmask, SIGTRAP);
440 #endif
441 
442 	zend_signal_init();
443 }
444 /* }}} */
445 
446 
447 #endif /* ZEND_SIGNALS */
448