php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #79820 Use after free when type duplicated into ReflectionProperty gets resolved
Submitted: 2020-07-09 05:44 UTC Modified: 2020-07-15 08:54 UTC
Votes:2
Avg. Score:5.0 ± 0.0
Reproduced:2 of 2 (100.0%)
Same Version:0 (0.0%)
Same OS:2 (100.0%)
From: christopher dot broadbent at zencontrol dot com Assigned: nikic (profile)
Status: Closed Package: Reflection related
PHP Version: 7.4.7 OS: linux debian buster
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: christopher dot broadbent at zencontrol dot com
New email:
PHP Version: OS:

 

 [2020-07-09 05:44 UTC] christopher dot broadbent at zencontrol dot com
Description:
------------
We're currently hitting a fairly random segfault inside PHP. It's reproducible on each run, but we're having difficulty making a minimal reproduction case to upload here.

The builds we're using are from https://deb.sury.org/, but we're also hitting the problem on the builds from docker.io

I have a capture running under the rr debugger, and have the first free with a backtrace of

#0  zend_mm_free_small (bin_num=<optimized out>, ptr=0x7ff047a6caf0, heap=<optimized out>) at ./Zend/zend_alloc.c:1279
#1  zend_mm_free_heap (ptr=0x7ff047a6caf0, heap=<optimized out>) at ./Zend/zend_alloc.c:1370
#2  _efree (ptr=0x7ff047a6caf0) at ./Zend/zend_alloc.c:2550
#3  0x000056422fc1cb24 in zend_objects_store_del (object=0x7ff047a6cb18) at ./Zend/zend_objects_API.c:197
#4  0x000056422fc6c92f in zend_object_release (obj=<optimized out>) at ./Zend/zend_objects_API.h:75
#5  ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER () at ./Zend/zend_vm_execute.h:1767
#6  execute_ex (ex=0x7ff047a6caf0) at ./Zend/zend_vm_execute.h:53830
#7  0x000056422fc6da71 in zend_execute (op_array=0x7ff04a27f2a0, return_value=<optimized out>) at ./Zend/zend_vm_execute.h:57922
#8  0x000056422fbe74b3 in zend_execute_scripts (type=type@entry=0x8, retval=0x7ff04a21ca80, retval@entry=0x0, file_count=file_count@entry=0x3) at ./Zend/zend.c:1672
#9  0x000056422fb86b70 in php_execute_script (primary_file=<optimized out>) at ./main/main.c:2621
#10 0x000056422fc6fb86 in do_cli (argc=0xb, argv=0x564230f0cf40) at ./sapi/cli/php_cli.c:961
#11 0x000056422fa4e96b in main (argc=0xb, argv=0x564230f0cf40) at ./sapi/cli/php_cli.c:1356

and the double free has a backtrace of

#0  0x000056422fbc1e4b in zend_mm_free_small (bin_num=<optimized out>, ptr=0x7ff047a6caf0, heap=0x7ff04a200040) at ./Zend/zend_alloc.c:1278
#1  zend_mm_free_heap (ptr=0x7ff047a6caf0, heap=0x7ff04a200040) at ./Zend/zend_alloc.c:1370
#2  _efree (ptr=0x7ff047a6caf0) at ./Zend/zend_alloc.c:2550
#3  0x000056422fac9ce4 in zend_string_release (s=<optimized out>) at ./ext/reflection/php_reflection.c:225
#4  reflection_free_objects_storage (object=0x7ff047a6cb18) at ./ext/reflection/php_reflection.c:230
#5  0x000056422fc1cb66 in zend_objects_store_del (object=0x7ff047a6cb18) at ./Zend/zend_objects_API.c:193
#6  0x000056422fc6c92f in zend_object_release (obj=<optimized out>) at ./Zend/zend_objects_API.h:75
#7  ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER () at ./Zend/zend_vm_execute.h:1767
#8  execute_ex (ex=0x7ff047a6caf0) at ./Zend/zend_vm_execute.h:53830
#9  0x000056422fc6da71 in zend_execute (op_array=0x7ff04a27f2a0, return_value=<optimized out>) at ./Zend/zend_vm_execute.h:57922
#10 0x000056422fbe74b3 in zend_execute_scripts (type=type@entry=0x8, retval=0x7ff04a21ca80, retval@entry=0x0, file_count=file_count@entry=0x3) at ./Zend/zend.c:1672
#11 0x000056422fb86b70 in php_execute_script (primary_file=<optimized out>) at ./main/main.c:2621
#12 0x000056422fc6fb86 in do_cli (argc=0xb, argv=0x564230f0cf40) at ./sapi/cli/php_cli.c:961
#13 0x000056422fa4e96b in main (argc=0xb, argv=0x564230f0cf40) at ./sapi/cli/php_cli.c:1356

This sticks the heap in to a state where

(rr) p $heap->free_slot[8]
$59 = (zend_mm_free_slot *) 0x7ff047a6caf0
(rr) p $heap->free_slot[8]->next_free_slot
$60 = (zend_mm_free_slot *) 0x7ff047a6caf0
(rr) p $heap->free_slot[8]->next_free_slot->next_free_slot
$61 = (zend_mm_free_slot *) 0x7ff047a6caf0

etc, and ends up segfaulting on the second-next allocation


Running under valgrind gives a double free in a sightly different place:

  USE_ZEND_ALLOC=0 valgrind  -- php our_args

outputs a lot of..stuff before eventually crashing with

==29090== Invalid free() / delete / delete[] / realloc()
==29090==    at 0x48369AB: free (vg_replace_malloc.c:530)
==29090==    by 0x28ACE3: zend_string_release (zend_string.h:277)
==29090==    by 0x28ACE3: reflection_free_objects_storage (php_reflection.c:230)
==29090==    by 0x3DDB65: zend_objects_store_del (zend_objects_API.c:193)
==29090==    by 0x42D92E: zend_object_release (zend_objects_API.h:75)
==29090==    by 0x42D92E: ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:1767)
==29090==    by 0x42D92E: execute_ex (zend_vm_execute.h:53830)
==29090==    by 0x42EA70: zend_execute (zend_vm_execute.h:57922)
==29090==    by 0x3A84B2: zend_execute_scripts (zend.c:1672)
==29090==    by 0x347B6F: php_execute_script (main.c:2621)
==29090==    by 0x430B85: do_cli (php_cli.c:961)
==29090==    by 0x20F96A: main (php_cli.c:1356)
==29090==  Address 0xc432c40 is 0 bytes inside a block of size 80 free'd
==29090==    at 0x48369AB: free (vg_replace_malloc.c:530)
==29090==    by 0x3E3E64: zend_string_release (zend_string.h:277)
==29090==    by 0x3E3E64: zend_resolve_class_type (zend_execute.c:947)
==29090==    by 0x410B64: i_zend_check_property_type (zend_execute.c:961)
==29090==    by 0x410B64: i_zend_verify_property_type (zend_execute.c:984)
==29090==    by 0x410B64: zend_verify_property_type (zend_execute.c:993)
==29090==    by 0x3DBA85: zend_std_write_property (zend_object_handlers.c:897)
==29090==    by 0x3B244E: zend_update_property_ex (zend_API.c:4115)
==29090==    by 0x289C4B: zim_reflection_property_setValue (php_reflection.c:5485)
==29090==    by 0x42DE5F: ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER (zend_vm_execute.h:1618)
==29090==    by 0x42DE5F: execute_ex (zend_vm_execute.h:53826)
==29090==    by 0x42EA70: zend_execute (zend_vm_execute.h:57922)
==29090==    by 0x3A84B2: zend_execute_scripts (zend.c:1672)
==29090==    by 0x347B6F: php_execute_script (main.c:2621)
==29090==    by 0x430B85: do_cli (php_cli.c:961)
==29090==    by 0x20F96A: main (php_cli.c:1356)
==29090==  Block was alloc'd at
==29090==    at 0x483577F: malloc (vg_replace_malloc.c:299)
==29090==    by 0x37EF38: __zend_malloc (zend_alloc.c:2976)
==29090==    by 0x38629B: zend_string_alloc (zend_string.h:133)
==29090==    by 0x38629B: zend_concat3 (zend_compile.c:791)
==29090==    by 0x387243: zend_compile_typename (zend_compile.c:5318)
==29090==    by 0x38BF6F: zend_compile_prop_decl (zend_compile.c:6100)
==29090==    by 0x393EF0: zend_compile_prop_group (zend_compile.c:6178)
==29090==    by 0x393EF0: zend_compile_stmt (zend_compile.c:8538)
==29090==    by 0x394E46: zend_compile_stmt_list (zend_compile.c:5262)
==29090==    by 0x394E46: zend_compile_stmt_list (zend_compile.c:5257)
==29090==    by 0x393D59: zend_compile_stmt (zend_compile.c:8479)
==29090==    by 0x395BDD: zend_compile_class_decl (zend_compile.c:6467)
==29090==    by 0x396AA6: zend_compile_top_stmt (zend_compile.c:8454)
==29090==    by 0x396ACF: zend_compile_top_stmt (zend_compile.c:8443)
==29090==    by 0x36E844: zend_compile (zend_language_scanner.l:614)

I can probably get the full run from the rr debugger uploaded, as well as the valgrind logs, if needed

$ php -m
[PHP Modules]
amqp
bcmath
calendar
Core
ctype
curl
date
dom
exif
FFI
fileinfo
filter
ftp
gettext
hash
iconv
intl
json
libxml
mbstring
mysqli
mysqlnd
openssl
pcntl
pcre
PDO
pdo_mysql
Phar
posix
rdkafka
readline
redis
Reflection
session
shmop
SimpleXML
sockets
sodium
SPL
standard
sysvmsg
sysvsem
sysvshm
tokenizer
xml
xmlreader
xmlwriter
xsl
Zend OPcache
zlib

[Zend Modules]
Zend OPcache



Patches

0001-Add-refs-to-prop-names-to-avoid-use-after-free (last revision 2020-07-15 05:44 UTC by chris-broadbent at zencontrol dot com)

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-07-09 08:41 UTC] nikic@php.net
On which version/commit of PHP were the gdb backtraces gathered? I can't find any version of PHP 7.4 that has a zend_string_release call in php_reflection.c:225. The valgrind trace looks more plausible.

There might be some relation to https://github.com/php/php-src/commit/c9abfaec6bf61bcef6d9651827b49cc7789018fd.
 [2020-07-09 08:47 UTC] nikic@php.net
Not seeing what could cause this. Property types getting resolved while being held by reflection is something we specifically protect against.
 [2020-07-13 23:54 UTC] christopher dot broadbent at zencontrol dot com
Upgraded to 7.4.8, still crashes.

> I can't find any version of PHP 7.4 that has a zend_string_release call in php_reflection.c:225

This might be the inline of the release call being assigned weird line numbers as it runs? The frame

#4  reflection_free_objects_storage (object=0x7ff047a6cb18) at ./ext/reflection/php_reflection.c:230

is calling:

zend_string_release(ZEND_TYPE_NAME(type_ref->type));


Regardless, I've asked the package maintainer if there are any patches applied before he builds: https://github.com/oerdnj/deb.sury.org/issues/1433

I doubt that is the problem though as we're also hitting this issue on the official docker containers, and they don't seem to do any patching. 

Docker images: https://hub.docker.com/layers/php/library/php/7.4.8-buster/images/sha256-96e4c07b30977303a1bc53a269ea5284b3d85d4d89787948c3a594dc89a03698?context=explore
Docker image source: https://github.com/docker-library/php/tree/master/7.4/buster
 [2020-07-14 00:41 UTC] christopher dot broadbent at zencontrol dot com
Have build 7.4.8 from source .tar.gz file on website, it exhibits the same crash. Trying to get it built and run with sanitizers now in case they give anything useful.
 [2020-07-14 01:06 UTC] christopher dot broadbent at zencontrol dot com
Built php 7.4.8 with

software@debian-software:~/Documents/php-7.4.8$ ./configure --prefix=/home/software/php --with-openssl --with-zlib --with-curl --enable-intl --enable-mbstring --with-pdo-mysql --with-sodium --with-pear --enable-debug CFLAGS="-fsanitize=address" CXXFLAGS="-fsanitize=address"

running then gives me the output


software@debian-software:~/Documents/zencontrol/gateway$ USE_ZEND_ALLOC=0 ~/php/bin/php vendor/phpunit/phpunit/phpunit --group default --configuration phpunit.xml --filter "/(Gateway\\\\Chris\\\\Heres\\\\Your\\\\Test\\\\Reproduction\\\\Of\\\\Segfault\\\\SeggyTest::testTesties)( .*)?$/" --test-suffix SeggyTest.php tests/Chris/Heres/Your/Test/Reproduction/Of/Segfault
PHPUnit 8.4.1 by Sebastian Bergmann and contributors.

Runtime:       PHP 7.4.8
Configuration: /home/software/Documents/zencontrol/gateway/phpunit.xml

=================================================================
==30325==ERROR: AddressSanitizer: heap-use-after-free on address 0x607000165804 at pc 0x5653a1555e1e bp 0x7ffc99079730 sp 0x7ffc99079728
READ of size 4 at 0x607000165804 thread T0
    #0 0x5653a1555e1d in zend_string_addref /home/software/Documents/php-7.4.8/Zend/zend_string.h:117
    #1 0x5653a156046d in reflection_type_factory /home/software/Documents/php-7.4.8/ext/reflection/php_reflection.c:1165
    #2 0x5653a158ae55 in zim_reflection_property_getType /home/software/Documents/php-7.4.8/ext/reflection/php_reflection.c:5623
    #3 0x5653a1c65114 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /home/software/Documents/php-7.4.8/Zend/zend_vm_execute.h:1730
    #4 0x5653a1d91a7b in execute_ex /home/software/Documents/php-7.4.8/Zend/zend_vm_execute.h:53828
    #5 0x5653a1d9dc18 in zend_execute /home/software/Documents/php-7.4.8/Zend/zend_vm_execute.h:57920
    #6 0x5653a1b491ef in zend_execute_scripts /home/software/Documents/php-7.4.8/Zend/zend.c:1678
    #7 0x5653a19e78a8 in php_execute_script /home/software/Documents/php-7.4.8/main/main.c:2621
    #8 0x5653a1da426b in do_cli /home/software/Documents/php-7.4.8/sapi/cli/php_cli.c:964
    #9 0x5653a1da6515 in main /home/software/Documents/php-7.4.8/sapi/cli/php_cli.c:1359
    #10 0x7f017115a09a in __libc_start_main ../csu/libc-start.c:308
    #11 0x5653a0f224c9 in _start (/home/software/php/bin/php+0x6064c9)

0x607000165804 is located 4 bytes inside of 80-byte region [0x607000165800,0x607000165850)
freed by thread T0 here:
    #0 0x7f01740defb0 in __interceptor_free (/lib/x86_64-linux-gnu/libasan.so.5+0xe8fb0)
    #1 0x5653a1ab64d6 in _efree_custom /home/software/Documents/php-7.4.8/Zend/zend_alloc.c:2426
    #2 0x5653a1ab66f2 in _efree /home/software/Documents/php-7.4.8/Zend/zend_alloc.c:2546
    #3 0x5653a1c36d5d in zend_string_release /home/software/Documents/php-7.4.8/Zend/zend_string.h:277
    #4 0x5653a1c3f7f9 in zend_resolve_class_type /home/software/Documents/php-7.4.8/Zend/zend_execute.c:947
    #5 0x5653a1c3f9e1 in i_zend_check_property_type /home/software/Documents/php-7.4.8/Zend/zend_execute.c:961
    #6 0x5653a1c3fcc1 in i_zend_verify_property_type /home/software/Documents/php-7.4.8/Zend/zend_execute.c:984
    #7 0x5653a1c3fd12 in zend_verify_property_type /home/software/Documents/php-7.4.8/Zend/zend_execute.c:993
    #8 0x5653a1c13073 in zend_std_write_property /home/software/Documents/php-7.4.8/Zend/zend_object_handlers.c:897
    #9 0x5653a1b7269b in zend_update_property_ex /home/software/Documents/php-7.4.8/Zend/zend_API.c:4115
    #10 0x5653a15898c5 in zim_reflection_property_setValue /home/software/Documents/php-7.4.8/ext/reflection/php_reflection.c:5485
    #11 0x5653a1c6433e in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER /home/software/Documents/php-7.4.8/Zend/zend_vm_execute.h:1618
    #12 0x5653a1d91a4b in execute_ex /home/software/Documents/php-7.4.8/Zend/zend_vm_execute.h:53824
    #13 0x5653a1d9dc18 in zend_execute /home/software/Documents/php-7.4.8/Zend/zend_vm_execute.h:57920
    #14 0x5653a1b491ef in zend_execute_scripts /home/software/Documents/php-7.4.8/Zend/zend.c:1678
    #15 0x5653a19e78a8 in php_execute_script /home/software/Documents/php-7.4.8/main/main.c:2621
    #16 0x5653a1da426b in do_cli /home/software/Documents/php-7.4.8/sapi/cli/php_cli.c:964
    #17 0x5653a1da6515 in main /home/software/Documents/php-7.4.8/sapi/cli/php_cli.c:1359
    #18 0x7f017115a09a in __libc_start_main ../csu/libc-start.c:308

previously allocated by thread T0 here:
    #0 0x7f01740df330 in __interceptor_malloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9330)
    #1 0x5653a1ab85ff in __zend_malloc /home/software/Documents/php-7.4.8/Zend/zend_alloc.c:2976
    #2 0x5653a1ab63f6 in _malloc_custom /home/software/Documents/php-7.4.8/Zend/zend_alloc.c:2417
    #3 0x5653a1ab6647 in _emalloc /home/software/Documents/php-7.4.8/Zend/zend_alloc.c:2536
    #4 0x5653a1ab8c29 in zend_string_alloc Zend/zend_string.h:133
    #5 0x5653a1abf605 in zend_concat3 /home/software/Documents/php-7.4.8/Zend/zend_compile.c:791
    #6 0x5653a1abf70d in zend_concat_names /home/software/Documents/php-7.4.8/Zend/zend_compile.c:802
    #7 0x5653a1abf7bc in zend_prefix_with_ns /home/software/Documents/php-7.4.8/Zend/zend_compile.c:808
    #8 0x5653a1ac0136 in zend_resolve_class_name /home/software/Documents/php-7.4.8/Zend/zend_compile.c:944
    #9 0x5653a1ac01fc in zend_resolve_class_name_ast /home/software/Documents/php-7.4.8/Zend/zend_compile.c:954
    #10 0x5653a1ae09ee in zend_compile_typename /home/software/Documents/php-7.4.8/Zend/zend_compile.c:5318
    #11 0x5653a1ae68f1 in zend_compile_prop_decl /home/software/Documents/php-7.4.8/Zend/zend_compile.c:6100
    #12 0x5653a1ae6f25 in zend_compile_prop_group /home/software/Documents/php-7.4.8/Zend/zend_compile.c:6178
    #13 0x5653a1afa741 in zend_compile_stmt /home/software/Documents/php-7.4.8/Zend/zend_compile.c:8538
    #14 0x5653a1ae03c2 in zend_compile_stmt_list /home/software/Documents/php-7.4.8/Zend/zend_compile.c:5262
    #15 0x5653a1afa5bb in zend_compile_stmt /home/software/Documents/php-7.4.8/Zend/zend_compile.c:8479
    #16 0x5653a1ae91a1 in zend_compile_class_decl /home/software/Documents/php-7.4.8/Zend/zend_compile.c:6467
    #17 0x5653a1afa21a in zend_compile_top_stmt /home/software/Documents/php-7.4.8/Zend/zend_compile.c:8454
    #18 0x5653a1afa040 in zend_compile_top_stmt /home/software/Documents/php-7.4.8/Zend/zend_compile.c:8443
    #19 0x5653a1a7023a in zend_compile Zend/zend_language_scanner.l:614
    #20 0x5653a1a706f4 in compile_file Zend/zend_language_scanner.l:650
    #21 0x5653a150fff0 in phar_compile_file /home/software/Documents/php-7.4.8/ext/phar/phar.c:3299
    #22 0x5653a1a709e4 in compile_filename Zend/zend_language_scanner.l:671
    #23 0x5653a1c57fb1 in zend_include_or_eval /home/software/Documents/php-7.4.8/Zend/zend_execute.c:4240
    #24 0x5653a1d45006 in ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER /home/software/Documents/php-7.4.8/Zend/zend_vm_execute.h:37728
    #25 0x5653a1d9af48 in execute_ex /home/software/Documents/php-7.4.8/Zend/zend_vm_execute.h:56968
    #26 0x5653a1b0a7f0 in zend_call_function /home/software/Documents/php-7.4.8/Zend/zend_execute_API.c:813
    #27 0x5653a15e8487 in zif_spl_autoload_call /home/software/Documents/php-7.4.8/ext/spl/php_spl.c:452
    #28 0x5653a1b0aa65 in zend_call_function /home/software/Documents/php-7.4.8/Zend/zend_execute_API.c:826
    #29 0x5653a1b0c0e1 in zend_lookup_class_ex /home/software/Documents/php-7.4.8/Zend/zend_execute_API.c:995

SUMMARY: AddressSanitizer: heap-use-after-free /home/software/Documents/php-7.4.8/Zend/zend_string.h:117 in zend_string_addref
Shadow bytes around the buggy address:
  0x0c0e80024ab0: fd fd fd fd fd fa fa fa fa fa 00 00 00 00 00 00
  0x0c0e80024ac0: 00 00 00 fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c0e80024ad0: fd fd fa fa fa fa fd fd fd fd fd fd fd fd fd fd
  0x0c0e80024ae0: fa fa fa fa fd fd fd fd fd fd fd fd fd fa fa fa
  0x0c0e80024af0: fa fa fd fd fd fd fd fd fd fd fd fa fa fa fa fa
=>0x0c0e80024b00:[fd]fd fd fd fd fd fd fd fd fd fa fa fa fa 00 00
  0x0c0e80024b10: 00 00 00 00 00 00 00 fa fa fa fa fa fd fd fd fd
  0x0c0e80024b20: fd fd fd fd fd fa fa fa fa fa 00 00 00 00 00 00
  0x0c0e80024b30: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c0e80024b40: 00 00 fa fa fa fa 00 00 00 00 00 00 00 00 00 fa
  0x0c0e80024b50: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==30325==ABORTING




I might be able to upload the reproduction case we have, but it looks like it's a fairly large amount of code, and I'm not familiar with any of it.
 [2020-07-14 07:48 UTC] ondrej@php.net
> On which version/commit of PHP were the gdb backtraces gathered? I can't find > any version of PHP 7.4 that has a zend_string_release call in  php_reflection.c:225. The valgrind trace looks more plausible.

If you look carefully, the backtrace matches the function with it's location and zend_string_release is always inlined, so the php_reflection.c:225 is just a red herring as it just got inlined (and the line number marks the location of the affected block where it is used).

The packages don't patch php_reflection.c at all.
 [2020-07-14 23:22 UTC] christopher dot broadbent at zencontrol dot com
This is probably my inexperience with the code-base, but shouldn't the implementation for

    ZEND_METHOD(reflection_property, __construct)

by increment the ref-count for the value copied by

    reference->prop = *property_info;

when dynam_prop is false? It seems to be the thing keeping a reference around to the deallocated string, causing the use-after-free, and I can't see anywhere in the code path where it bumps the reference count.
 [2020-07-14 23:27 UTC] christopher dot broadbent at zencontrol dot com
Sorry, I meant to say bump the ref counters on

    reference->prop.type
 [2020-07-14 23:53 UTC] christopher dot broadbent at zencontrol dot com
I've made a change to php_reflecation.c lines 5281 to 5302, on 7.4.8 release, inside reflection_property____construct


	reference = (property_reference*) emalloc(sizeof(property_reference));
	if (dynam_prop) {
		reference->prop.flags = ZEND_ACC_PUBLIC;
		reference->prop.name = name;
		reference->prop.doc_comment = NULL;
		reference->prop.ce = ce;
		reference->dynamic = 1;
	} else {
		reference->prop = *property_info;
		reference->dynamic = 0;

+		if (ZEND_TYPE_IS_NAME(reference->prop.type)) {
+			zend_string_addref(ZEND_TYPE_NAME(reference->prop.type));
+		}
	}
	
	reference->unmangled_name = zend_string_copy(name);
	intern->ptr = reference;
	intern->ref_type = REF_TYPE_PROPERTY;
	intern->ce = ce;
	intern->ignore_visibility = 0;
}


This fixes the segfault in all the reproduction cases we've got, but I'm not familiar enough with the rest of the code to know if this is correct or causes leaks.
 [2020-07-15 05:44 UTC] chris-broadbent at zencontrol dot com
The following patch has been added/updated:

Patch Name: 0001-Add-refs-to-prop-names-to-avoid-use-after-free
Revision:   1594791891
URL:        https://bugs.php.net/patch-display.php?bug=79820&patch=0001-Add-refs-to-prop-names-to-avoid-use-after-free&revision=1594791891
 [2020-07-15 08:39 UTC] nikic@php.net
Right you are! Minimal reproducer:

<?php

class Test {
    public stdClass $prop; 
}   
 
$rp = new ReflectionProperty(Test::class, 'prop'); 
$test = new Test; 
$test->prop = new stdClass; 
var_dump($rp->getType()->getName());

For PHP 8 this has already been fixed by https://github.com/php/php-src/commit/0e3045ae69d1b80c21b2779df721a4ad8bcda401.
 [2020-07-15 08:54 UTC] nikic@php.net
-Summary: double-free causing heap corruption +Summary: Use after free when type duplicated into ReflectionProperty gets resolved -Package: Reproducible crash +Package: Reflection related -Assigned To: +Assigned To: nikic
 [2020-07-15 09:00 UTC] nikic@php.net
Automatic comment on behalf of chris-broadbent@zencontrol.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=ee7c7a8e26b99e3b25a7d41abfe1a2c37b3f6968
Log: Fixed bug #79820
 [2020-07-15 09:00 UTC] nikic@php.net
-Status: Assigned +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 11:01:29 2024 UTC