opcache_compile_file() on a file with a fatal compile error disables the cycle collector for the rest of the request
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 4/5
- Tempo estimado
- 3-5 dias
- Facilidade para iniciantes
- 22/100
Direção de pesquisa
Start with the zend_catch branch of ZEND_FUNCTION(opcache_compile_file) in ext/opcache/zend_accelerator_module.c, then look at _zend_bailout() in Zend/zend.c, which calls gc_protect(1). Reproduce with the script in the issue and check that gc_status()['protected'] returns to false after the failed compile. Done means the cycle collector runs again after the catch, or the behaviour is documented and the gc_protect state is restored.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
Description
When opcache_compile_file() is given a file that fails with a fatal compile error, the function catches the bailout, emits a warning, returns false and lets the script continue. But zend_bailout() has already called gc_protect(1), and nothing resets it until the next request. From that point on gc_possible_root() drops every new root candidate, so cycles created later in the request are never collected, not even by an explicit gc_collect_cycles().
The following code:
<?php
$bad = sys_get_temp_dir() . "/bad.php";
file_put_contents($bad, "<?php\nbreak 2;\n");
class Node { public $self; }
var_dump(gc_status()["protected"]);
var_dump(@opcache_compile_file($bad));
var_dump(gc_status()["protected"]);
for ($i = 0; $i < 100000; $i++) { $o = new Node; $o->self = $o; }
var_dump(gc_collect_cycles());
var_dump(gc_status()["collected"]);
Run with php -n -d opcache.enable_cli=1 (master, debug build).
Resulted in this output:
bool(false)
Fatal error: 'break' not in the 'loop' or 'switch' context in /tmp/bad.php on line 2
Stack trace:
#0 /path/to/test.php(8): opcache_compile_file('/tmp/bad.php')
#1 {main}
bool(false)
bool(true)
int(0)
int(0)
But I expected this output instead (the second protected stays false and the cycles get collected; the last two numbers are what the same script prints with the opcache_compile_file() line removed):
bool(false)
Fatal error: 'break' not in the 'loop' or 'switch' context in /tmp/bad.php on line 2
Stack trace:
#0 /path/to/test.php(8): opcache_compile_file('/tmp/bad.php')
#1 {main}
bool(false)
bool(false)
int(9999)
int(99999)
Measured with a slightly longer script (prints gc_status() and memory_get_usage() after creating 100 000 self-referencing objects):
| build | opcache_compile_file() called |
protected after |
GC runs | collected | memory at end |
|---|---|---|---|---|---|
master 0b4a5a84492, debug, NTS |
no | false | 10 | 100 000 | 583 136 |
master 0b4a5a84492, debug, NTS |
yes | true | 0 | 0 | 11 141 648 |
master, debug, opcache.enable_cli=0 |
yes (returns false without compiling) | false | 10 | 100 000 | 635 656 |
PHP-8.4 81fd6105bc6, release + ASAN, NTS |
no | false | 10 | 100 000 | 414 792 |
PHP-8.4 81fd6105bc6, release + ASAN, NTS |
yes | true | 0 | 0 | 6 942 856 |
PHP-8.3 9c6ad6caeeb, release, NTS |
no | false | 10 | 100 000 | 471 080 |
PHP-8.3 9c6ad6caeeb, release, NTS |
yes | true | 0 | 0 | 7 023 624 |
Where it happens: ext/opcache/zend_accelerator_module.c, ZEND_FUNCTION(opcache_compile_file): the zend_try { persistent_compile_file(...) } zend_catch { ... zend_error(E_WARNING, "... could not compile file %s") } branch (non-preload path). The catch restores EG(current_execute_data) but not the GC protection that zend_bailout() set (Zend/zend.c, _zend_bailout() → gc_protect(1)). The catch dates back to the introduction of the function (69454d9f0f7, 2013), the gc_protect(1) in _zend_bailout() to f844d40fdc3 (2018, PHP 7.3.0), so presumably every version since 7.3 is affected.
The same class of problem is reported for ext/soap in #10026 (SoapClient converts fatal errors into SoapFault after a bailout). I found this second instance while auditing the zend_catch sites that continue execution after a bailout.
Possible directions (not a proposal of one in particular):
- report compile failures from
opcache_compile_file()without a bailout, if the compiler path allows it; - or document that after a fatal compile error the rest of the request runs without the cycle collector.
PHP Version
PHP 8.7.0-dev (cli) (NTS DEBUG), master 0b4a5a84492
PHP 8.4.28-dev (cli) (NTS), PHP-8.4 81fd6105bc6
PHP 8.3.36-dev (cli) (NTS), PHP-8.3 9c6ad6caeeb
Operating System
Ubuntu 24.04, x86_64
- Linguagem predominante
- C
- Estrelas
- 40.7k
- Forks
- 8.3k
- Merge médio
- 2d 18h
- PRs com merge (30d)
- 159
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Sem modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de php/php-src
-
Windows CLI: get_ps_title() passes bytes to GetConsoleTitleW() instead of characters, overflowing the 260-wchar_t bufferTalvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 79/100
Mantenedores costumam responder em até 1 dia
-
zend_set_local_var() overwrites existing compiled variable (CV) zval without calling zval_ptr_dtor()Talvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. AbertaBug Status: Needs Triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 82/100
Mantenedores costumam responder em até 1 dia
-
master: part of the mbstring bailout fix got lost in a mergeTalvez já em andamento @lazerg assumiu há 7 dias. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 1 dia
-
Bug Status: Needs Triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 64/100
Mantenedores costumam responder em até 1 dia
-
quoted_printable_encode() produces lines longer than 76 characters for bytes 0xF5 to 0xFFTalvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
Mantenedores costumam responder em até 1 dia
Todas as issues de php/php-src
Issues semelhantes
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
zephyrproject-rtos/zephyr#123881 ·
Mantenedores costumam responder em até 2 dias
-
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 65/100
ly05010419/MacNativeGames#1252 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 70/100
Mantenedores costumam responder em até 2 dias
-
Component - Build
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 75/100
Mantenedores costumam responder em até 1 dia
-
Description of the bugAbertabug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 68/100
HarbourMasters/Lighthouse#655 ·
Mantenedores costumam responder em até 1 dia