Skip to content

Commit f55f0ae

Browse files
committed
Merge branch 'PHP-7.4'
* PHP-7.4: Fix #79019: Copied cURL handles upload empty file
2 parents fabe6aa + 2d0dec9 commit f55f0ae

10 files changed

+221
-70
lines changed

Diff for: ext/curl/interface.c

+105-55
Original file line numberDiff line numberDiff line change
@@ -1591,11 +1591,20 @@ static void curl_free_post(void **post)
15911591
}
15921592
/* }}} */
15931593

1594-
/* {{{ curl_free_stream
1594+
struct mime_data_cb_arg {
1595+
zend_string *filename;
1596+
php_stream *stream;
1597+
};
1598+
1599+
/* {{{ curl_free_cb_arg
15951600
*/
1596-
static void curl_free_stream(void **post)
1601+
static void curl_free_cb_arg(void **cb_arg_p)
15971602
{
1598-
php_stream_close((php_stream *)*post);
1603+
struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p;
1604+
1605+
ZEND_ASSERT(cb_arg->stream == NULL);
1606+
zend_string_release(cb_arg->filename);
1607+
efree(cb_arg);
15991608
}
16001609
/* }}} */
16011610

@@ -1692,11 +1701,13 @@ php_curl *alloc_curl_handle()
16921701

16931702
zend_llist_init(&ch->to_free->str, sizeof(char *), (llist_dtor_func_t)curl_free_string, 0);
16941703
zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t)curl_free_post, 0);
1695-
zend_llist_init(&ch->to_free->stream, sizeof(php_stream *), (llist_dtor_func_t)curl_free_stream, 0);
1704+
zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t)curl_free_cb_arg, 0);
16961705

16971706
ch->to_free->slist = emalloc(sizeof(HashTable));
16981707
zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0);
1699-
1708+
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
1709+
ZVAL_UNDEF(&ch->postfields);
1710+
#endif
17001711
return ch;
17011712
}
17021713
/* }}} */
@@ -1882,62 +1893,48 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
18821893
(*source->clone)++;
18831894
}
18841895

1885-
/* {{{ proto resource curl_copy_handle(resource ch)
1886-
Copy a cURL handle along with all of it's preferences */
1887-
PHP_FUNCTION(curl_copy_handle)
1896+
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
1897+
static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */
18881898
{
1889-
CURL *cp;
1890-
zval *zid;
1891-
php_curl *ch, *dupch;
1892-
1893-
ZEND_PARSE_PARAMETERS_START(1,1)
1894-
Z_PARAM_RESOURCE(zid)
1895-
ZEND_PARSE_PARAMETERS_END();
1899+
struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;
1900+
ssize_t numread;
18961901

1897-
if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
1898-
RETURN_THROWS();
1902+
if (cb_arg->stream == NULL) {
1903+
if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), "rb", IGNORE_PATH, NULL))) {
1904+
return CURL_READFUNC_ABORT;
1905+
}
18991906
}
1900-
1901-
cp = curl_easy_duphandle(ch->cp);
1902-
if (!cp) {
1903-
php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle");
1904-
RETURN_FALSE;
1907+
numread = php_stream_read(cb_arg->stream, buffer, nitems * size);
1908+
if (numread < 0) {
1909+
php_stream_close(cb_arg->stream);
1910+
cb_arg->stream = NULL;
1911+
return CURL_READFUNC_ABORT;
19051912
}
1906-
1907-
dupch = alloc_curl_handle();
1908-
dupch->cp = cp;
1909-
1910-
_php_setup_easy_copy_handlers(dupch, ch);
1911-
1912-
Z_ADDREF_P(zid);
1913-
1914-
ZVAL_RES(return_value, zend_register_resource(dupch, le_curl));
1915-
dupch->res = Z_RES_P(return_value);
1913+
return numread;
19161914
}
19171915
/* }}} */
19181916

1919-
#if LIBCURL_VERSION_NUM >= 0x073800
1920-
static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */
1917+
static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */
19211918
{
1922-
php_stream *stream = (php_stream *) arg;
1923-
ssize_t numread = php_stream_read(stream, buffer, nitems * size);
1919+
struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;
1920+
int res;
19241921

1925-
if (numread < 0) {
1926-
return CURL_READFUNC_ABORT;
1922+
if (cb_arg->stream == NULL) {
1923+
return CURL_SEEKFUNC_CANTSEEK;
19271924
}
1928-
return numread;
1925+
res = php_stream_seek(cb_arg->stream, offset, origin);
1926+
return res == SUCCESS ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK;
19291927
}
19301928
/* }}} */
19311929

1932-
static int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */
1930+
static void free_cb(void *arg) /* {{{ */
19331931
{
1934-
php_stream *stream = (php_stream *) arg;
1935-
int res = php_stream_seek(stream, offset, origin);
1932+
struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;
19361933

1937-
if (res) {
1938-
return CURL_SEEKFUNC_CANTSEEK;
1934+
if (cb_arg->stream != NULL) {
1935+
php_stream_close(cb_arg->stream);
1936+
cb_arg->stream = NULL;
19391937
}
1940-
return CURL_SEEKFUNC_OK;
19411938
}
19421939
/* }}} */
19431940
#endif
@@ -1948,7 +1945,7 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields
19481945
zval *current;
19491946
HashTable *postfields;
19501947
zend_string *string_key;
1951-
zend_ulong num_key;
1948+
zend_ulong num_key;
19521949
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
19531950
curl_mime *mime = NULL;
19541951
curl_mimepart *part;
@@ -1990,7 +1987,7 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields
19901987
zval *prop, rv;
19911988
char *type = NULL, *filename = NULL;
19921989
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
1993-
php_stream *stream;
1990+
struct mime_data_cb_arg *cb_arg;
19941991
#endif
19951992

19961993
prop = zend_read_property(curl_CURLFile_class, current, "name", sizeof("name")-1, 0, &rv);
@@ -2013,24 +2010,25 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields
20132010
}
20142011

20152012
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
2016-
if (!(stream = php_stream_open_wrapper(ZSTR_VAL(postval), "rb", IGNORE_PATH, NULL))) {
2017-
zend_string_release_ex(string_key, 0);
2018-
return FAILURE;
2019-
}
2013+
zval_ptr_dtor(&ch->postfields);
2014+
ZVAL_COPY(&ch->postfields, zpostfields);
2015+
2016+
cb_arg = emalloc(sizeof *cb_arg);
2017+
cb_arg->filename = zend_string_copy(postval);
2018+
cb_arg->stream = NULL;
2019+
20202020
part = curl_mime_addpart(mime);
20212021
if (part == NULL) {
2022-
php_stream_close(stream);
20232022
zend_string_release_ex(string_key, 0);
20242023
return FAILURE;
20252024
}
20262025
if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK
2027-
|| (form_error = curl_mime_data_cb(part, -1, read_cb, seek_cb, NULL, stream)) != CURLE_OK
2026+
|| (form_error = curl_mime_data_cb(part, -1, read_cb, seek_cb, free_cb, cb_arg)) != CURLE_OK
20282027
|| (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK
20292028
|| (form_error = curl_mime_type(part, type ? type : "application/octet-stream")) != CURLE_OK) {
2030-
php_stream_close(stream);
20312029
error = form_error;
20322030
}
2033-
zend_llist_add_element(&ch->to_free->stream, &stream);
2031+
zend_llist_add_element(&ch->to_free->stream, &cb_arg);
20342032
#else
20352033
form_error = curl_formadd(&first, &last,
20362034
CURLFORM_COPYNAME, ZSTR_VAL(string_key),
@@ -2098,11 +2096,60 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields
20982096
zend_llist_add_element(&ch->to_free->post, &first);
20992097
error = curl_easy_setopt(ch->cp, CURLOPT_HTTPPOST, first);
21002098
#endif
2099+
21012100
SAVE_CURL_ERROR(ch, error);
21022101
return error == CURLE_OK ? SUCCESS : FAILURE;
21032102
}
21042103
/* }}} */
21052104

2105+
/* {{{ proto resource curl_copy_handle(resource ch)
2106+
Copy a cURL handle along with all of it's preferences */
2107+
PHP_FUNCTION(curl_copy_handle)
2108+
{
2109+
CURL *cp;
2110+
zval *zid;
2111+
php_curl *ch, *dupch;
2112+
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
2113+
zval *postfields;
2114+
#endif
2115+
2116+
ZEND_PARSE_PARAMETERS_START(1,1)
2117+
Z_PARAM_RESOURCE(zid)
2118+
ZEND_PARSE_PARAMETERS_END();
2119+
2120+
if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
2121+
RETURN_FALSE;
2122+
}
2123+
2124+
cp = curl_easy_duphandle(ch->cp);
2125+
if (!cp) {
2126+
php_error_docref(NULL, E_WARNING, "Cannot duplicate cURL handle");
2127+
RETURN_FALSE;
2128+
}
2129+
2130+
dupch = alloc_curl_handle();
2131+
dupch->cp = cp;
2132+
2133+
_php_setup_easy_copy_handlers(dupch, ch);
2134+
2135+
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
2136+
postfields = &ch->postfields;
2137+
if (Z_TYPE_P(postfields) != IS_UNDEF) {
2138+
if (build_mime_structure_from_hash(dupch, postfields) != SUCCESS) {
2139+
_php_curl_close_ex(dupch);
2140+
php_error_docref(NULL, E_WARNING, "Cannot rebuild mime structure");
2141+
RETURN_FALSE;
2142+
}
2143+
}
2144+
#endif
2145+
2146+
Z_ADDREF_P(zid);
2147+
2148+
ZVAL_RES(return_value, zend_register_resource(dupch, le_curl));
2149+
dupch->res = Z_RES_P(return_value);
2150+
}
2151+
/* }}} */
2152+
21062153
static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ */
21072154
{
21082155
CURLcode error = CURLE_OK;
@@ -3286,6 +3333,9 @@ static void _php_curl_close_ex(php_curl *ch)
32863333
}
32873334

32883335
efree(ch->handlers);
3336+
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
3337+
zval_ptr_dtor(&ch->postfields);
3338+
#endif
32893339
efree(ch);
32903340
}
32913341
/* }}} */

Diff for: ext/curl/php_curl.h

+3
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ typedef struct {
181181
struct _php_curl_error err;
182182
zend_bool in_callback;
183183
uint32_t* clone;
184+
#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
185+
zval postfields;
186+
#endif
184187
} php_curl;
185188

186189
#define CURLOPT_SAFE_UPLOAD -1

Diff for: ext/curl/tests/bug27023.phpt

+4-4
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ var_dump(curl_exec($ch));
3838
curl_close($ch);
3939
?>
4040
--EXPECTF--
41-
string(%d) "curl_testdata1.txt|application/octet-stream"
42-
string(%d) "curl_testdata1.txt|text/plain"
43-
string(%d) "foo.txt|application/octet-stream"
44-
string(%d) "foo.txt|text/plain"
41+
string(%d) "curl_testdata1.txt|application/octet-stream|6"
42+
string(%d) "curl_testdata1.txt|text/plain|6"
43+
string(%d) "foo.txt|application/octet-stream|6"
44+
string(%d) "foo.txt|text/plain|6"

Diff for: ext/curl/tests/bug77711.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ curl_close($ch);
2323
?>
2424
--EXPECTF--
2525
bool(true)
26-
string(%d) "АБВ.txt|application/octet-stream"
26+
string(%d) "АБВ.txt|application/octet-stream|5"
2727
--CLEAN--
2828
<?php
2929
@unlink(__DIR__ . '/АБВ.txt');

Diff for: ext/curl/tests/curl_copy_handle_variation3.phpt

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ curl_close($ch2);
2828
?>
2929
--EXPECTF--
3030
bool(true)
31-
string(%d) "АБВ.txt|application/octet-stream"
32-
string(%d) "АБВ.txt|application/octet-stream"
31+
string(%d) "АБВ.txt|application/octet-stream|5"
32+
string(%d) "АБВ.txt|application/octet-stream|5"
3333
--CLEAN--
3434
<?php
3535
@unlink(__DIR__ . '/АБВ.txt');

Diff for: ext/curl/tests/curl_copy_handle_variation4.phpt

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
curl_copy_handle() allows to post CURLFile multiple times with curl_multi_exec()
3+
--SKIPIF--
4+
<?php include 'skipif.inc'; ?>
5+
--FILE--
6+
<?php
7+
include 'server.inc';
8+
$host = curl_cli_server_start();
9+
10+
$ch1 = curl_init();
11+
curl_setopt($ch1, CURLOPT_SAFE_UPLOAD, 1);
12+
curl_setopt($ch1, CURLOPT_URL, "{$host}/get.php?test=file");
13+
// curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);
14+
15+
$filename = __DIR__ . '/АБВ.txt';
16+
file_put_contents($filename, "Test.");
17+
$file = curl_file_create($filename);
18+
$params = array('file' => $file);
19+
var_dump(curl_setopt($ch1, CURLOPT_POSTFIELDS, $params));
20+
21+
$ch2 = curl_copy_handle($ch1);
22+
$ch3 = curl_copy_handle($ch1);
23+
24+
$mh = curl_multi_init();
25+
curl_multi_add_handle($mh, $ch1);
26+
curl_multi_add_handle($mh, $ch2);
27+
do {
28+
$status = curl_multi_exec($mh, $active);
29+
if ($active) {
30+
curl_multi_select($mh);
31+
}
32+
} while ($active && $status == CURLM_OK);
33+
34+
curl_multi_remove_handle($mh, $ch1);
35+
curl_multi_remove_handle($mh, $ch2);
36+
curl_multi_remove_handle($mh, $ch3);
37+
curl_multi_close($mh);
38+
?>
39+
===DONE===
40+
--EXPECTF--
41+
bool(true)
42+
АБВ.txt|application/octet-stream|5АБВ.txt|application/octet-stream|5===DONE===
43+
--CLEAN--
44+
<?php
45+
@unlink(__DIR__ . '/АБВ.txt');
46+
?>

Diff for: ext/curl/tests/curl_copy_handle_variation5.phpt

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
--TEST--
2+
curl_copy_handle() allows to post CURLFile multiple times if postfields change
3+
--SKIPIF--
4+
<?php include 'skipif.inc'; ?>
5+
--FILE--
6+
<?php
7+
include 'server.inc';
8+
$host = curl_cli_server_start();
9+
10+
$ch1 = curl_init();
11+
curl_setopt($ch1, CURLOPT_SAFE_UPLOAD, 1);
12+
curl_setopt($ch1, CURLOPT_URL, "{$host}/get.php?test=file");
13+
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);
14+
15+
$filename = __DIR__ . '/abc.txt';
16+
file_put_contents($filename, "Test.");
17+
$file = curl_file_create($filename);
18+
$params = array('file' => $file);
19+
var_dump(curl_setopt($ch1, CURLOPT_POSTFIELDS, $params));
20+
21+
$ch2 = curl_copy_handle($ch1);
22+
23+
$filename = __DIR__ . '/def.txt';
24+
file_put_contents($filename, "Other test.");
25+
$file = curl_file_create($filename);
26+
$params = array('file' => $file);
27+
var_dump(curl_setopt($ch2, CURLOPT_POSTFIELDS, $params));
28+
29+
$ch3 = curl_copy_handle($ch2);
30+
31+
var_dump(curl_exec($ch1));
32+
curl_close($ch1);
33+
34+
var_dump(curl_exec($ch2));
35+
curl_close($ch2);
36+
37+
var_dump(curl_exec($ch3));
38+
curl_close($ch3);
39+
?>
40+
===DONE===
41+
--EXPECTF--
42+
bool(true)
43+
bool(true)
44+
string(%d) "abc.txt|application/octet-stream|5"
45+
string(%d) "def.txt|application/octet-stream|11"
46+
string(%d) "def.txt|application/octet-stream|11"
47+
===DONE===
48+
--CLEAN--
49+
<?php
50+
@unlink(__DIR__ . '/abc.txt');
51+
@unlink(__DIR__ . '/def.txt');
52+
?>

0 commit comments

Comments
 (0)