Skip to content

Commit 85b669e

Browse files
committed
Merge branch 'PHP-8.0' into PHP-8.1
2 parents ba6bb85 + e6cf583 commit 85b669e

File tree

9 files changed

+113
-115
lines changed

9 files changed

+113
-115
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ PHP NEWS
1010
. Fixed bug GH-7958 (Nested CallbackFilterIterator is leaking memory). (cmb)
1111
. Fixed bug GH-8074 (Wrong type inference of range() result). (cmb)
1212
. Fixed bug GH-8140 (Wrong first class callable by name optimization). (cmb)
13+
. Fixed bug GH-8082 (op_arrays with temporary run_time_cache leak memory
14+
when observed). (Bob)
1315

1416
- GD:
1517
. Fixed libpng warning when loading interlaced images. (Brett)

Zend/zend.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,8 +1249,6 @@ ZEND_API void zend_deactivate(void) /* {{{ */
12491249
/* we're no longer executing anything */
12501250
EG(current_execute_data) = NULL;
12511251

1252-
zend_observer_deactivate();
1253-
12541252
zend_try {
12551253
shutdown_scanner();
12561254
} zend_end_try();

Zend/zend_extensions.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,17 @@ ZEND_API int zend_get_resource_handle(const char *module_name)
267267

268268
ZEND_API int zend_get_op_array_extension_handle(const char *module_name)
269269
{
270+
int handle = zend_op_array_extension_handles++;
270271
zend_add_system_entropy(module_name, "zend_get_op_array_extension_handle", &zend_op_array_extension_handles, sizeof(int));
271-
return zend_op_array_extension_handles++;
272+
return handle;
273+
}
274+
275+
ZEND_API int zend_get_op_array_extension_handles(const char *module_name, int handles)
276+
{
277+
int handle = zend_op_array_extension_handles;
278+
zend_op_array_extension_handles += handles;
279+
zend_add_system_entropy(module_name, "zend_get_op_array_extension_handle", &zend_op_array_extension_handles, sizeof(int));
280+
return handle;
272281
}
273282

274283
ZEND_API zend_extension *zend_get_extension(const char *extension_name)

Zend/zend_extensions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ extern ZEND_API int zend_op_array_extension_handles;
115115

116116
ZEND_API int zend_get_resource_handle(const char *module_name);
117117
ZEND_API int zend_get_op_array_extension_handle(const char *module_name);
118+
ZEND_API int zend_get_op_array_extension_handles(const char *module_name, int handles);
118119
ZEND_API void zend_extension_dispatch_message(int message, void *arg);
119120
END_EXTERN_C()
120121

Zend/zend_observer.c

Lines changed: 94 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@
3131
#define ZEND_OBSERVABLE_FN(fn_flags) \
3232
(!(fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
3333

34-
typedef struct _zend_observer_fcall_data {
35-
// points after the last handler
36-
zend_observer_fcall_handlers *end;
37-
// a variadic array using "struct hack"
38-
zend_observer_fcall_handlers handlers[1];
39-
} zend_observer_fcall_data;
40-
4134
zend_llist zend_observers_fcall_list;
4235
zend_llist zend_observer_error_callbacks;
4336
zend_llist zend_observer_fiber_init;
@@ -46,33 +39,18 @@ zend_llist zend_observer_fiber_destroy;
4639

4740
int zend_observer_fcall_op_array_extension;
4841

49-
ZEND_TLS zend_arena *fcall_handlers_arena;
5042
ZEND_TLS zend_execute_data *first_observed_frame;
5143
ZEND_TLS zend_execute_data *current_observed_frame;
5244

5345
// Call during minit/startup ONLY
54-
ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init init) {
55-
if (!ZEND_OBSERVER_ENABLED) {
56-
/* We don't want to get an extension handle unless an ext installs an observer */
57-
zend_observer_fcall_op_array_extension =
58-
zend_get_op_array_extension_handle("Zend Observer");
59-
60-
/* ZEND_CALL_TRAMPOLINE has SPEC(OBSERVER) but zend_init_call_trampoline_op()
61-
* is called before any extensions have registered as an observer. So we
62-
* adjust the offset to the observed handler when we know we need to observe. */
63-
ZEND_VM_SET_OPCODE_HANDLER(&EG(call_trampoline_op));
64-
65-
/* ZEND_HANDLE_EXCEPTION also has SPEC(OBSERVER) and no observer extensions
66-
* exist when zend_init_exception_op() is called. */
67-
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op));
68-
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op)+1);
69-
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op)+2);
70-
}
46+
ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init init)
47+
{
7148
zend_llist_add_element(&zend_observers_fcall_list, &init);
7249
}
7350

7451
// Called by engine before MINITs
75-
ZEND_API void zend_observer_startup(void) {
52+
ZEND_API void zend_observer_startup(void)
53+
{
7654
zend_llist_init(&zend_observers_fcall_list, sizeof(zend_observer_fcall_init), NULL, 1);
7755
zend_llist_init(&zend_observer_error_callbacks, sizeof(zend_observer_error_cb), NULL, 1);
7856
zend_llist_init(&zend_observer_fiber_init, sizeof(zend_observer_fiber_init_handler), NULL, 1);
@@ -82,110 +60,115 @@ ZEND_API void zend_observer_startup(void) {
8260
zend_observer_fcall_op_array_extension = -1;
8361
}
8462

85-
ZEND_API void zend_observer_activate(void) {
86-
if (ZEND_OBSERVER_ENABLED) {
87-
fcall_handlers_arena = zend_arena_create(4096);
88-
} else {
89-
fcall_handlers_arena = NULL;
63+
ZEND_API void zend_observer_post_startup(void)
64+
{
65+
if (zend_observers_fcall_list.count) {
66+
/* We don't want to get an extension handle unless an ext installs an observer
67+
* Allocate each a begin and an end pointer */
68+
zend_observer_fcall_op_array_extension =
69+
zend_get_op_array_extension_handles("Zend Observer", (int) zend_observers_fcall_list.count * 2);
70+
71+
/* ZEND_CALL_TRAMPOLINE has SPEC(OBSERVER) but zend_init_call_trampoline_op()
72+
* is called before any extensions have registered as an observer. So we
73+
* adjust the offset to the observed handler when we know we need to observe. */
74+
ZEND_VM_SET_OPCODE_HANDLER(&EG(call_trampoline_op));
75+
76+
/* ZEND_HANDLE_EXCEPTION also has SPEC(OBSERVER) and no observer extensions
77+
* exist when zend_init_exception_op() is called. */
78+
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op));
79+
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op) + 1);
80+
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op) + 2);
9081
}
82+
}
83+
84+
ZEND_API void zend_observer_activate(void)
85+
{
9186
first_observed_frame = NULL;
9287
current_observed_frame = NULL;
9388
}
9489

95-
ZEND_API void zend_observer_deactivate(void) {
96-
if (fcall_handlers_arena) {
97-
zend_arena_destroy(fcall_handlers_arena);
98-
}
90+
ZEND_API void zend_observer_deactivate(void)
91+
{
92+
// now empty and unused, but kept for ABI compatibility
9993
}
10094

101-
ZEND_API void zend_observer_shutdown(void) {
95+
ZEND_API void zend_observer_shutdown(void)
96+
{
10297
zend_llist_destroy(&zend_observers_fcall_list);
10398
zend_llist_destroy(&zend_observer_error_callbacks);
10499
zend_llist_destroy(&zend_observer_fiber_switch);
105100
}
106101

107-
static void zend_observer_fcall_install(zend_execute_data *execute_data) {
108-
zend_llist_element *element;
102+
static void zend_observer_fcall_install(zend_execute_data *execute_data)
103+
{
109104
zend_llist *list = &zend_observers_fcall_list;
110105
zend_function *function = execute_data->func;
111106
zend_op_array *op_array = &function->op_array;
112107

113-
if (fcall_handlers_arena == NULL) {
114-
return;
115-
}
116-
117108
ZEND_ASSERT(function->type != ZEND_INTERNAL_FUNCTION);
118109

119-
zend_llist handlers_list;
120-
zend_llist_init(&handlers_list, sizeof(zend_observer_fcall_handlers), NULL, 0);
121-
for (element = list->head; element; element = element->next) {
110+
ZEND_ASSERT(RUN_TIME_CACHE(op_array));
111+
zend_observer_fcall_begin_handler *begin_handlers = (zend_observer_fcall_begin_handler *)&ZEND_OBSERVER_DATA(op_array);
112+
zend_observer_fcall_end_handler *end_handlers = (zend_observer_fcall_end_handler *)begin_handlers + list->count, *end_handlers_start = end_handlers;
113+
114+
*begin_handlers = ZEND_OBSERVER_NOT_OBSERVED;
115+
*end_handlers = ZEND_OBSERVER_NOT_OBSERVED;
116+
117+
for (zend_llist_element *element = list->head; element; element = element->next) {
122118
zend_observer_fcall_init init;
123119
memcpy(&init, element->data, sizeof init);
124120
zend_observer_fcall_handlers handlers = init(execute_data);
125-
if (handlers.begin || handlers.end) {
126-
zend_llist_add_element(&handlers_list, &handlers);
121+
if (handlers.begin) {
122+
*(begin_handlers++) = handlers.begin;
127123
}
128-
}
129-
130-
ZEND_ASSERT(RUN_TIME_CACHE(op_array));
131-
void *ext;
132-
if (handlers_list.count) {
133-
size_t size = sizeof(zend_observer_fcall_data) + (handlers_list.count - 1) * sizeof(zend_observer_fcall_handlers);
134-
zend_observer_fcall_data *fcall_data = zend_arena_alloc(&fcall_handlers_arena, size);
135-
zend_observer_fcall_handlers *handlers = fcall_data->handlers;
136-
for (element = handlers_list.head; element; element = element->next) {
137-
memcpy(handlers++, element->data, sizeof *handlers);
124+
if (handlers.end) {
125+
*(end_handlers++) = handlers.end;
138126
}
139-
fcall_data->end = handlers;
140-
ext = fcall_data;
141-
} else {
142-
ext = ZEND_OBSERVER_NOT_OBSERVED;
143127
}
144-
145-
ZEND_OBSERVER_DATA(op_array) = ext;
146-
zend_llist_destroy(&handlers_list);
128+
129+
// end handlers are executed in reverse order
130+
for (--end_handlers; end_handlers_start < end_handlers; --end_handlers, ++end_handlers_start) {
131+
zend_observer_fcall_end_handler tmp = *end_handlers;
132+
*end_handlers = *end_handlers_start;
133+
*end_handlers_start = tmp;
134+
}
147135
}
148136

149137
static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_data)
150138
{
151-
zend_op_array *op_array;
152-
uint32_t fn_flags;
153-
zend_observer_fcall_data *fcall_data;
154-
zend_observer_fcall_handlers *handlers, *end;
155-
156139
if (!ZEND_OBSERVER_ENABLED) {
157140
return;
158141
}
159142

160-
op_array = &execute_data->func->op_array;
161-
fn_flags = op_array->fn_flags;
143+
zend_op_array *op_array = &execute_data->func->op_array;
144+
uint32_t fn_flags = op_array->fn_flags;
162145

163146
if (!ZEND_OBSERVABLE_FN(fn_flags)) {
164147
return;
165148
}
166149

167-
fcall_data = ZEND_OBSERVER_DATA(op_array);
168-
if (!fcall_data) {
150+
zend_observer_fcall_begin_handler *handler = (zend_observer_fcall_begin_handler *)&ZEND_OBSERVER_DATA(op_array);
151+
if (!*handler) {
169152
zend_observer_fcall_install(execute_data);
170-
fcall_data = ZEND_OBSERVER_DATA(op_array);
171153
}
172154

173-
ZEND_ASSERT(fcall_data);
174-
if (fcall_data == ZEND_OBSERVER_NOT_OBSERVED) {
175-
return;
176-
}
155+
zend_observer_fcall_begin_handler *possible_handlers_end = handler + zend_observers_fcall_list.count;
177156

178-
if (first_observed_frame == NULL) {
179-
first_observed_frame = execute_data;
157+
zend_observer_fcall_end_handler *end_handler = (zend_observer_fcall_end_handler *)possible_handlers_end;
158+
if (*end_handler != ZEND_OBSERVER_NOT_OBSERVED) {
159+
if (first_observed_frame == NULL) {
160+
first_observed_frame = execute_data;
161+
}
162+
current_observed_frame = execute_data;
180163
}
181-
current_observed_frame = execute_data;
182164

183-
end = fcall_data->end;
184-
for (handlers = fcall_data->handlers; handlers != end; ++handlers) {
185-
if (handlers->begin) {
186-
handlers->begin(execute_data);
187-
}
165+
if (*handler == ZEND_OBSERVER_NOT_OBSERVED) {
166+
return;
188167
}
168+
169+
do {
170+
(*handler)(execute_data);
171+
} while (++handler != possible_handlers_end && *handler != NULL);
189172
}
190173

191174
ZEND_API void ZEND_FASTCALL zend_observer_generator_resume(zend_execute_data *execute_data)
@@ -201,43 +184,48 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin(zend_execute_data *execute
201184
}
202185
}
203186

204-
ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(
205-
zend_execute_data *execute_data,
206-
zval *return_value)
187+
static inline bool zend_observer_is_skipped_frame(zend_execute_data *execute_data) {
188+
zend_function *func = execute_data->func;
189+
190+
if (!func || func->type == ZEND_INTERNAL_FUNCTION || !ZEND_OBSERVABLE_FN(func->common.fn_flags)) {
191+
return true;
192+
}
193+
194+
zend_observer_fcall_end_handler end_handler = (&ZEND_OBSERVER_DATA(&func->op_array))[zend_observers_fcall_list.count];
195+
if (end_handler == NULL || end_handler == ZEND_OBSERVER_NOT_OBSERVED) {
196+
return true;
197+
}
198+
199+
return false;
200+
}
201+
202+
ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(zend_execute_data *execute_data, zval *return_value)
207203
{
208204
zend_function *func = execute_data->func;
209-
zend_observer_fcall_data *fcall_data;
210-
zend_observer_fcall_handlers *handlers, *end;
211205

212206
if (!ZEND_OBSERVER_ENABLED
213207
|| !ZEND_OBSERVABLE_FN(func->common.fn_flags)) {
214208
return;
215209
}
216210

217-
fcall_data = (zend_observer_fcall_data*)ZEND_OBSERVER_DATA(&func->op_array);
211+
zend_observer_fcall_end_handler *handler = (zend_observer_fcall_end_handler *)&ZEND_OBSERVER_DATA(&func->op_array) + zend_observers_fcall_list.count;
218212
// TODO: Fix exceptions from generators
219213
// ZEND_ASSERT(fcall_data);
220-
if (!fcall_data || fcall_data == ZEND_OBSERVER_NOT_OBSERVED) {
214+
if (!*handler || *handler == ZEND_OBSERVER_NOT_OBSERVED) {
221215
return;
222216
}
223217

224-
handlers = fcall_data->end;
225-
end = fcall_data->handlers;
226-
while (handlers-- != end) {
227-
if (handlers->end) {
228-
handlers->end(execute_data, return_value);
229-
}
230-
}
218+
zend_observer_fcall_end_handler *possible_handlers_end = handler + zend_observers_fcall_list.count;
219+
do {
220+
(*handler)(execute_data, return_value);
221+
} while (++handler != possible_handlers_end && *handler != NULL);
231222

232223
if (first_observed_frame == execute_data) {
233224
first_observed_frame = NULL;
234225
current_observed_frame = NULL;
235226
} else {
236227
zend_execute_data *ex = execute_data->prev_execute_data;
237-
while (ex && (!ex->func || ex->func->type == ZEND_INTERNAL_FUNCTION
238-
|| !ZEND_OBSERVABLE_FN(ex->func->common.fn_flags)
239-
|| !ZEND_OBSERVER_DATA(&ex->func->op_array)
240-
|| ZEND_OBSERVER_DATA(&ex->func->op_array) == ZEND_OBSERVER_NOT_OBSERVED)) {
228+
while (ex && zend_observer_is_skipped_frame(ex)) {
241229
ex = ex->prev_execute_data;
242230
}
243231
current_observed_frame = ex;
@@ -253,7 +241,6 @@ ZEND_API void zend_observer_fcall_end_all(void)
253241
}
254242
ex = ex->prev_execute_data;
255243
}
256-
current_observed_frame = NULL;
257244
}
258245

259246
ZEND_API void zend_observer_error_register(zend_observer_error_cb cb)
@@ -263,11 +250,8 @@ ZEND_API void zend_observer_error_register(zend_observer_error_cb cb)
263250

264251
void zend_observer_error_notify(int type, zend_string *error_filename, uint32_t error_lineno, zend_string *message)
265252
{
266-
zend_llist_element *element;
267-
zend_observer_error_cb callback;
268-
269-
for (element = zend_observer_error_callbacks.head; element; element = element->next) {
270-
callback = *(zend_observer_error_cb *) (element->data);
253+
for (zend_llist_element *element = zend_observer_error_callbacks.head; element; element = element->next) {
254+
zend_observer_error_cb callback = *(zend_observer_error_cb *) (element->data);
271255
callback(type, error_filename, error_lineno, message);
272256
}
273257
}

Zend/zend_observer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ typedef zend_observer_fcall_handlers (*zend_observer_fcall_init)(zend_execute_da
5757
ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init);
5858

5959
ZEND_API void zend_observer_startup(void); // Called by engine before MINITs
60+
ZEND_API void zend_observer_post_startup(void); // Called by engine after MINITs
6061
ZEND_API void zend_observer_activate(void);
6162
ZEND_API void zend_observer_deactivate(void);
6263
ZEND_API void zend_observer_shutdown(void);

ext/zend_test/tests/observer_bug81430_1.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
--TEST--
22
Bug #81430 (Attribute instantiation frame accessing invalid frame pointer)
33
--EXTENSIONS--
4-
zend_test
4+
zend-test
55
--INI--
66
memory_limit=20M
77
zend_test.observer.enabled=1

ext/zend_test/tests/observer_bug81430_2.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
--TEST--
22
Bug #81430 (Attribute instantiation leaves dangling execute_data pointer)
33
--EXTENSIONS--
4-
zend_test
4+
zend-test
55
--INI--
66
memory_limit=20M
77
zend_test.observer.enabled=1

main/main.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,6 +2278,9 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod
22782278
module->version = PHP_VERSION;
22792279
module->info_func = PHP_MINFO(php_core);
22802280
}
2281+
2282+
/* freeze the list of observer fcall_init handlers */
2283+
zend_observer_post_startup();
22812284

22822285
/* Extensions that add engine hooks after this point do so at their own peril */
22832286
zend_finalize_system_id();

0 commit comments

Comments
 (0)