diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | ext/curl/interface.c | 24 | ||||
-rw-r--r-- | ext/curl/tests/curl_multi_segfault.phpt | 56 |
3 files changed, 81 insertions, 0 deletions
@@ -7,6 +7,7 @@ PHP NEWS (Johannes) - cURL extension: + . Fixed bug (segfault due to libcurl connection caching). (Pierrick) . Fixed bug #63795 (CURL >= 7.28.0 no longer support value 1 for CURLOPT_SSL_VERIFYHOST). (Pierrick) . Fixed bug #63352 (Can't enable hostname validation when using curl stream diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 9ac89c5ff1..37747fcc3a 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -971,6 +971,15 @@ PHP_MSHUTDOWN_FUNCTION(curl) } /* }}} */ +/* {{{ curl_write_nothing + * Used as a work around. See _php_curl_close_ex + */ +static size_t curl_write_nothing(char *data, size_t size, size_t nmemb, void *ctx) +{ + return size * nmemb; +} +/* }}} */ + /* {{{ curl_write */ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx) @@ -2604,6 +2613,21 @@ static void _php_curl_close_ex(php_curl *ch TSRMLS_DC) #endif _php_curl_verify_handlers(ch, 0 TSRMLS_CC); + + /* + * Libcurl is doing connection caching. When easy handle is cleaned up, + * if the handle was previously used by the curl_multi_api, the connection + * remains open un the curl multi handle is cleaned up. Some protocols are + * sending content like the FTP one, and libcurl try to use the + * WRITEFUNCTION or the HEADERFUNCTION. Since structures used in those + * callback are freed, we need to use an other callback to which avoid + * segfaults. + * + * Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2 + */ + curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing); + curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing); + curl_easy_cleanup(ch->cp); /* cURL destructors should be invoked only by last curl handle */ diff --git a/ext/curl/tests/curl_multi_segfault.phpt b/ext/curl/tests/curl_multi_segfault.phpt new file mode 100644 index 0000000000..dde8189411 --- /dev/null +++ b/ext/curl/tests/curl_multi_segfault.phpt @@ -0,0 +1,56 @@ +--TEST-- +Segfault due to libcurl connection caching +--CREDITS-- +--SKIPIF-- +<?php +if (!extension_loaded("curl")) exit("skip curl extension not loaded"); +if (false === getenv('PHP_CURL_FTP_REMOTE_SERVER')) exit("skip PHP_CURL_FTP_REMOTE_SERVER env variable is not defined"); +if (false === getenv('PHP_CURL_FTP_REMOTE_USER')) exit("skip PHP_CURL_FTP_REMOTE_USER env variable is not defined"); +if (false === getenv('PHP_CURL_FTP_REMOTE_PASSWD')) exit("skip PHP_CURL_FTP_REMOTE_PASSWD env variable is not defined"); +?> +--FILE-- +<?php + $host = getenv('PHP_CURL_FTP_REMOTE_SERVER'); + $username = getenv('PHP_CURL_FTP_REMOTE_USER'); + $password = getenv('PHP_CURL_FTP_REMOTE_PASSWD'); + + // FTP this script to a server + $fp = fopen ( __FILE__ , "r" ); + $url = "ftp://$username:$password@$host/" ; + + $ch = curl_init (); + + curl_setopt ( $ch , CURLOPT_URL, $url ); + curl_setopt ( $ch , CURLOPT_RETURNTRANSFER, 1 ); + + //force passive connection + curl_setopt ( $ch , CURLOPT_FTP_USE_EPSV, 0 ); + curl_setopt ( $ch , CURLOPT_FTP_SKIP_PASV_IP, 1 ); + + $cmh = curl_multi_init(); + curl_multi_add_handle($cmh, $ch); + + $active = null; + + do { + $mrc = curl_multi_exec($cmh, $active); + } while ($mrc == CURLM_CALL_MULTI_PERFORM); + + + while ($active && $mrc == CURLM_OK) { + if (curl_multi_select($cmh) != -1) { + do { + $mrc = curl_multi_exec($cmh, $active); + } while ($mrc == CURLM_CALL_MULTI_PERFORM); + } + } + + var_dump(is_string(curl_multi_getcontent($ch))); + curl_multi_remove_handle($cmh, $ch); + curl_close($ch); + curl_multi_close($cmh); +?> +===DONE=== +--EXPECTF-- +bool(true) +===DONE=== |