Edit report at https://bugs.php.net/bug.php?id=77345&edit=1
ID: 77345
Updated by: dmitry@php.net
Reported by: hugh at allthethings dot co dot nz
Summary: Stack Overflow caused by circular reference in
garbage collection
Status: Assigned
Type: Bug
Package: Reproducible crash
Operating System: Linux
PHP Version: 7.3.0
Assigned To: dmitry
Block user comment: N
Private report: N
New Comment:
I confirm the problem, however, I didn't find a way to fix this yet.
The GC algorithm uses recursive graph traverse by definition.
It's already improved using tail calls (and may be improved a bit more), but this is not going
to be a general fix anyway.
Previous Comments:
------------------------------------------------------------------------
[2018-12-25 06:17:18] hugh at allthethings dot co dot nz
Description:
------------
Hi,
Stumbled across this one. The test script below causes a stack overflow (not stack buffer overflow)
caused by what seems like a circular reference when doing garbage collection.
Unfortunately I haven't had time to debug this fully and write a patch yet.
I've looked at bug #72286 which looks similar, but that is not an issue on my 7.3.0
Test script:
---------------
<?php
class A{}
for(;;){
$x=new A;
$x->x=$y->y;
if(1){
$y->y->r=$x;
}
$y->y=$x;
}
Expected result:
----------------
Script runs fully and no crash occurs due to circular reference in the garbage collection process
Actual result:
--------------
lldb output:
(lldb) target create "./sapi/cli/php"
Current executable set to './sapi/cli/php' (x86_64).
(lldb) settings set -- target.run-args "../stack-overflow.php"
(lldb) r
Process 25450 launched: './sapi/cli/php' (x86_64)
Warning: Creating default object from empty value in /home/hugh/stack-overflow.php on line 7
Process 25450 stopped
* thread #1, name = 'php', stop reason = signal SIGSEGV: invalid address (fault address:
0x7fffff7feff8)
frame #0: 0x0000555555811c34 php`gc_scan_black(ref=0x00007ffff5b1e820) at zend_gc.c:610
607 }
608
609 static void gc_scan_black(zend_refcounted *ref)
-> 610 {
611 HashTable *ht;
612 Bucket *p, *end;
613 zval *zv;
(lldb) register read
General Purpose Registers:
rax = 0x0000000000000001
rbx = 0x00007ffff5b22ae0
rcx = 0x00007ffff5b1e848
rdx = 0x0000000000000000
rdi = 0x00007ffff5b1e820
rsi = 0x00007fffff7ff018
rbp = 0x00007ffff5b22ac0
rsp = 0x00007fffff7ff000
r8 = 0x000055555582467f php`zend_std_get_properties at zend_object_handlers.c:105
r9 = 0x0000000000000081
r10 = 0x0000000000000200
r11 = 0x00000000ffffffff
r12 = 0x0000000000000000
r13 = 0x00007ffff2e7bf50
r14 = 0x00007ffff6c1d030
r15 = 0x00007ffff6c8e080
rip = 0x0000555555811c34 php`gc_scan_black + 2 at zend_gc.c:610
rflags = 0x0000000000010206
cs = 0x0000000000000033
fs = 0x0000000000000000
gs = 0x0000000000000000
ss = 0x000000000000002b
ds = 0x0000000000000000
es = 0x0000000000000000
(lldb) bt 10
* thread #1, name = 'php', stop reason = signal SIGSEGV: invalid address (fault address:
0x7fffff7feff8)
* frame #0: 0x0000555555811c34 php`gc_scan_black(ref=0x00007ffff5b1e820) at zend_gc.c:610
frame #1: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #2: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #3: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #4: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #5: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #6: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #7: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #8: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #9: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
<snip>
frame #104590: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #104591: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #104592: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #104593: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #104594: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #104595: 0x0000555555811df3 php`gc_scan_black(ref=<unavailable>) at zend_gc.c:701
frame #104596: 0x0000555555811e9f php`gc_scan(ref=0x00007ffff2e7bf50) at zend_gc.c:882
frame #104597: 0x000055555581201e php`gc_scan_roots at zend_gc.c:977
frame #104598: 0x0000555555812af7 php`zend_gc_collect_cycles at zend_gc.c:1295
frame #104599: 0x00005555558121a9 php`gc_possible_root_when_full(ref=0x00007ffff2e7bf50) at
zend_gc.c:510
frame #104600: 0x00005555558127e4 php`gc_possible_root(ref=<unavailable>) at zend_gc.c:560
frame #104601: 0x0000555555835635 php`ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER at
zend_execute.h:113
frame #104602: 0x0000555555835550 php`ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER at
zend_vm_execute.h:45781
frame #104603: 0x000055555587fd03 php`execute_ex(ex=<unavailable>) at
zend_vm_execute.h:60198
frame #104604: 0x00005555558807e5 php`zend_execute(op_array=0x00007ffff6c7c2a0,
return_value=0x0000000000000000) at zend_vm_execute.h:60834
frame #104605: 0x00005555557ecb90 php`zend_execute_scripts(type=8, retval=0x00007ffff6c8e080,
file_count=-155070416) at zend.c:1568
frame #104606: 0x0000555555788d80 php`php_execute_script(primary_file=0x00007fffffffc530) at
main.c:2630
frame #104607: 0x0000555555882a2e php`do_cli(argc=2, argv=0x0000555555c96d40) at php_cli.c:997
frame #104608: 0x0000555555883754 php`main(argc=2, argv=0x0000555555c96d40) at php_cli.c:1389
frame #104609: 0x00007ffff7040b97 libc.so.6__libc_start_main(main=(phpmain at
php_cli.c:1188), argc=2, argv=0x00007fffffffd8e8, init=<unavailable>,
fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fffffffd8d8) at
libc-start.c:310
frame #104610: 0x0000555555624b5a php`_start + 42
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=77345&edit=1