-
Notifications
You must be signed in to change notification settings - Fork 7.8k
/
Copy pathzend_max_execution_timer.c
140 lines (114 loc) · 4.17 KB
/
zend_max_execution_timer.c
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