For the second time in a row, I find a bug in PHP.
$ USE_ZEND_ALLOC=0 $(phpenv which php) vendor/bin/phpunit tests/ContainerTest.php --debug PHPUnit 4.8.36 by Sebastian Bergmann and contributors. Starting test 'Slim\Tests\ContainerTest::testGet'. . Starting test 'Slim\Tests\ContainerTest::testGetWithValueNotFoundError'. . Starting test 'Slim\Tests\ContainerTest::testGetWithDiConfigErrorThrownAsContainerValueNotFoundException'. . Starting test 'Slim\Tests\ContainerTest::testGetWithDiConfigErrorThrownAsInvalidArgumentException'. . Starting test 'Slim\Tests\ContainerTest::testGetWithErrorThrownByFactoryClosure'. . Starting test 'Slim\Tests\ContainerTest::testGetRequest'. . Starting test 'Slim\Tests\ContainerTest::testGetResponse'. . Starting test 'Slim\Tests\ContainerTest::testGetRouter'. . Starting test 'Slim\Tests\ContainerTest::testGetErrorHandler'. . Starting test 'Slim\Tests\ContainerTest::testGetNotAllowedHandler'. . Starting test 'Slim\Tests\ContainerTest::testSettingsCanBeEdited'. . Starting test 'Slim\Tests\ContainerTest::testMagicIssetMethod'. . Starting test 'Slim\Tests\ContainerTest::testMagicGetMethod'. . Starting test 'Slim\Tests\ContainerTest::testRouteCacheDisabledByDefault'. . Time: 56 ms, Memory: 0.00MB OK (14 tests, 15 assertions) *** Error in/home/volodymyr/.phpenv/versions/7.1.17-nts-debug/bin/php': corrupted size vs. prev_size: 0x000055732d1ed91f *** Aborted (core dumped) </pre> Without
USE_ZEND_ALLOC=0` everything works. Sometimes I hate PHP. Valgrind shows a long error list, which looks like this:==5164== Invalid read of size 4 ==5164== at 0x97D15B: gc_mark_grey (zend_gc.c:517) ==5164== by 0x97D125: gc_mark_grey (zend_gc.c:511) ==5164== by 0x97D452: gc_mark_roots (zend_gc.c:598) ==5164== by 0x97E605: zend_gc_collect_cycles (zend_gc.c:1072) ==5164== by 0x92B3B5: shutdown_executor (zend_execute_API.c:358) ==5164== by 0x945738: zend_deactivate (zend.c:1005) ==5164== by 0x8AB007: php_request_shutdown (main.c:1902) ==5164== by 0xA31844: do_cli (php_cli.c:1160) ==5164== by 0xA320C6: main (php_cli.c:1381) ==5164== Address 0x10f3e300 is 0 bytes inside a block of size 24 free'd ==5164== at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==5164== by 0x90D976: _efree (zend_alloc.c:2444) ==5164== by 0x960695: list_entry_destructor (zend_list.c:189) ==5164== by 0x95C04B: _zend_hash_del_el_ex (zend_hash.c:997) ==5164== by 0x95C8C7: zend_hash_index_del (zend_hash.c:1210) ==5164== by 0x960144: zend_list_delete (zend_list.c:50) ==5164== by 0x8CAA2C: _php_stream_free (streams.c:449) ==5164== by 0x8CA973: _php_stream_free (streams.c:410) ==5164== by 0x8CD808: stream_resource_regular_dtor (streams.c:1619) ==5164== by 0x960213: zend_resource_dtor (zend_list.c:76) ==5164== by 0x9607DE: zend_close_rsrc (zend_list.c:230) ==5164== by 0x95D9F3: zend_hash_reverse_apply (zend_hash.c:1598) ==5164== by 0x960804: zend_close_rsrc_list (zend_list.c:238) ==5164== by 0x92B374: shutdown_executor (zend_execute_API.c:353) ==5164== by 0x945738: zend_deactivate (zend.c:1005) ==5164== by 0x8AB007: php_request_shutdown (main.c:1902) ==5164== by 0xA31844: do_cli (php_cli.c:1160) ==5164== by 0xA320C6: main (php_cli.c:1381) ==5164== Block was alloc'd at ==5164== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==5164== by 0x90E622: __zend_malloc (zend_alloc.c:2838) ==5164== by 0x90D8CF: _emalloc (zend_alloc.c:2429) ==5164== by 0x960067: zend_list_insert (zend_list.c:43) ==5164== by 0x9602A9: zend_register_resource (zend_list.c:98) ==5164== by 0x8CA71B: _php_stream_alloc (streams.c:310) ==5164== by 0x8D164F: _php_stream_temp_create_ex (memory.c:579) ==5164== by 0x8D171B: _php_stream_temp_create (memory.c:591) ==5164== by 0x7D939A: php_stream_url_wrap_php (php_fopen_wrapper.c:211) ==5164== by 0x8CEB88: _php_stream_open_wrapper_ex (streams.c:2055) ==5164== by 0x77A89E: php_if_fopen (file.c:870) ==5164== by 0x65A8B8: phar_fopen (func_interceptors.c:427) ==5164== by 0x9AB437: ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:876) ==5164== by 0x9AA21C: execute_ex (zend_vm_execute.h:429) ==5164== by 0x92CF09: zend_call_function (zend_execute_API.c:855) ==5164== by 0x97034E: zend_call_method (zend_interfaces.c:99) ==5164== by 0x996108: zend_std_read_dimension (zend_object_handlers.c:819) ==5164== by 0x9A76BC: zend_fetch_dimension_address_read (zend_execute.c:1874) ==5164== by 0x9A7861: zend_fetch_dimension_address_read_R (zend_execute.c:1898) ==5164== by 0x9D373D: ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CONST_HANDLER (zend_vm_execute.h:18552) ==5164== by 0x9AA21C: execute_ex (zend_vm_execute.h:429) ==5164== by 0x92CF09: zend_call_function (zend_execute_API.c:855) ==5164== by 0x694EEA: reflection_method_invoke (php_reflection.c:3342) ==5164== by 0x6950BA: zim_reflection_method_invokeArgs (php_reflection.c:3378) ==5164== by 0x9ABE80: ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:1097) ==5164== by 0x9AA21C: execute_ex (zend_vm_execute.h:429) ==5164== by 0x9AA32D: zend_execute (zend_vm_execute.h:474) ==5164== by 0x946D7B: zend_execute_scripts (zend.c:1482) ==5164== by 0x8AC4EB: php_execute_script (main.c:2577) ==5164== by 0xA30EF2: do_cli (php_cli.c:993) ==5164== ==5164== Invalid write of size 4 ==5164== at 0x97D164: gc_mark_grey (zend_gc.c:517) ==5164== by 0x97D125: gc_mark_grey (zend_gc.c:511) ==5164== by 0x97D452: gc_mark_roots (zend_gc.c:598) ==5164== by 0x97E605: zend_gc_collect_cycles (zend_gc.c:1072) ==5164== by 0x92B3B5: shutdown_executor (zend_execute_API.c:358) ==5164== by 0x945738: zend_deactivate (zend.c:1005) ==5164== by 0x8AB007: php_request_shutdown (main.c:1902) ==5164== by 0xA31844: do_cli (php_cli.c:1160) ==5164== by 0xA320C6: main (php_cli.c:1381) ==5164== Address 0x10f3e300 is 0 bytes inside a block of size 24 free'd ==5164== at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==5164== by 0x90D976: _efree (zend_alloc.c:2444) ==5164== by 0x960695: list_entry_destructor (zend_list.c:189) ==5164== by 0x95C04B: _zend_hash_del_el_ex (zend_hash.c:997) ==5164== by 0x95C8C7: zend_hash_index_del (zend_hash.c:1210) ==5164== by 0x960144: zend_list_delete (zend_list.c:50) ==5164== by 0x8CAA2C: _php_stream_free (streams.c:449) ==5164== by 0x8CA973: _php_stream_free (streams.c:410) ==5164== by 0x8CD808: stream_resource_regular_dtor (streams.c:1619) ==5164== by 0x960213: zend_resource_dtor (zend_list.c:76) ==5164== by 0x9607DE: zend_close_rsrc (zend_list.c:230) ==5164== by 0x95D9F3: zend_hash_reverse_apply (zend_hash.c:1598) ==5164== by 0x960804: zend_close_rsrc_list (zend_list.c:238) ==5164== by 0x92B374: shutdown_executor (zend_execute_API.c:353) ==5164== by 0x945738: zend_deactivate (zend.c:1005) ==5164== by 0x8AB007: php_request_shutdown (main.c:1902) ==5164== by 0xA31844: do_cli (php_cli.c:1160) ==5164== by 0xA320C6: main (php_cli.c:1381) ==5164== Block was alloc'd at ==5164== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==5164== by 0x90E622: __zend_malloc (zend_alloc.c:2838) ==5164== by 0x90D8CF: _emalloc (zend_alloc.c:2429) ==5164== by 0x960067: zend_list_insert (zend_list.c:43) ==5164== by 0x9602A9: zend_register_resource (zend_list.c:98) ==5164== by 0x8CA71B: _php_stream_alloc (streams.c:310) ==5164== by 0x8D164F: _php_stream_temp_create_ex (memory.c:579) ==5164== by 0x8D171B: _php_stream_temp_create (memory.c:591) ==5164== by 0x7D939A: php_stream_url_wrap_php (php_fopen_wrapper.c:211) ==5164== by 0x8CEB88: _php_stream_open_wrapper_ex (streams.c:2055) ==5164== by 0x77A89E: php_if_fopen (file.c:870) ==5164== by 0x65A8B8: phar_fopen (func_interceptors.c:427) ==5164== by 0x9AB437: ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:876) ==5164== by 0x9AA21C: execute_ex (zend_vm_execute.h:429) ==5164== by 0x92CF09: zend_call_function (zend_execute_API.c:855) ==5164== by 0x97034E: zend_call_method (zend_interfaces.c:99) ==5164== by 0x996108: zend_std_read_dimension (zend_object_handlers.c:819) ==5164== by 0x9A76BC: zend_fetch_dimension_address_read (zend_execute.c:1874) ==5164== by 0x9A7861: zend_fetch_dimension_address_read_R (zend_execute.c:1898) ==5164== by 0x9D373D: ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CONST_HANDLER (zend_vm_execute.h:18552) ==5164== by 0x9AA21C: execute_ex (zend_vm_execute.h:429) ==5164== by 0x92CF09: zend_call_function (zend_execute_API.c:855) ==5164== by 0x694EEA: reflection_method_invoke (php_reflection.c:3342) ==5164== by 0x6950BA: zim_reflection_method_invokeArgs (php_reflection.c:3378) ==5164== by 0x9ABE80: ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:1097) ==5164== by 0x9AA21C: execute_ex (zend_vm_execute.h:429) ==5164== by 0x9AA32D: zend_execute (zend_vm_execute.h:474) ==5164== by 0x946D7B: zend_execute_scripts (zend.c:1482) ==5164== by 0x8AC4EB: php_execute_script (main.c:2577) ==5164== by 0xA30EF2: do_cli (php_cli.c:993)It looks like the garbage collector is fed with already destroyed data.
Affected PHP versions: 7.0.30 (earlier versions are probably affected as well), 7.1.11 — 7.1.17 (earlier versions are probably affected as well), 7.2.x branch is not affected.
I guess I need a minimal test case to report the bug.Here is the minimal test case:
<?php class Body { private $r; public function __construct($x) { $this->r = $x; } } class Pimple_Container implements ArrayAccess { private $values = []; private $raw = []; public function offsetGet($id) { $raw = $this->values[$id]; $this->values[$id] = $raw($this); $this->raw[$id] = $raw; return $this->values[$id]; } public function offsetSet($id, $val) { $this->values[$id] = $val; } public function offsetUnset($id) {} public function offsetExists($id) {} } class Container extends Pimple_Container { public function __construct() { $this['body'] = function($c) { return new Body(fopen('php://temp', 'r+')); }; } public function get($id) { return $this[$id]; } } $c = new Container(); $response = $c['body'];Bug report: https://bugs.php.net/bug.php?id=76302
Update: this fixes the issue for me:
--- 7.1.17/main/streams/streams.c.orig 2018-05-05 21:13:42.470900381 +0300 +++ 7.1.17/main/streams/streams.c 2018-05-05 21:10:26.585014131 +0300 @@ -408,7 +408,7 @@ * enclosing stream can free this stream. We remove rsrc_dtor because * we want the enclosing stream to be deleted from the resource list */ return php_stream_free(enclosing_stream, - (close_options | PHP_STREAM_FREE_CALL_DTOR) & ~PHP_STREAM_FREE_RSRC_DTOR); + (close_options | PHP_STREAM_FREE_CALL_DTOR | PHP_STREAM_FREE_KEEP_RSRC) & ~PHP_STREAM_FREE_RSRC_DTOR); } /* if we are releasing the stream only (and preserving the underlying handle),One More Bug in PHP