Skip to content

Commit 267aea0

Browse files
committed
error when curl read func returns unexpected long
Raise a value error when the callback registered with CURLOPT_READFUNCTION returns an unexpected long. The function registered with CURLOPT_READFUNCTION should return a string. PHP then writes that string to a buffer and returns the length, so that curl can read that many bytes from the buffer. The function can also return CURL_READFUNC_ABORT and CURL_READFUNC_PAUSE, so it also supports returning longs. However, when it returns a long other than these two constants, it is interpreted as a length. PHP does not update the buffer, but does instruct curl it can read that many bytes from the buffer. It reads whatever uninitialized data that is in the buffer and sends it over the line to the server. This seems bad, so validate the return value of the read function and raise an error. Returning 0 is a bit of an edge case. It is not documented but does results in correct behavior (i.e. end-of-file). So we accept that, but don't advertise it as valid in the error message. I am not worried about BC-break, because sending an uninitialized buffer does not result in a valid request, so this would already not work. I considered silently casting the int to a string. This would be very "old PHP" behavior. I think throwing an error is more strict and better and more in line with "new PHP" behavior, if that makes sense. Related to php#10270
1 parent 6441f89 commit 267aea0

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

ext/curl/interface.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,17 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
820820
memcpy(data, Z_STRVAL(retval), length);
821821
} else if (Z_TYPE(retval) == IS_LONG) {
822822
length = Z_LVAL_P(&retval);
823+
824+
switch (length) {
825+
// Acceptable long values:
826+
case 0:
827+
case CURL_READFUNC_ABORT:
828+
case CURL_READFUNC_PAUSE:
829+
break;
830+
default:
831+
zend_value_error("The CURLOPT_READFUNCTION callback must return a string or CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE");
832+
length = CURL_READFUNC_ABORT;
833+
}
823834
}
824835
// TODO Do type error if invalid type?
825836
zval_ptr_dtor(&retval);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
error when CURLOPT_READFUNCTION returns an integer
3+
--EXTENSIONS--
4+
curl
5+
--FILE--
6+
<?php
7+
function custom_readfunction($oCurl, $hReadHandle, $iMaxOut)
8+
{
9+
static $size = 2;
10+
return $size--;
11+
}
12+
13+
include 'server.inc';
14+
$host = curl_cli_server_start();
15+
$ch = curl_init();
16+
curl_setopt($ch, CURLOPT_URL, "{$host}/get.php?test=post");
17+
curl_setopt($ch, CURLOPT_POST, ['f' => 'f']);
18+
curl_setopt($ch, CURLOPT_TIMEOUT, 2);
19+
curl_setopt($ch, CURLOPT_READFUNCTION, "custom_readfunction" );
20+
21+
try {
22+
curl_exec($ch);
23+
} catch (ValueError $e) {
24+
echo $e->getMessage() . "\n";
25+
}
26+
var_dump(curl_error($ch));
27+
?>
28+
--EXPECT--
29+
The CURLOPT_READFUNCTION callback must return a string or CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE
30+
string(29) "operation aborted by callback"

0 commit comments

Comments
 (0)