Skip to content

Commit 28febe2

Browse files
authored
ext/curl: abort curl transfer if callback throws exception (#22745)
Closes GH-16513 Closes GH-16790
1 parent b704c6c commit 28febe2

7 files changed

Lines changed: 283 additions & 12 deletions

ext/curl/interface.c

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,8 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
583583
_php_curl_verify_handlers(ch, /* reporterror */ true);
584584
/* TODO Check callback returns an int or something castable to int */
585585
length = php_curl_get_long(&retval);
586+
} else {
587+
length = -1;
586588
}
587589

588590
zval_ptr_dtor(&argv[0]);
@@ -632,14 +634,14 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
632634
static int curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
633635
{
634636
php_curl *ch = (php_curl *)clientp;
635-
int rval = 0;
637+
int rval = 1; // error
636638

637639
#if PHP_CURL_DEBUG
638640
fprintf(stderr, "curl_progress() called\n");
639641
fprintf(stderr, "clientp = %p, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", clientp, dltotal, dlnow, ultotal, ulnow);
640642
#endif
641643
if (!ZEND_FCC_INITIALIZED(ch->handlers.progress)) {
642-
return rval;
644+
return 0; // ok
643645
}
644646

645647
zval args[5];
@@ -659,8 +661,8 @@ static int curl_progress(void *clientp, double dltotal, double dlnow, double ult
659661
if (!Z_ISUNDEF(retval)) {
660662
_php_curl_verify_handlers(ch, /* reporterror */ true);
661663
/* TODO Check callback returns an int or something castable to int */
662-
if (0 != php_curl_get_long(&retval)) {
663-
rval = 1;
664+
if (0 == php_curl_get_long(&retval)) {
665+
rval = 0; // ok
664666
}
665667
}
666668

@@ -673,14 +675,14 @@ static int curl_progress(void *clientp, double dltotal, double dlnow, double ult
673675
static int curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
674676
{
675677
php_curl *ch = (php_curl *)clientp;
676-
int rval = 0;
678+
int rval = 1; // error
677679

678680
#if PHP_CURL_DEBUG
679681
fprintf(stderr, "curl_xferinfo() called\n");
680682
fprintf(stderr, "clientp = %p, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\n", clientp, dltotal, dlnow, ultotal, ulnow);
681683
#endif
682-
if (!ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) {
683-
return rval;
684+
if (UNEXPECTED(!ZEND_FCC_INITIALIZED(ch->handlers.xferinfo))) {
685+
return 0; // ok
684686
}
685687

686688
zval argv[5];
@@ -700,8 +702,8 @@ static int curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, cu
700702
if (!Z_ISUNDEF(retval)) {
701703
_php_curl_verify_handlers(ch, /* reporterror */ true);
702704
/* TODO Check callback returns an int or something castable to int */
703-
if (0 != php_curl_get_long(&retval)) {
704-
rval = 1;
705+
if (0 == php_curl_get_long(&retval)) {
706+
rval = 0; // ok
705707
}
706708
}
707709

@@ -714,13 +716,13 @@ static int curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, cu
714716
static int curl_prereqfunction(void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port)
715717
{
716718
php_curl *ch = (php_curl *)clientp;
717-
int rval = CURL_PREREQFUNC_OK;
719+
int rval = CURL_PREREQFUNC_ABORT;
718720

719721
// when CURLOPT_PREREQFUNCTION is set to null, curl_prereqfunction still
720722
// gets called. Return CURL_PREREQFUNC_OK immediately in this case to avoid
721723
// zend_call_known_fcc() with an uninitialized FCC.
722-
if (!ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {
723-
return rval;
724+
if (UNEXPECTED(!ZEND_FCC_INITIALIZED(ch->handlers.prereq))) {
725+
return CURL_PREREQFUNC_OK;
724726
}
725727

726728
#if PHP_CURL_DEBUG
@@ -858,6 +860,8 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
858860
}
859861
// TODO Do type error if invalid type?
860862
zval_ptr_dtor(&retval);
863+
} else {
864+
length = CURL_READFUNC_ABORT;
861865
}
862866

863867
zval_ptr_dtor(&argv[0]);
@@ -952,6 +956,8 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
952956
// TODO: Check for valid int type for return value
953957
_php_curl_verify_handlers(ch, /* reporterror */ true);
954958
length = php_curl_get_long(&retval);
959+
} else {
960+
length = -1;
955961
}
956962
zval_ptr_dtor(&argv[0]);
957963
zval_ptr_dtor(&argv[1]);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
CURLOPT_HEADERFUNCTION aborts transfer when callback throws
3+
--EXTENSIONS--
4+
curl
5+
--SKIPIF--
6+
<?php
7+
if (!defined('CURLOPT_HEADERFUNCTION')) {
8+
die('skip CURLOPT_HEADERFUNCTION not available');
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
14+
include 'server.inc';
15+
$host = curl_cli_server_start();
16+
$ch = curl_init("{$host}/get.inc");
17+
18+
echo "Test: header function throws exception\n";
19+
curl_setopt($ch, CURLOPT_HEADERFUNCTION,
20+
function (): int {
21+
throw new Exception('header exception');
22+
}
23+
);
24+
25+
try {
26+
curl_exec($ch);
27+
} catch (Exception $e) {
28+
echo $e->getMessage(), "\n";
29+
}
30+
31+
var_dump(curl_errno($ch) === CURLE_WRITE_ERROR);
32+
33+
echo "Test: header function is null\n";
34+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
35+
curl_setopt($ch, CURLOPT_HEADERFUNCTION, null);
36+
curl_exec($ch);
37+
var_dump(curl_errno($ch) === CURLE_OK);
38+
39+
?>
40+
--EXPECTF--
41+
Test: header function throws exception
42+
header exception
43+
bool(true)
44+
Test: header function is null
45+
bool(true)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
CURLOPT_PREREQFUNCTION aborts transfer when callback throws
3+
--EXTENSIONS--
4+
curl
5+
--SKIPIF--
6+
<?php
7+
if (!defined('CURLOPT_PREREQFUNCTION')) {
8+
die('skip CURLOPT_PREREQFUNCTION not available');
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
14+
include 'server.inc';
15+
$host = curl_cli_server_start();
16+
$ch = curl_init("{$host}/get.inc");
17+
18+
curl_setopt($ch, CURLOPT_PREREQFUNCTION,
19+
function (): int {
20+
throw new Exception('prereq exception');
21+
}
22+
);
23+
24+
try {
25+
curl_exec($ch);
26+
} catch (Exception $e) {
27+
echo $e->getMessage(), "\n";
28+
}
29+
30+
var_dump(curl_errno($ch) === CURLE_ABORTED_BY_CALLBACK);
31+
32+
?>
33+
--EXPECTF--
34+
prereq exception
35+
bool(true)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
CURLOPT_PROGRESSFUNCTION aborts transfer when callback throws
3+
--EXTENSIONS--
4+
curl
5+
--SKIPIF--
6+
<?php
7+
if (!defined('CURLOPT_PROGRESSFUNCTION')) {
8+
die('skip CURLOPT_PROGRESSFUNCTION not available');
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
14+
include 'server.inc';
15+
$host = curl_cli_server_start();
16+
$ch = curl_init("{$host}/get.inc");
17+
18+
echo "Test: progress function throws exception\n";
19+
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
20+
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION,
21+
function (): int {
22+
throw new Exception('info exception');
23+
}
24+
);
25+
26+
try {
27+
curl_exec($ch);
28+
} catch (Exception $e) {
29+
echo $e->getMessage(), "\n";
30+
}
31+
32+
var_dump(curl_errno($ch) === CURLE_ABORTED_BY_CALLBACK);
33+
34+
echo "Test: progress function is null\n";
35+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
36+
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, null);
37+
curl_exec($ch);
38+
var_dump(curl_errno($ch) === CURLE_OK);
39+
40+
?>
41+
--EXPECTF--
42+
Test: progress function throws exception
43+
info exception
44+
bool(true)
45+
Test: progress function is null
46+
bool(true)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
--TEST--
2+
CURLOPT_READFUNCTION aborts transfer when callback throws
3+
--EXTENSIONS--
4+
curl
5+
--SKIPIF--
6+
<?php
7+
if (!defined('CURLOPT_READFUNCTION')) {
8+
die('skip CURLOPT_READFUNCTION not available');
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
14+
include 'server.inc';
15+
$host = curl_cli_server_start();
16+
$ch = curl_init("{$host}/get.inc");
17+
18+
$file = new CURLFile(__DIR__ . '/curl_testdata1.txt');
19+
curl_setopt($ch, CURLOPT_POST, 1);
20+
21+
echo "Test: read function throws exception\n";
22+
curl_setopt($ch, CURLOPT_READFUNCTION,
23+
function (): int {
24+
throw new Exception('read exception');
25+
}
26+
);
27+
28+
try {
29+
curl_exec($ch);
30+
} catch (Exception $e) {
31+
echo $e->getMessage(), "\n";
32+
}
33+
34+
var_dump(curl_errno($ch) === CURLE_ABORTED_BY_CALLBACK);
35+
36+
echo "Test: read function is null\n";
37+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
38+
curl_setopt($ch, CURLOPT_READFUNCTION, null);
39+
curl_exec($ch);
40+
var_dump(curl_errno($ch) === CURLE_OK);
41+
42+
?>
43+
--EXPECTF--
44+
Test: read function throws exception
45+
read exception
46+
bool(true)
47+
Test: read function is null
48+
bool(true)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
CURLOPT_WRITEFUNCTION aborts transfer when callback throws
3+
--EXTENSIONS--
4+
curl
5+
--SKIPIF--
6+
<?php
7+
if (!defined('CURLOPT_WRITEFUNCTION')) {
8+
die('skip CURLOPT_WRITEFUNCTION not available');
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
14+
include 'server.inc';
15+
$host = curl_cli_server_start();
16+
$ch = curl_init("{$host}/get.inc");
17+
18+
echo "Test: write function throws exception\n";
19+
curl_setopt($ch, CURLOPT_WRITEFUNCTION,
20+
function (): int {
21+
throw new Exception('write exception');
22+
}
23+
);
24+
25+
try {
26+
curl_exec($ch);
27+
} catch (Exception $e) {
28+
echo $e->getMessage(), "\n";
29+
}
30+
31+
var_dump(curl_errno($ch) === CURLE_WRITE_ERROR);
32+
33+
echo "Test: write function is null\n";
34+
curl_setopt($ch, CURLOPT_WRITEFUNCTION, null);
35+
curl_exec($ch);
36+
var_dump(curl_errno($ch) === CURLE_OK);
37+
38+
?>
39+
--EXPECTF--
40+
Test: write function throws exception
41+
write exception
42+
bool(true)
43+
Test: write function is null
44+
Hello World!
45+
Hello World!bool(true)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
CURLOPT_XFERINFOFUNCTION aborts transfer when callback throws
3+
--EXTENSIONS--
4+
curl
5+
--SKIPIF--
6+
<?php
7+
if (!defined('CURLOPT_XFERINFOFUNCTION')) {
8+
die('skip CURLOPT_XFERINFOFUNCTION not available');
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
14+
include 'server.inc';
15+
$host = curl_cli_server_start();
16+
$ch = curl_init("{$host}/get.inc");
17+
18+
echo "Test: xfer info function throws exception\n";
19+
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
20+
curl_setopt($ch, CURLOPT_XFERINFOFUNCTION,
21+
function (): int {
22+
throw new Exception('info exception');
23+
}
24+
);
25+
26+
try {
27+
curl_exec($ch);
28+
} catch (Exception $e) {
29+
echo $e->getMessage(), "\n";
30+
}
31+
32+
var_dump(curl_errno($ch) === CURLE_ABORTED_BY_CALLBACK);
33+
34+
echo "Test: xfer info function is null\n";
35+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
36+
curl_setopt($ch, CURLOPT_XFERINFOFUNCTION, null);
37+
curl_exec($ch);
38+
var_dump(curl_errno($ch) === CURLE_OK);
39+
40+
?>
41+
--EXPECTF--
42+
Test: xfer info function throws exception
43+
info exception
44+
bool(true)
45+
Test: xfer info function is null
46+
bool(true)

0 commit comments

Comments
 (0)