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
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: 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 06:01:26 2024 UTC