View Issue Details

IDProjectCategoryView StatusLast Update
0002328XdebugUncategorizedpublic2025-10-06 09:44
Reportertenzap Assigned To 
PrioritynormalSeveritymajorReproducibilityalways
Status closedResolutionfixed 
Product Version3.4.1 
Target Version3.4devFixed in Version3.4.6 
Summary0002328: Stream resource references in stored stack traces don't hold, and can cause crashes
Description

Running the tests of php-league-csv leads to crash.

phpunit --exclude-group network --no-coverage

Running it with
export USE_ZEND_ALLOC=0
export ZEND_DONT_UNLOAD_MODULES=1
doesn't lead to crash

Steps To Reproduce

Run the test suite of https://github.com/thephpleague/csv/ version 9.22.0

Command:

vendor/bin/phpunit --exclude-group network --no-coverage

You could also try this command which results into the same crash:

composer run-script phpunit-min

Be sure to not have XDEBUG_MODE=coverage, because when it is set, the tests don't fail.

If Xdebug is not installed, it doesn't fail either.

Output is

PHPUnit 11.5.11 by Sebastian Bergmann and contributors.

Runtime: PHP 8.4.4
Configuration: /mnt/packages/git_repos/dpkg/php-league-csv/phpunit.xml

............................................................... 63 / 678 ( 9%)
............................................................... 126 / 678 ( 18%)
..........DDDDD................................................ 189 / 678 ( 27%)
....................................DDDDDDDDDDD.............zend_mm_heap corrupted
Aborted (core dumped)

I also attach the output of valgrind, invoked with various parameters.

TagsNo tags attached.
Operating SystemDebian trixie
PHP Version8.4-dev

Activities

tenzap

2025-03-07 07:36

reporter   ~0007196

There is a typo. It's not

composer run-script phpunit-min

but

composer run-script phpunit:min

derick

2025-03-07 12:07

administrator   ~0007198

Hi,

I can't seem to be able to reproduce this.

  1. Could you share the output of php -r 'xdebug_info();'
  2. You say that you're using Debian trixie, which is "testing". I wonder whether that is the problem. Can you try with "bookwork" perhaps?
  3. Your reproduce case runs through a lot of tests. Would you be able to narrow it down into running just the test that makes things fail? You can use phpunit's --debug flag for that

cheers,
Derick

tenzap

2025-03-07 12:33

reporter   ~0007202

  1. See attachement
  2. It works on bookworm but this is with php 8.2 & xdebug 3.2. We can only say that the problem is probably not in league/csv
  3. Running only the crashing test succeeds when run alone. Even running the tests ot the class that contain it succeeds.

tenzap

2025-03-07 12:34

reporter   ~0007203

By the way it also fais on debian's CI, so my system is not particularly in cause.

tenzap

2025-03-07 12:53

reporter   ~0007204

I found that:

test suite succeeds with
xdebug.mode = debug,coverage

test suite crashes when xdebug.mode is not set, which corresponds to
xdebug.mode = develop

tenzap

2025-03-13 07:55

reporter   ~0007213

This bug is still present in 3.4.2

tenzap

2025-08-16 15:24

reporter   ~0007347

I tested again with xdebug 3.4.5 & php 8.4.11 on Debian trixie and it still fails.

derick

2025-08-27 14:05

administrator   ~0007348

Hi,

So I have tried again now — first on Trixie, and then later locally again, and I can now indeed reproduce it. Using 9.2.2 was critical for doing so.

I also need to run it with valgrind, like:

    export USE_ZEND_ALLOC=0
    export ZEND_DONT_UNLOAD_MODULES=1
    valgrind php -dpcre.jit=0 vendor/bin/phpunit --exclude-group=network --no-coverage 2>&1 | less

This produces the following warning (as first):

Invalid read of size 4
==3029939==    at 0xC3C61B: zend_gc_delref (zend_types.h:1358)
==3029939==    by 0xC3CF71: i_zval_ptr_dtor (zend_variables.h:44)
==3029939==    by 0xC42DD1: zend_array_destroy (zend_hash.c:1843)
==3029939==    by 0xCBA840: rc_dtor_func (zend_variables.c:57)
==3029939==    by 0xC3CF81: i_zval_ptr_dtor (zend_variables.h:45)
==3029939==    by 0xC42DD1: zend_array_destroy (zend_hash.c:1843)
==3029939==    by 0xCBA840: rc_dtor_func (zend_variables.c:57)
==3029939==    by 0xC3CF81: i_zval_ptr_dtor (zend_variables.h:45)
==3029939==    by 0xC42D4A: zend_array_destroy (zend_hash.c:1831)
==3029939==    by 0xCBA840: rc_dtor_func (zend_variables.c:57)
==3029939==    by 0xCBA7BA: i_zval_ptr_dtor (zend_variables.h:45)
==3029939==    by 0xCBAA00: zval_ptr_dtor (zend_variables.c:84)
==3029939==    by 0x9F44133: last_exception_get_slot (stack.c:520)
==3029939==    by 0x9F4681E: xdebug_develop_throw_exception_hook (stack.c:1249)
==3029939==    by 0x9F05F45: xdebug_throw_exception_hook (base.c:1569)
==3029939==    by 0xB8F79A: zend_throw_exception_internal (zend_exceptions.c:222)
==3029939==    by 0xB92E13: zend_throw_exception_object (zend_exceptions.c:1034)
==3029939==    by 0xBCE36C: ZEND_THROW_SPEC_TMPVAR_HANDLER (zend_vm_execute.h:15225)
==3029939==    by 0xC2B2F9: execute_ex (zend_vm_execute.h:60765)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==  Address 0xcebfeb0 is 0 bytes inside a block of size 32 free'd
==3029939==    at 0x484787F: free (vg_replace_malloc.c:989)
==3029939==    by 0xB34BDC: __zend_free (zend_alloc.c:3322)
==3029939==    by 0xB336B8: _efree (zend_alloc.c:2750)
==3029939==    by 0xC8F3AD: list_entry_destructor (zend_list.c:182)
==3029939==    by 0xC41CEC: _zend_hash_del_el_ex (zend_hash.c:1487)
==3029939==    by 0xC427FB: zend_hash_index_del (zend_hash.c:1718)
==3029939==    by 0xC8EF67: zend_list_free (zend_list.c:58)
==3029939==    by 0xCBA840: rc_dtor_func (zend_variables.c:57)
==3029939==    by 0xB99D48: i_zval_ptr_dtor (zend_variables.h:45)
==3029939==    by 0xBA6731: i_free_compiled_variables (zend_execute.c:4068)
==3029939==    by 0xC2967D: execute_ex (zend_vm_execute.h:58833)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==  Block was alloc'd at
==3029939==    at 0x4844818: malloc (vg_replace_malloc.c:446)
==3029939==    by 0xB34AAA: __zend_malloc (zend_alloc.c:3294)
==3029939==    by 0xB33632: _emalloc (zend_alloc.c:2740)
==3029939==    by 0xC8EE4E: zend_list_insert (zend_list.c:42)
==3029939==    by 0xC8F07D: zend_register_resource (zend_list.c:91)
==3029939==    by 0xAAFEAA: _php_stream_alloc (streams.c:314)
==3029939==    by 0xAAB73A: _php_stream_fopen_from_fd_int (plain_wrapper.c:194)
==3029939==    by 0xAAB92E: _php_stream_fopen_temporary_file (plain_wrapper.c:230)
==3029939==    by 0xAABA35: _php_stream_fopen_tmpfile (plain_wrapper.c:252)
==3029939==    by 0x937294: zif_tmpfile (file.c:716)
==3029939==    by 0xBAD1AC: ZEND_DO_ICALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:1416)
==3029939==    by 0xC29826: execute_ex (zend_vm_execute.h:58896)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)
==3029939==    by 0xC298B6: execute_ex (zend_vm_execute.h:58941)
==3029939==    by 0x9F0497C: xdebug_execute_ex (base.c:883)
==3029939==    by 0xBAECAF: ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER (zend_vm_execute.h:2123)

This provides the following hints.

From Block was alloc'd at, we see:

==3029939==    at 0x4844818: malloc (vg_replace_malloc.c:446)
==3029939==    by 0xB34AAA: __zend_malloc (zend_alloc.c:3294)
==3029939==    by 0xB33632: _emalloc (zend_alloc.c:2740)
==3029939==    by 0xC8EE4E: zend_list_insert (zend_list.c:42)
==3029939==    by 0xC8F07D: zend_register_resource (zend_list.c:91)
==3029939==    by 0xAAFEAA: _php_stream_alloc (streams.c:314)
==3029939==    by 0xAAB73A: _php_stream_fopen_from_fd_int (plain_wrapper.c:194)
==3029939==    by 0xAAB92E: _php_stream_fopen_temporary_file (plain_wrapper.c:230)
==3029939==    by 0xAABA35: _php_stream_fopen_tmpfile (plain_wrapper.c:252)
==3029939==    by 0x937294: zif_tmpfile (file.c:716)

And this allocation gets freed in the Address 0xcebfeb0 is 0 bytes inside a block of size 32 free'd section:

==3029939==    at 0x484787F: free (vg_replace_malloc.c:989)
==3029939==    by 0xB34BDC: __zend_free (zend_alloc.c:3322)
==3029939==    by 0xB336B8: _efree (zend_alloc.c:2750)
==3029939==    by 0xC8F3AD: list_entry_destructor (zend_list.c:182)
==3029939==    by 0xC41CEC: _zend_hash_del_el_ex (zend_hash.c:1487)
==3029939==    by 0xC427FB: zend_hash_index_del (zend_hash.c:1718)
==3029939==    by 0xC8EF67: zend_list_free (zend_list.c:58)
==3029939==    by 0xCBA840: rc_dtor_func (zend_variables.c:57)
==3029939==    by 0xB99D48: i_zval_ptr_dtor (zend_variables.h:45)
==3029939==    by 0xBA6731: i_free_compiled_variables (zend_execute.c:4068)

These two together show that the stream resource opened with tmpfile is freed at the end of a function/method call during the i_free_compiled_variables stage.

Then Xdebug later uses this freed memory when it tries to show an exception (xdebug_develop_throw_exception_hook) where it then also tries to free this memory.

However, I can't quite see how this happens, as I do increase the ref count for the exceptions when they get caught. But for some reason the refcount for the stream resource is not affected by this, and gets freed regardless. Reducing this test case is not so easy, as the freeing of this exception that Xdebug keeps hold of only happens after 8 other exceptions.

However, the fix is easy, and I've made a PR: https://github.com/xdebug/xdebug/pull/1033

derick

2025-08-29 08:55

administrator   ~0007350

@tenzap — it would be nice if you could test the xdebug_3_4 branch of Xdebug's GIT repo (https://github.com/xdebug/xdebug) to verify it is also fixed for you. I can then make a release.