php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #78175 Preloading segfaults at preload time and at runtime
Submitted: 2019-06-17 12:48 UTC Modified: 2019-07-22 09:26 UTC
From: nicolas dot grekas+php at gmail dot com Assigned: dmitry (profile)
Status: Closed Package: opcache
PHP Version: 7.4.0alpha1 OS:
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: nicolas dot grekas+php at gmail dot com
New email:
PHP Version: OS:

 

 [2019-06-17 12:48 UTC] nicolas dot grekas+php at gmail dot com
Description:
------------
From the code in this archive:
https://send.firefox.com/download/f87c7176853e1dfa/#YxTCKp75r1nMwmq-PJfnSg

Two separate crashes here:

Cannot load the autoloader
--------------------------

Run `bin/console c:c && php -dopcache.preload=var/cache/dev/srcApp_KernelDevDebugContainer.preload.php -S localhost:8000` in this directory.

This should spawn a web server with two ignorable warnings saying "Can't preload already declared class class@anonymous".

Then in another console run `php info.php`.

This makes the server process segfault.

Cannot run preloading script
----------------------------

Add a `return;` line 321 in `vendor/symfony/dependency-injection/Dumper/PhpDumper.php` (just after `$r->getDefaultProperties();`).

Run `bin/console c:c && php -dopcache.preload=var/cache/dev/srcApp_KernelDevDebugContainer.preload.php -S localhost:8000` in this directory.

This fails with a segfault.



Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-06-17 14:27 UTC] nikic@php.net
First valgrind warning:

==4693== Invalid read of size 4
==4693==    at 0x93E9A9: zend_gc_delref (zend_types.h:1030)
==4693==    by 0x93FAE1: gc_mark_grey (zend_gc.c:823)
==4693==    by 0x940093: gc_mark_roots (zend_gc.c:958)
==4693==    by 0x9412BE: zend_gc_collect_cycles (zend_gc.c:1434)
==4693==    by 0x8EC2ED: shutdown_executor (zend_execute_API.c:277)
==4693==    by 0x905C9B: zend_deactivate (zend.c:1178)
==4693==    by 0x86950A: php_request_shutdown (main.c:1971)
==4693==    by 0x10C23D4C: accel_finish_startup (ZendAccelerator.c:4340)
==4693==    by 0x10C1F463: accel_post_startup (ZendAccelerator.c:2971)
==4693==    by 0x90577B: zend_post_startup (zend.c:992)
==4693==    by 0x86A265: php_module_startup (main.c:2399)
==4693==    by 0x9EC4B0: php_cli_startup (php_cli.c:408)
==4693==  Address 0x1116e600 is 0 bytes inside a block of size 56 free'd
==4693==    at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4693==    by 0x8CCED1: _efree_custom (zend_alloc.c:2392)
==4693==    by 0x8CD012: _efree (zend_alloc.c:2512)
==4693==    by 0x10C4464F: _zend_shared_memdup (zend_shared_alloc.c:386)
==4693==    by 0x10C446C3: zend_shared_memdup_put_free (zend_shared_alloc.c:398)
==4693==    by 0x10C29858: zend_persist_zval (zend_persist.c:212)
==4693==    by 0x10C2C754: zend_persist_class_entry (zend_persist.c:738)
==4693==    by 0x10C2F5B3: zend_accel_persist_class_table (zend_persist.c:1028)
==4693==    by 0x10C2F8CB: zend_accel_script_persist (zend_persist.c:1070)
==4693==    by 0x10C2272B: preload_script_in_shared_memory (ZendAccelerator.c:3939)
==4693==    by 0x10C236EA: accel_preload (ZendAccelerator.c:4187)
==4693==    by 0x10C23D1E: accel_finish_startup (ZendAccelerator.c:4334)
==4693==  Block was alloc'd at
==4693==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4693==    by 0x8CDC4F: __zend_malloc (zend_alloc.c:2895)
==4693==    by 0x8CCE6A: _malloc_custom (zend_alloc.c:2383)
==4693==    by 0x8CCF98: _emalloc (zend_alloc.c:2502)
==4693==    by 0x91A582: _zend_new_array (zend_hash.c:256)
==4693==    by 0x8E31D5: zend_try_ct_eval_array (zend_compile.c:7013)
==4693==    by 0x8E8ABE: zend_eval_const_expr (zend_compile.c:8834)
==4693==    by 0x8E7108: zend_const_expr_to_zval (zend_compile.c:8286)
==4693==    by 0x8E003E: zend_compile_prop_decl (zend_compile.c:6023)
==4693==    by 0x8E02E6: zend_compile_prop_group (zend_compile.c:6072)
==4693==    by 0x8E7629: zend_compile_stmt (zend_compile.c:8409)
==4693==    by 0x8DD43E: zend_compile_stmt_list (zend_compile.c:5176)

Looks like GC is trying to use some structures that have been moved to SHM.

Here is a very simple way to cause a crash (use as preload script).

<?php
register_shutdown_function(function() {
    echo "Shutdown\n";
});

It's not quite the same, but I think the root cause is basically the same.
 [2019-06-17 14:35 UTC] nikic@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: dmitry
 [2019-06-17 14:35 UTC] nikic@php.net
Dmitry, can you take a look?

I think we should create a stronger separation between preloading script and preloaded files. That is, require the use of opcache_compile_file() on everything that should be preloaded -- all other executed code (including include and require) should not be preloaded (or even cached, for that matter).

Does that make sense?
 [2019-06-18 14:19 UTC] dmitry@php.net
Automatic comment on behalf of dmitry@zend.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=148eb202268b0c2786e9f541b46138e5d1271761
Log: Fixed bug #78175 (Preloading segfaults at preload time and at runtime)
 [2019-06-18 14:19 UTC] dmitry@php.net
-Status: Assigned +Status: Closed
 [2019-06-19 07:10 UTC] nicolas dot grekas+php at gmail dot com
The crash still exists, this time on a second request:
run php info.php twice, the second time crashed the server.
 [2019-06-19 07:48 UTC] dmitry@php.net
-Status: Closed +Status: Re-Opened
 [2019-06-19 07:48 UTC] dmitry@php.net
I don't see crash, but got the following valgrind error on first request (modification of vendor/symfony/dependency-injection/Dumper/PhpDumper.php is not required).

==17283== Invalid read of size 4
==17283==    at 0x87D1339: ZEND_GET_CLASS_SPEC_CV_UNUSED_HANDLER (zend_vm_execute.h:47674)
==17283==    by 0x87DBF1A: execute_ex (zend_vm_execute.h:60625)
==17283==    by 0x87DC538: zend_execute (zend_vm_execute.h:61077)
==17283==    by 0x8728F00: zend_execute_scripts (zend.c:1657)
==17283==    by 0x86B656B: php_execute_script (main.c:2680)
==17283==    by 0x87E5E72: php_cli_server_dispatch_script (php_cli_server.c:1983)
==17283==    by 0x87E6498: php_cli_server_dispatch (php_cli_server.c:2158)
==17283==    by 0x87E6BF9: php_cli_server_recv_event_read_request (php_cli_server.c:2374)
==17283==    by 0x87E6EEE: php_cli_server_do_event_for_each_fd_callback (php_cli_server.c:2452)
==17283==    by 0x87E3B4C: php_cli_server_poller_iter_on_active (php_cli_server.c:841)
==17283==    by 0x87E6F88: php_cli_server_do_event_for_each_fd (php_cli_server.c:2475)
==17283==    by 0x87E6FE2: php_cli_server_do_event_loop (php_cli_server.c:2485)
==17283==  Address 0xa5caeb4 is 1,148 bytes inside a block of size 65,536 free'd
==17283==    at 0x4036729: free (vg_replace_malloc.c:540)
==17283==    by 0x86FC3F2: _efree_custom (zend_alloc.c:2392)
==17283==    by 0x86FC4EF: _efree (zend_alloc.c:2512)
==17283==    by 0x86FDDD2: zend_arena_destroy (zend_arena.h:46)
==17283==    by 0x86FE860: shutdown_compiler (zend_compile.c:384)
==17283==    by 0x872813B: zend_deactivate (zend.c:1185)
==17283==    by 0x86B5640: php_request_shutdown (main.c:1975)
==17283==    by 0x7EA098F: accel_finish_startup (ZendAccelerator.c:4353)
==17283==    by 0x7E9D284: accel_post_startup (ZendAccelerator.c:2969)
==17283==    by 0x8727D36: zend_post_startup (zend.c:992)
==17283==    by 0x86B5FD7: php_module_startup (main.c:2403)
==17283==    by 0x87E2ED8: sapi_cli_server_startup (php_cli_server.c:486)
==17283==  Block was alloc'd at
==17283==    at 0x40356A4: malloc (vg_replace_malloc.c:309)
==17283==    by 0x86FCE7E: __zend_malloc (zend_alloc.c:2895)
==17283==    by 0x86FC39C: _malloc_custom (zend_alloc.c:2383)
==17283==    by 0x86FC48E: _emalloc (zend_alloc.c:2502)
==17283==    by 0x86FDD7F: zend_arena_create (zend_arena.h:34)
==17283==    by 0x86FE7A2: init_compiler (zend_compile.c:365)
==17283==    by 0x8728011: zend_activate (zend.c:1151)
==17283==    by 0x86B528D: php_request_startup (main.c:1836)
==17283==    by 0x7EA08C6: accel_finish_startup (ZendAccelerator.c:4324)
==17283==    by 0x7E9D284: accel_post_startup (ZendAccelerator.c:2969)
==17283==    by 0x8727D36: zend_post_startup (zend.c:992)
==17283==    by 0x86B5FD7: php_module_startup (main.c:2403)
 [2019-06-19 09:22 UTC] dmitry@php.net
The problem caused by assignment object to static property of preloaded class, and opcache doesn't expect objects...

A simpler way to reproduce:

preload.php
-----------
<?php
class Loader {
        static private $loader;

        static function getLoader() {
                if (null !== self::$loader) {
                        return self::$loader;
                }
                return self::$loader = new Loader();
        }
}

Loader::getLoader();
?>

$ USE_ZEND_ALLOC=0 valgrind sapi/cli/php -dopcache.preload=preload.php -r 'var_dump(get_class(Loader::getLoader()));'

Although, this is a bug, it's also an interesting unexpected side effect, that allow initialization and preloading of big arrays as static properties (e.g. application configuration)
 [2019-06-20 12:30 UTC] nicolas dot grekas+php at gmail dot com
> Although, this is a bug, it's also an interesting unexpected side effect, that allow initialization and preloading of big arrays as static properties (e.g. application configuration)

While funny, that's not that interesting to keep: if one wants this, it's always possible to generate a file with the array and load it. Preloading the default static value is what would be the most expected, to me at least :)
 [2019-06-21 10:30 UTC] dmitry@php.net
A partial fix is available at

https://github.com/php/php-src/compare/PHP-7.4...dstogov:bug78175?expand=1

It needs to be extended to support static variables of methods.
 [2019-06-24 17:38 UTC] dmitry@php.net
-Status: Re-Opened +Status: Closed
 [2019-07-21 19:42 UTC] nicolas dot grekas+php at gmail dot com
Sorry, this still segfaults for me, here is the code:
https://github.com/nicolas-grekas/symfony/releases/download/php-bugs/preload-php-bug-78175.zip
 [2019-07-21 19:47 UTC] requinix@php.net
-Status: Closed +Status: Re-Opened
 [2019-07-22 09:26 UTC] dmitry@php.net
-Status: Re-Opened +Status: Closed
 [2019-07-22 09:26 UTC] dmitry@php.net
Commit 60dca2295d4eb87873e85bed24880a948a4049cc fixes a related crash
 [2019-07-24 09:24 UTC] nicolas dot grekas+php at gmail dot com
Thanks for the fix, I have another error, not a segfault this time:
https://github.com/nicolas-grekas/symfony/releases/download/php-bugs/preload-failures-78175.tar.bz2

I run
php -S localhost:8000 -t public/ -dopcache.preload=var/cache/prod/srcApp_KernelProdContainer.preload.php

Then in another console:
php info.php

This fails with an error:
Uncaught Psr\Log\InvalidArgumentException: The log level "critical" does not exist.

Yet it exists. To debug, I added a var_dump in
vendor/symfony/http-kernel/Log/Logger.php

And it displays:
UNKNOWN:0
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 13:01:29 2024 UTC