php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71861 Buffer Overrun in curl_exec() causing hang
Submitted: 2016-03-19 20:14 UTC Modified: 2016-04-04 09:28 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: paul at salesintel dot com Assigned: ab (profile)
Status: Closed Package: cURL related
PHP Version: 7.0.4 OS: Windows 10 x64
Private report: No CVE-ID: None
 [2016-03-19 20:14 UTC] paul at salesintel dot com
Description:
------------
When a couple of cURL options are set in a specific and valid way, and the curl.cainfo is set in php.ini, curl_exec() never returns and PHP does not throw any exception, causing indefinite hang.

Same code in php 5.6.x (in each x32/x64 ts/nts) worked as expected.

In php 7.0.4 x32/x64 nts builds,  hang occurs (ts versions not tried).


Test script:
---------------
FILE php.ini:

;change root path of your php accordingly 
extension_dir = "C:\bin\php\7.0.4\nts-x32\ext\"
extension=php_curl.dll
curl.cainfo = "C:\bin\php\cacert.pem"
;cacert.pem downloaded from "https://curl.haxx.se/ca/cacert.pem"


FILE repro.php
<?php
repro();
function repro() {
	$location = 'https://mail.microsoft.com/ews/exchange.asmx';
	$request = str_repeat(' ', 1025); 

	$ch = curl_init($location);
	curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
	curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
	curl_setopt($ch, CURLOPT_USERPWD, ' ');

	$response = curl_exec($ch);
}

Expected result:
----------------
Call to curl_exec($ch) returns.

Actual result:
--------------
Call to curl_exec($ch) never returns.

However....

If you comment out the "curl.cainfo=" setting in php.ini, curl_exec($ch) returns.

If you change $location to an invalid endpoint or one that does not require NTLM, curl_exec() returns.

If you remove the 'CURLAUTH_BASIC' leaving only 'CURLAUTH_NTLM', curl_exec() returns;

If you remove 'CURLAUTH_NTLM' leaving only 'CURLAUTH_BASIC', curl_exec() returns;

If you set an empty string ('') instead of one with one or more characters to CURLOPT_USERPWD, curl_exec() returns.

$request is a string with 1025 characters. If you remove one character, leaving a (suspiciously sized) 1024 character length $request, curl_exec($ch) returns.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-03-19 20:15 UTC] paul at salesintel dot com
-Summary: Buffer Overrun in curl_exec() causing hang +Summary: RE-OPENED, MORE INFO, Buffer Overrun in curl_exec() causing hang
 [2016-03-19 20:15 UTC] paul at salesintel dot com
I could not reopen the original bug # 71563
 [2016-03-20 12:41 UTC] laruence@php.net
Seems like a curl bug?
==95176== Conditional jump or move depends on uninitialised value(s)
==95176==    at 0x79128CD: gnutls_session_get_data (in /usr/lib/x86_64-linux-gnu/libgnutls.so.26.22.6)
==95176==    by 0x63A0819: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.3.0)
==95176==    by 0x63A0D29: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.3.0)
==95176==    by 0x63A17EF: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.3.0)
==95176==    by 0x63650DD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.3.0)
==95176==    by 0x63877F0: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.3.0)
==95176==    by 0x6388440: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.3.0)
==95176==    by 0x637FBA2: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.3.0)
==95176==    by 0x5F121B: zif_curl_exec (interface.c:2880)
==95176==    by 0x9F2902: ZEND_DO_ICALL_SPEC_HANDLER (zend_vm_execute.h:586)
==95176==    by 0x9F2331: execute_ex (zend_vm_execute.h:414)
==95176==    by 0x9F2443: zend_execute (zend_vm_execute.h:458)
==95176==  Uninitialised value was created by a stack allocation
==95176==    at 0x63A0100: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.3.0)
 [2016-03-20 13:26 UTC] paul at salesintel dot com
If the curl library is different between 5.6 and 7.0, than maybe there's a bug in curl; there is no hanging when running repro() in 5.6 as noted in the original bug report.

However, I would be surprised curl is coupling anything of their implementation to php cadence, and given the significant change in memory management and access between 5.6 and 7.0, and that the exception is saying something is uninitialized, I would think it more likely there's something the 7.0 curl wrapper is doing differently that is the underlying cause, and the exception occurring in the curl library is merely a symptom of it.

curl_exec() requires certain state to be configured before it's called, and that state is being set through the php curl wrapper, which could be de-allocating or moving something around between the state setting calls curl_init() & curl_setop(), and the curl_exec() call. What's also suspect is that minor changes to several different curl_setop() arguments can either expose or hide the problem.

I'm saying all this in hopes this issue isn't being 'punted' over to there being something wrong with curl, without first empirically ruling out php, given the aforementioned observations.
 [2016-03-28 13:56 UTC] krakjoe@php.net
-Status: Open +Status: Feedback
 [2016-03-28 13:56 UTC] krakjoe@php.net
I am using a slightly different kind of build to laurence, libssl.

I can reproduce memory errors in PHP 5 and 7 using the following:

<?php
$ch = curl_init('https://mail.microsoft.com/ews/exchange.asmx');

curl_exec($ch);

Here's one from 5:

==22050== Conditional jump or move depends on uninitialised value(s)
==22050==    at 0x6DC7A42: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22050==    by 0x6DCB253: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22050==    by 0x6DDFC27: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22050==    by 0x6DE9ACB: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22050==    by 0x6DEA270: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22050==    by 0x6DE18A2: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22050==    by 0x515CF5: zif_curl_exec (interface.c:2978)
==22050==    by 0xA2AA64: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:558)
==22050==    by 0xA31592: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2602)
==22050==    by 0xA29CBD: execute_ex (zend_vm_execute.h:363)
==22050==    by 0xA29DA3: zend_execute (zend_vm_execute.h:388)
==22050==    by 0x9DD2CE: zend_execute_scripts (zend.c:1341)

Here's one from 7:

==22058== Conditional jump or move depends on uninitialised value(s)
==22058==    at 0x6C5CA42: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22058==    by 0x6C60253: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22058==    by 0x6C74C27: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22058==    by 0x6C7EACB: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22058==    by 0x6C7F270: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22058==    by 0x6C768A2: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==22058==    by 0x607D99: zif_curl_exec (interface.c:2875)
==22058==    by 0xACA4F0: ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_UNUSED_HANDLER (zend_vm_execute.h:795)
==22058==    by 0xAC9360: execute_ex (zend_vm_execute.h:424)
==22058==    by 0xAC9562: zend_execute (zend_vm_execute.h:468)
==22058==    by 0xA63628: zend_execute_scripts (zend.c:1427)
==22058==    by 0x9A11AC: php_execute_script (main.c:2494)

This really does feel like a cURL bug, it would be best to make the cURL maintainers aware of the problem.
 [2016-03-28 13:57 UTC] krakjoe@php.net
Also, I forgot to mention, that I don't need cainfo set in php.ini, just used defaults.
 [2016-03-28 14:52 UTC] nikic@php.net
These valgrind warnings are definitely not related to PHP. E.g. this is what I get running curl directly from the CLI: https://gist.github.com/nikic/b7af10da8f73923a46a0

This does not necessarily mean that there is an issue in curl, as all of this starts with libssl, which is notoriously valgrind unsafe (don't ask unless you want to hear an answer). However, given the number of warnings originating in libcurl I'm inclined it doesn't seem unlikely that there is a genuine issue there.
 [2016-03-28 15:07 UTC] nikic@php.net
-Summary: RE-OPENED, MORE INFO, Buffer Overrun in curl_exec() causing hang +Summary: Buffer Overrun in curl_exec() causing hang -Status: Feedback +Status: Open
 [2016-03-28 15:07 UTC] nikic@php.net
I've opened an issue at libcurl: https://github.com/curl/curl/issues/734
 [2016-03-28 17:12 UTC] paul at salesintel dot com
That it can be reproduced even more simply in both 5 and 7 starts pointing the finger to something other than the php curl wrapper, should it not still be a bug, or problem, that an exception is never thrown? That the program just hangs?
 [2016-03-28 19:55 UTC] nikic@php.net
-Status: Open +Status: Verified
 [2016-03-28 19:55 UTC] nikic@php.net
Curl confirmed that these warnings are indirectly caused by openssl, so we can ignore those.

I was not able to reproduce the original issue on Ubuntu 14.04.

I am able to reproduce the hang on Windows, but cannot help beyond that.
 [2016-03-30 12:02 UTC] ab@php.net
I was able to reproduce the hang now. Both latest 5.6 and 7 reproduce, both use libcurl 7.47.1 on Windows. A step through shows, that where it hangs is curl_easy_perform(), around interface.c:2880 in 7.0;

So how it looks like, it is either a bug or a behavior change in libcurl. 7.48.0 show it as well, 7.42.1 doesn't. I currently don't see any suspicious commits to ext/libcurl that could cause this regression. Will check with a small C program whether it behaves same.

Thanks.
 [2016-03-30 14:47 UTC] ab@php.net
Till now it looks indeed like a libcurl issue, a simple program can reproduce the same behavior https://gist.github.com/weltling/15400de1018c7e6a4b476a415f08768d . I filed an issue to the upstream on github https://github.com/curl/curl/issues/741 .

Thanks.
 [2016-04-02 09:48 UTC] ab@php.net
-Status: Verified +Status: Feedback
 [2016-04-02 09:48 UTC] ab@php.net
@paul could you please check whether this build fixes the issue for you? http://windows.php.net/downloads/snaps/ostc/71861/ . Just drop it into the ext folder and activate in the php.ini instead of the php_curl.dll .

Thanks.
 [2016-04-03 17:19 UTC] paul at salesintel dot com
-Status: Feedback +Status: Open
 [2016-04-03 17:19 UTC] paul at salesintel dot com
The repro() function now completes in all cases (although supplied arguments are invalid garbage).

I removed my workaround in my actual project, where similar curl_* calls occur (but with valid parameters), and all appears to be working as expected.

I'm still curious, in the original php_curl version, why PHP hung rather than throwing an Exception.

Regardless, thanks @ab@php.net so much for resolving. 
-paul

p.s. we're using the non-thread-safe 32b & 64b versions of php. Any chance of getting a (hot)fixed build for those configurations?
 [2016-04-04 09:28 UTC] ab@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: ab
 [2016-04-04 09:28 UTC] ab@php.net
@paul thanks for the checks, I've uploaded the builds to the same location.

It's not a PHP issue, the hang was happening inside libcurl. You can read about more details in the github ticket i've linked above. We'll be checking to upgrade when the next cURL version is available, for now the patched 7.47.1 can be used.

Thanks.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Mar 29 12:01:27 2024 UTC