php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77345 Stack Overflow caused by circular reference in garbage collection
Submitted: 2018-12-25 06:17 UTC Modified: 2019-01-08 09:27 UTC
Votes:3
Avg. Score:5.0 ± 0.0
Reproduced:2 of 2 (100.0%)
Same Version:2 (100.0%)
Same OS:2 (100.0%)
From: hugh at allthethings dot co dot nz Assigned: dmitry (profile)
Status: Closed Package: Reproducible crash
PHP Version: 7.3.0 OS: Linux
Private report: No CVE-ID: None
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: hugh at allthethings dot co dot nz
New email:
PHP Version: OS:

 

 [2018-12-25 06:17 UTC] 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=(php`main 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


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-12-25 07:02 UTC] stas@php.net
-Type: Security +Type: Bug -Assigned To: +Assigned To: dmitry
 [2018-12-26 08:32 UTC] dmitry@php.net
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.
 [2019-01-08 09:27 UTC] nikic@php.net
I think the only way to fix this is to migrate from stack recursion to a work item stack on the heap.
 [2019-03-07 10:58 UTC] dmitry@php.net
Automatic comment on behalf of dmitry@zend.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=5da591c529d3d6ec5e59664c4faa4bc12210fb94
Log: Fixed bug #77345 (Stack Overflow caused by circular reference in garbage collection)
 [2019-03-07 10:58 UTC] dmitry@php.net
-Status: Assigned +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Oct 09 07:01:28 2024 UTC