Coverage Report

Created: 2025-06-13 06:43

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