[php-src] Issue #11078: PHP Fatal error triggers pointer being freed was not allocated and malloc: double free for ptr errors
| From: | lucasnetau | Date: | Sat, 15 Apr 2023 01:52:42 +0000 |
| Subject: | [php-src] Issue #11078: PHP Fatal error triggers pointer being freed was not allocated and malloc: double free for ptr errors | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-244201@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/11078
Author: lucasnetau
### Description
The following code:
https://gist.github.com/lucasnetau/244ba31f321307e06177f48582273e86
Resulted in this output:
```
php(95590,0x7ff847fe5680) malloc: double free for ptr 0x7fd2c0198000
php(95560,0x7ff847fe5680) malloc: *** error for object 0x7fc2e801800a: pointer being freed was not
allocated
SIGABRT
```
To trigger the issue a PHP Fatal error (out of memory) needs to occur in a write to a custom
streamWrapper, the test case forces this quickly (pointer issue occurs on each run, double free
occurs on most runs but not all). Notably when CURLOPT_HTTPHEADER is set the double free occurs in
libcurl, when CURLOPT_HTTPHEADER is not set then the point being freed error occurs.
Double Free backtrace:
```
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00007ff80467022a libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`:
-> 0x7ff80467022a <+10>: jae 0x7ff804670234 ; <+20>
0x7ff80467022c <+12>: movq %rax, %rdi
0x7ff80467022f <+15>: jmp 0x7ff804669ce2 ; cerror_nocancel
0x7ff804670234 <+20>: retq
Target 0: (php) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
* frame #0: 0x00007ff80467022a libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007ff8046a7f7b libsystem_pthread.dylib`pthread_kill + 263
frame #2: 0x00007ff8045f1ca5 libsystem_c.dylib`abort + 123
frame #3: 0x00007ff804507637 libsystem_malloc.dylib`malloc_vreport + 888
frame #4: 0x00007ff80451c897 libsystem_malloc.dylib`malloc_zone_error + 178
frame #5: 0x0000000101b920ce libcurl.4.dylib`Curl_close(datap=0x00007ff7bfefe9d0) at url.c:410:3
[opt]
frame #6: 0x0000000101b565e0 libcurl.4.dylib`curl_easy_cleanup(data=0x0000000000000000) at
easy.c:804:3 [opt]
frame #7: 0x00000001000735dd php`curl_free_obj(object=0x0000000103bcd798) at interface.c:2839:2
[opt]
frame #8: 0x000000010047b31e php`zend_objects_store_del(object=0x0000000103bcd798) at
zend_objects_API.c:200:4 [opt]
frame #9: 0x00000001003d26af php`zend_llist_destroy(l=0x0000000103bc70a8) at zend_llist.c:109:4
[opt]
frame #10: 0x00000001003d2703 php`zend_llist_clean(l=0x0000000103bc70a8) at zend_llist.c:123:2
[opt]
frame #11: 0x000000010007ab71 php`curl_multi_free_obj(object=<unavailable>) at
multi.c:555:2 [opt]
frame #12: 0x000000010047b167
php`zend_objects_store_free_object_storage(objects=<unavailable>,
fast_shutdown=<unavailable>) at zend_objects_API.c:122:6 [opt]
frame #13: 0x00000001003cf2f2
php`zend_shutdown_executor_values(fast_shutdown=<unavailable>) at zend_execute_API.c:399:2
[opt]
frame #14: 0x00000001003cf3d8 php`shutdown_executor at zend_execute_API.c:416:2 [opt]
frame #15: 0x00000001003df2bc php`zend_deactivate at zend.c:1258:2 [opt]
frame #16: 0x000000010037a1e5 php`php_request_shutdown(dummy=0x0000000000000000) at
main.c:1863:2 [opt]
frame #17: 0x00000001004c41dc php`do_cli(argc=4, argv=0x0000600000c08c90) at php_cli.c:1135:3
[opt]
frame #18: 0x00000001004c1e81 php`main(argc=4, argv=0x0000600000c08c90) at php_cli.c:1333:18
[opt]
frame #19: 0x00007ff804375310 dyld`start + 2432
(lldb) f 5
warning: libcurl.4.dylib was compiled with optimization - stepping may behave oddly; variables may
not be available.
frame #5: 0x0000000101b920ce libcurl.4.dylib`Curl_close(datap=0x00007ff7bfefe9d0) at url.c:410:3
[opt]
407 up_free(data);
408 Curl_safefree(data->state.buffer);
409 Curl_dyn_free(&data->state.headerb);
-> 410 Curl_safefree(data->state.ulbuf);
411 Curl_flush_cookies(data, TRUE);
412 Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
413 Curl_altsvc_cleanup(&data->asi);
(lldb) f 6
frame #6: 0x0000000101b565e0 libcurl.4.dylib`curl_easy_cleanup(data=0x0000000000000000) at
easy.c:804:3 [opt]
801 return;
802
803 sigpipe_ignore(data, &pipe_st);
-> 804 Curl_close(&data);
805 sigpipe_restore(&pipe_st);
806 }
807
(lldb) f 7
warning: php was compiled with optimization - stepping may behave oddly; variables may not be
available.
frame #7: 0x00000001000735dd php`curl_free_obj(object=0x0000000103bcd798) at interface.c:2839:2
[opt]
2836 curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing);
2837 curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing);
2838
-> 2839 curl_easy_cleanup(ch->cp);
2840
2841 /* cURL destructors should be invoked only by last curl handle */
2842 if (--(*ch->clone) == 0) {
```
Pointer being freed was not allocated:
```
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00007ff80467022a libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`:
-> 0x7ff80467022a <+10>: jae 0x7ff804670234 ; <+20>
0x7ff80467022c <+12>: movq %rax, %rdi
0x7ff80467022f <+15>: jmp 0x7ff804669ce2 ; cerror_nocancel
0x7ff804670234 <+20>: retq
Target 0: (php) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
* frame #0: 0x00007ff80467022a libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007ff8046a7f7b libsystem_pthread.dylib`pthread_kill + 263
frame #2: 0x00007ff8045f1ca5 libsystem_c.dylib`abort + 123
frame #3: 0x00007ff804507637 libsystem_malloc.dylib`malloc_vreport + 888
frame #4: 0x00007ff80450a951 libsystem_malloc.dylib`malloc_report + 151
frame #5: 0x00007ff804595860 libsystem_c.dylib`fclose + 140
frame #6: 0x0000000100391f0b php`stream_resource_regular_dtor(rsrc=<unavailable>) at
streams.c:1761:19 [opt]
frame #7: 0x00000001003f539a php`zend_resource_dtor(res=<unavailable>) at zend_list.c:73:3
[opt]
frame #8: 0x00000001003f56df php`zend_close_rsrc_list(ht=0x0000000100ed5c30) at
zend_list.c:225:5 [opt]
frame #9: 0x00000001003cf5dc
php`zend_shutdown_executor_values(fast_shutdown=<unavailable>) at zend_execute_API.c:277:3
[opt]
frame #10: 0x00000001003cfcd8 php`shutdown_executor at zend_execute_API.c:416:2 [opt]
frame #11: 0x00000001003dfbbc php`zend_deactivate at zend.c:1258:2 [opt]
frame #12: 0x000000010037aae5 php`php_request_shutdown(dummy=0x0000000000000000) at
main.c:1863:2 [opt]
frame #13: 0x00000001004c4adc php`do_cli(argc=2, argv=0x00006000002080e0) at php_cli.c:1135:3
[opt]
frame #14: 0x00000001004c2781 php`main(argc=2, argv=0x00006000002080e0) at php_cli.c:1333:18
[opt]
frame #15: 0x00007ff804375310 dyld`start + 2432
(lldb) f 6
warning: php was compiled with optimization - stepping may behave oddly; variables may not be
available.
frame #6: 0x0000000100391f0b php`stream_resource_regular_dtor(rsrc=<unavailable>) at
streams.c:1761:19 [opt]
1758 {
1759 php_stream *stream = (php_stream*)rsrc->ptr;
1760 /* set the return value for pclose */
-> 1761 FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE |
PHP_STREAM_FREE_RSRC_DTOR);
1762 }
1763
1764 static void stream_resource_persistent_dtor(zend_resource *rsrc)
(lldb) f 7
frame #7: 0x00000001003f539a php`zend_resource_dtor(res=<unavailable>) at zend_list.c:73:3
[opt]
70 ZEND_ASSERT(ld && "Unknown list entry type");
71
72 if (ld->list_dtor_ex) {
-> 73 ld->list_dtor_ex(&r);
74 }
75 }
76
```
Double free was reported to curl via https://github.com/curl/curl/issues/10964,
however since a subtle change in settings (CURLOPT_HTTPHEADER) triggers a crash in PHP I am posting
both here.
With ext-curl compiled with PHP_CURL_DEBUG=0 I only see the DTOR being called once (DTOR CALLED, ch
= *)
The comments in interface.c before where the crash occurs for Curl are interesting
https://github.com/php/php-src/blob/master/ext/curl/interface.c#L2829-2838
### PHP Version
PHP 8.2.5
### Operating System
MacOS / Linux