Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Zend/tests/oom_compile_failure.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
OOM: Delayed OOM raised during compilation of the main script is reported when compilation fails
--SKIPIF--
<?php
if (!getenv('TEST_PHP_EXECUTABLE')) die('skip TEST_PHP_EXECUTABLE is not set');
?>
--FILE--
<?php

$fname = __DIR__ . '/oom_compile_failure.test.php';
file_put_contents($fname, "<?php\n\$a = '" . str_repeat('A', 3 << 20) . "';\n)\n");

$php = getenv('TEST_PHP_EXECUTABLE');
echo shell_exec(escapeshellarg($php) . ' -n -d fatal_error_backtraces=0 -d memory_limit=6M ' . escapeshellarg($fname) . ' 2>&1');

?>
--CLEAN--
<?php
unlink(__DIR__ . '/oom_compile_failure.test.php');
?>
--EXPECTF--
Fatal error: Allowed memory size of 6291456 bytes exhausted %S(tried to allocate %d bytes) in %soom_compile_failure.test.php on line 2
18 changes: 18 additions & 0 deletions Zend/tests/oom_compile_failure_eval_string.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
OOM: Delayed OOM raised during compilation of php -r code is reported when compilation fails
--SKIPIF--
<?php
if (!getenv('TEST_PHP_EXECUTABLE')) die('skip TEST_PHP_EXECUTABLE is not set');
if (PHP_OS_FAMILY === 'Windows') die('skip the -r code is longer than Windows allows on the command line');
?>
--FILE--
<?php

$code = '$a = [' . str_repeat('1,', 50000) . ']; )';

$php = getenv('TEST_PHP_EXECUTABLE');
echo shell_exec(escapeshellarg($php) . ' -n -d fatal_error_backtraces=0 -d memory_limit=3M -r ' . escapeshellarg($code) . ' 2>&1');

?>
--EXPECTF--
Fatal error: Allowed memory size of 3145728 bytes exhausted %S(tried to allocate %d bytes) in Command line code on line 1
29 changes: 29 additions & 0 deletions Zend/tests/oom_compile_failure_lint.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
OOM: Delayed OOM raised during compilation is reported by php -l
--SKIPIF--
<?php
if (!getenv('TEST_PHP_EXECUTABLE')) die('skip TEST_PHP_EXECUTABLE is not set');
?>
--FILE--
<?php

$fname = __DIR__ . '/oom_compile_failure_lint.test.php';
$php = getenv('TEST_PHP_EXECUTABLE');

foreach (["\n", "\n)\n"] as $tail) {
file_put_contents($fname, "<?php\n\$a = '" . str_repeat('A', 3 << 20) . "';" . $tail);
// Windows rounds huge blocks up to 2MB, so leave room below the hard limit
echo shell_exec(escapeshellarg($php) . ' -n -d fatal_error_backtraces=0 -d memory_limit=6M -d memory_hard_limit_percent=400 -l ' . escapeshellarg($fname) . ' 2>&1');
}

?>
--CLEAN--
<?php
@unlink(__DIR__ . '/oom_compile_failure_lint.test.php');
?>
--EXPECTF--
Fatal error: Allowed memory size of 6291456 bytes exhausted %S(tried to allocate %d bytes) in %soom_compile_failure_lint.test.php on line 2
Errors parsing %soom_compile_failure_lint.test.php

Fatal error: Allowed memory size of 6291456 bytes exhausted %S(tried to allocate %d bytes) in %soom_compile_failure_lint.test.php on line 2
Errors parsing %soom_compile_failure_lint.test.php
10 changes: 8 additions & 2 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -1956,8 +1956,14 @@ ZEND_API zend_result zend_execute_script(int type, zval *retval, zend_file_handl
zend_destroy_static_vars(op_array);
destroy_op_array(op_array);
efree_size(op_array, sizeof(zend_op_array));
} else if (type == ZEND_REQUIRE) {
ret = FAILURE;
} else {
if (EG(delayed_oom).state == ZEND_OOM_STATE_PENDING) {
zend_clear_exception();
zend_handle_delayed_oom();
}
if (type == ZEND_REQUIRE) {
ret = FAILURE;
}
}

return ret;
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,10 @@ ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *re
efree_size(new_op_array, sizeof(zend_op_array));
retval = SUCCESS;
} else {
if (EG(delayed_oom).state == ZEND_OOM_STATE_PENDING) {
zend_clear_exception();
zend_handle_delayed_oom();
}
retval = FAILURE;
}
zend_string_release(code_str);
Expand Down
5 changes: 5 additions & 0 deletions main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2771,6 +2771,11 @@ PHPAPI zend_result php_lint_script(zend_file_handle *file)
efree(op_array);
retval = SUCCESS;
}
if (EG(delayed_oom).state == ZEND_OOM_STATE_PENDING) {
retval = FAILURE;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this line

@lazerg lazerg Oct 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is needed: zend_handle_delayed_oom() bails out here, so without it php -l on a valid file prints "No syntax errors detected" after the fatal error and oom_compile_failure_lint.phpt fails. I dropped the NEWS change.

zend_clear_exception();
zend_handle_delayed_oom();
}
} zend_end_try();
if (EG(exception)) {
zend_exception_error(EG(exception), E_ERROR);
Expand Down
3 changes: 3 additions & 0 deletions sapi/fuzzer/fuzzer-sapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ int fuzzer_do_request_from_buffer(
zend_destroy_static_vars(op_array);
destroy_op_array(op_array);
efree(op_array);
} else if (EG(delayed_oom).state == ZEND_OOM_STATE_PENDING) {
zend_clear_exception();
zend_handle_delayed_oom();
}
} zend_end_try();

Expand Down
Loading