[php-src] Issue #21420: Error in the repeat 2 logic: php_request_shutdown is not called the second time.
[email protected] (EdmondDantes)
| Newsgroups | php.bugs |
|---|---|
| Message-ID | <YYoK4AE01T7PdmzvRfJQuaoEGBrgupstPmp5QXWoj24@main.internal.php.net> |
Issue: https://github.com/php/php-src/issues/21420
Author: EdmondDantes
### Description
There is a bug in the repeat 2 logic caused by an incorrect goto inside a zend_try block. This bug does not manifest in most cases, but I encountered it in tests with the async core.
```
zend_try { // 619 — SETJMP + EG(bailout) = &__bailout
// ...
do_repeat: // 871 — goto jumps HERE
// ...
php_execute_script(); // has its own zend_try inside
// ...
} zend_end_try(); // 1149 — EG(bailout) = __orig_bailout
out:
php_request_shutdown(); // 1156
goto do_repeat; // 1171 — jumps to 871
```
**After Round 1:**
1. zend_end_try() (1149) — restores EG(bailout) = __orig_bailout
2. php_request_shutdown() — OK
3. goto do_repeat — jumps to 871
**Round 2 — the problem:**
- SETJMP at line 619 is NOT called again
- EG(bailout) points to __orig_bailout (the outer handler), not &__bailout
- When php_execute_script does its own zend_try, it saves __orig_bailout as its parent
- When zend_bailout() at scheduler.c:1216 fires → it jumps past do_cli() entirely → php_request_shutdown() is NOT called on Round 2!
The flow goes straight to main() → php_module_shutdown() → executor_globals_dtor() → ...
This is the root cause! The bug is in php_cli.c itself — goto do_repeat jumps into the middle of a zend_try block without reinitializing SETJMP.
That is, when the CLI runs in repeat mode twice, it is not cleaned up correctly the second time. The request shutdown is not called.
### PHP Version
```plain
PHP 8.6.0-dev (cli) (built: Mar 12 2026 06:51:37) (ZTS TrueAsync ABI v0.9.0)
Copyright (c) The PHP Group
Zend Engine v4.6.0-dev, Copyright (c) Zend Technologies
with Zend OPcache v8.6.0-dev, Copyright (c), by Zend Technologies
```
### Operating System
_No response_