1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Kévin Dunglas <kevin@dunglas.dev> |
+----------------------------------------------------------------------+
*/
#ifdef ZEND_MAX_EXECUTION_TIMERS
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <sys/syscall.h>
#include <sys/types.h>
# ifdef __FreeBSD__
# include <pthread_np.h>
# endif
#include "zend.h"
#include "zend_globals.h"
#include "zend_portability.h"
#if __has_feature(memory_sanitizer)
# include <sanitizer/msan_interface.h>
#endif
// Musl Libc defines this macro, glibc does not
// According to "man 2 timer_create" this field should always be available, but it's not: https://sourceware.org/bugzilla/show_bug.cgi?id=27417
# ifndef sigev_notify_thread_id
# define sigev_notify_thread_id _sigev_un._tid
# endif
// FreeBSD doesn't support CLOCK_BOOTTIME
# ifdef __FreeBSD__
# define ZEND_MAX_EXECUTION_TIMERS_CLOCK CLOCK_MONOTONIC
# else
# define ZEND_MAX_EXECUTION_TIMERS_CLOCK CLOCK_BOOTTIME
# endif
ZEND_API void zend_max_execution_timer_init(void) /* {{{ */
{
pid_t pid = getpid();
if (EG(pid) == pid) {
return;
}
struct sigevent sev;
sev.sigev_notify = SIGEV_THREAD_ID;
sev.sigev_value.sival_ptr = &EG(max_execution_timer_timer);
sev.sigev_signo = SIGRTMIN;
# ifdef __FreeBSD__
sev.sigev_notify_thread_id = pthread_getthreadid_np();
# else
sev.sigev_notify_thread_id = (pid_t) syscall(SYS_gettid);
# endif
#if __has_feature(memory_sanitizer)
/* MSan does not intercept timer_create() */
__msan_unpoison(&EG(max_execution_timer_timer),
sizeof(EG(max_execution_timer_timer)));
#endif
// Measure wall time instead of CPU time as originally planned now that it is possible https://github.com/php/php-src/pull/6504#issuecomment-1370303727
if (timer_create(ZEND_MAX_EXECUTION_TIMERS_CLOCK, &sev, &EG(max_execution_timer_timer)) != 0) {
zend_strerror_noreturn(E_ERROR, errno, "Could not create timer");
}
EG(pid) = pid;
# ifdef MAX_EXECUTION_TIMERS_DEBUG
fprintf(stderr, "Timer %#jx created on thread %d\n", (uintmax_t) EG(max_execution_timer_timer), sev.sigev_notify_thread_id);
# endif
sigaction(sev.sigev_signo, NULL, &EG(oldact));
}
/* }}} */
void zend_max_execution_timer_settime(zend_long seconds) /* {{{ }*/
{
/* Timer not initialized or shutdown. */
if (!EG(pid)) {
return;
}
timer_t timer = EG(max_execution_timer_timer);
// Prevent EINVAL error
if (seconds < 0 || seconds > 999999999) {
seconds = 0;
}
struct itimerspec its;
its.it_value.tv_sec = seconds;
its.it_value.tv_nsec = its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
# ifdef MAX_EXECUTION_TIMERS_DEBUG
fprintf(stderr, "Setting timer %#jx on thread %d (%ld seconds)...\n", (uintmax_t) timer, (pid_t) syscall(SYS_gettid), seconds);
# endif
if (timer_settime(timer, 0, &its, NULL) != 0) {
zend_strerror_noreturn(E_ERROR, errno, "Could not set timer");
}
}
/* }}} */
void zend_max_execution_timer_shutdown(void) /* {{{ */
{
/* Don't try to delete a timer created before a call to fork() */
if (EG(pid) != getpid()) {
return;
}
EG(pid) = 0;
timer_t timer = EG(max_execution_timer_timer);
# ifdef MAX_EXECUTION_TIMERS_DEBUG
fprintf(stderr, "Deleting timer %#jx on thread %d...\n", (uintmax_t) timer, (pid_t) syscall(SYS_gettid));
# endif
int err = timer_delete(timer);
if (err != 0) {
zend_strerror_noreturn(E_ERROR, errno, "Could not delete timer");
}
}
/* }}}} */
#endif
|