Skip to content

PHP Fatal error triggers pointer being freed was not allocated and malloc: double free for ptr errors #11078

Closed
@lucasnetau

Description

@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 curl/curl#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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions