Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 51892ba

Browse files
Handlers might be freed from OOM
1 parent cfe4d71 commit 51892ba

File tree

3 files changed

+83
-18
lines changed

3 files changed

+83
-18
lines changed

‎main/output.c‎

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,7 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl
934934
return PHP_OUTPUT_HANDLER_FAILURE;
935935
}
936936

937+
bool still_have_handler = true;
937938
/* storable? */
938939
if (php_output_handler_append(handler, &context->in) && !context->op) {
939940
context->op = original_op;
@@ -971,7 +972,22 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl
971972
"Returning a non-string result from user output handler %s is deprecated",
972973
ZSTR_VAL(handler->name)
973974
);
974-
handler->flags &= (~PHP_OUTPUT_HANDLER_DISABLED);
975+
// Check if the handler is still in the list of handlers to
976+
// determine if the PHP_OUTPUT_HANDLER_DISABLED flag can
977+
// be removed
978+
still_have_handler = false;
979+
int handler_count = php_output_get_level();
980+
if (handler_count) {
981+
php_output_handler **handlers = (php_output_handler **) zend_stack_base(&OG(handlers));
982+
for (int iii = 0; iii < handler_count; ++iii) {
983+
php_output_handler *curr_handler = handlers[iii];
984+
if (curr_handler == handler) {
985+
handler->flags &= (~PHP_OUTPUT_HANDLER_DISABLED);
986+
still_have_handler = true;
987+
break;
988+
}
989+
}
990+
}
975991
}
976992
if (Z_TYPE(retval) == IS_FALSE) {
977993
/* call failed, pass internal buffer along */
@@ -1013,14 +1029,18 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl
10131029
status = PHP_OUTPUT_HANDLER_FAILURE;
10141030
}
10151031
}
1016-
handler->flags |= PHP_OUTPUT_HANDLER_STARTED;
1032+
if (still_have_handler) {
1033+
handler->flags |= PHP_OUTPUT_HANDLER_STARTED;
1034+
}
10171035
OG(running) = NULL;
10181036
}
10191037

10201038
switch (status) {
10211039
case PHP_OUTPUT_HANDLER_FAILURE:
1022-
/* disable this handler */
1023-
handler->flags |= PHP_OUTPUT_HANDLER_DISABLED;
1040+
if (still_have_handler) {
1041+
/* disable this handler */
1042+
handler->flags |= PHP_OUTPUT_HANDLER_DISABLED;
1043+
}
10241044
/* discard any output */
10251045
if (context->out.data && context->out.free) {
10261046
efree(context->out.data);
@@ -1029,18 +1049,22 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl
10291049
context->out.data = handler->buffer.data;
10301050
context->out.used = handler->buffer.used;
10311051
context->out.free = 1;
1032-
handler->buffer.data = NULL;
1033-
handler->buffer.used = 0;
1034-
handler->buffer.size = 0;
1052+
if (still_have_handler) {
1053+
handler->buffer.data = NULL;
1054+
handler->buffer.used = 0;
1055+
handler->buffer.size = 0;
1056+
}
10351057
break;
10361058
case PHP_OUTPUT_HANDLER_NO_DATA:
10371059
/* handler ate all */
10381060
php_output_context_reset(context);
10391061
ZEND_FALLTHROUGH;
10401062
case PHP_OUTPUT_HANDLER_SUCCESS:
1041-
/* no more buffered data */
1042-
handler->buffer.used = 0;
1043-
handler->flags |= PHP_OUTPUT_HANDLER_PROCESSED;
1063+
if (still_have_handler) {
1064+
/* no more buffered data */
1065+
handler->buffer.used = 0;
1066+
handler->flags |= PHP_OUTPUT_HANDLER_PROCESSED;
1067+
}
10441068
break;
10451069
}
10461070

@@ -1242,6 +1266,19 @@ static int php_output_stack_pop(int flags)
12421266
}
12431267
php_output_handler_op(orphan, &context);
12441268
}
1269+
// If it isn't still in the stack, cannot free it
1270+
bool still_have_handler = false;
1271+
int handler_count = php_output_get_level();
1272+
if (handler_count) {
1273+
php_output_handler **handlers = (php_output_handler **) zend_stack_base(&OG(handlers));
1274+
for (int iii = 0; iii < handler_count; ++iii) {
1275+
php_output_handler *curr_handler = handlers[iii];
1276+
if (curr_handler == orphan) {
1277+
still_have_handler = true;
1278+
break;
1279+
}
1280+
}
1281+
}
12451282

12461283
/* pop it off the stack */
12471284
zend_stack_del_top(&OG(handlers));
@@ -1257,7 +1294,9 @@ static int php_output_stack_pop(int flags)
12571294
}
12581295

12591296
/* destroy the handler (after write!) */
1260-
php_output_handler_free(&orphan);
1297+
if (still_have_handler) {
1298+
php_output_handler_free(&orphan);
1299+
}
12611300
php_output_context_dtor(&context);
12621301

12631302
return 1;

‎tests/output/ob_start_callback_bad_return/exception_handler.phpt‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ foreach ($cases as $case) {
3838
$log = [];
3939
echo "\n\nTesting: $case\n";
4040
ob_start($case);
41-
echo "Inside of $case";
41+
echo "Inside of $case\n";
4242
try {
43-
ob_end_flush();
43+
ob_end_flush();
4444
} catch (\ErrorException $e) {
4545
echo $e . "\n";
4646
}
@@ -58,17 +58,20 @@ Stack trace:
5858
#2 {main}
5959

6060
End of return_null, log was:
61-
return_null: <<<Inside of return_null>>>
61+
return_null: <<<Inside of return_null
62+
>>>
6263

6364
Testing: return_false
64-
Inside of return_falseErrorException: ob_end_flush(): Returning a non-string result from user output handler return_false is deprecated in %s:%d
65+
Inside of return_false
66+
ErrorException: ob_end_flush(): Returning a non-string result from user output handler return_false is deprecated in %s:%d
6567
Stack trace:
6668
#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d)
6769
#1 %s(%d): ob_end_flush()
6870
#2 {main}
6971

7072
End of return_false, log was:
71-
return_false: <<<Inside of return_false>>>
73+
return_false: <<<Inside of return_false
74+
>>>
7275

7376
Testing: return_true
7477
ErrorException: ob_end_flush(): Returning a non-string result from user output handler return_true is deprecated in %s:%d
@@ -78,7 +81,8 @@ Stack trace:
7881
#2 {main}
7982

8083
End of return_true, log was:
81-
return_true: <<<Inside of return_true>>>
84+
return_true: <<<Inside of return_true
85+
>>>
8286

8387
Testing: return_zero
8488
0ErrorException: ob_end_flush(): Returning a non-string result from user output handler return_zero is deprecated in %s:%d
@@ -88,4 +92,5 @@ Stack trace:
8892
#2 {main}
8993

9094
End of return_zero, log was:
91-
return_zero: <<<Inside of return_zero>>>
95+
return_zero: <<<Inside of return_zero
96+
>>>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
ob_start(): Check behaviour with deprecation when OOM triggers handler removal
3+
--FILE--
4+
<?php
5+
6+
ob_start(function() {
7+
// We are out of memory, now trigger a deprecation
8+
return false;
9+
});
10+
11+
$a = [];
12+
// trigger OOM in a resize operation
13+
while (1) {
14+
$a[] = 1;
15+
}
16+
17+
?>
18+
--EXPECTF--
19+
Deprecated: main(): Returning a non-string result from user output handler Closure::__invoke is deprecated in %s on line %d
20+
21+
Fatal error: Allowed memory size of %d bytes exhausted at %s:%d (tried to allocate %d bytes) in %s on line %d

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /