Skip to content

Commit 2251e12

Browse files
committed
Throw an error on null byte in curl array options
This is already done for string options in php_curl_option_str (see bug #68089). Similarly, array arguments should also not contain null bytes, so we throw the same error for these. The error message is consistent with the one for strings. This was extracted from php#22651, as this seems less controversial. Using null bytes is clearly not supported and curl just throws away everything after the null byte, so https://wiki.php.net/rfc/policy-exempt-type-value-error-bc-policy seems to apply here.
1 parent 201e3cc commit 2251e12

2 files changed

Lines changed: 94 additions & 34 deletions

File tree

ext/curl/interface.c

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,42 +2030,42 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
20302030
HashTable *ph;
20312031
zend_string *val, *tmp_val;
20322032
struct curl_slist *slist = NULL;
2033+
const char *name = NULL;
20332034

2034-
if (Z_TYPE_P(zvalue) != IS_ARRAY) {
2035-
const char *name = NULL;
2036-
switch (option) {
2037-
case CURLOPT_HTTPHEADER:
2038-
name = "CURLOPT_HTTPHEADER";
2039-
break;
2040-
case CURLOPT_QUOTE:
2041-
name = "CURLOPT_QUOTE";
2042-
break;
2043-
case CURLOPT_HTTP200ALIASES:
2044-
name = "CURLOPT_HTTP200ALIASES";
2045-
break;
2046-
case CURLOPT_POSTQUOTE:
2047-
name = "CURLOPT_POSTQUOTE";
2048-
break;
2049-
case CURLOPT_PREQUOTE:
2050-
name = "CURLOPT_PREQUOTE";
2051-
break;
2052-
case CURLOPT_TELNETOPTIONS:
2053-
name = "CURLOPT_TELNETOPTIONS";
2054-
break;
2055-
case CURLOPT_MAIL_RCPT:
2056-
name = "CURLOPT_MAIL_RCPT";
2057-
break;
2058-
case CURLOPT_RESOLVE:
2059-
name = "CURLOPT_RESOLVE";
2060-
break;
2061-
case CURLOPT_PROXYHEADER:
2062-
name = "CURLOPT_PROXYHEADER";
2063-
break;
2064-
case CURLOPT_CONNECT_TO:
2065-
name = "CURLOPT_CONNECT_TO";
2066-
break;
2067-
}
2035+
switch (option) {
2036+
case CURLOPT_HTTPHEADER:
2037+
name = "CURLOPT_HTTPHEADER";
2038+
break;
2039+
case CURLOPT_QUOTE:
2040+
name = "CURLOPT_QUOTE";
2041+
break;
2042+
case CURLOPT_HTTP200ALIASES:
2043+
name = "CURLOPT_HTTP200ALIASES";
2044+
break;
2045+
case CURLOPT_POSTQUOTE:
2046+
name = "CURLOPT_POSTQUOTE";
2047+
break;
2048+
case CURLOPT_PREQUOTE:
2049+
name = "CURLOPT_PREQUOTE";
2050+
break;
2051+
case CURLOPT_TELNETOPTIONS:
2052+
name = "CURLOPT_TELNETOPTIONS";
2053+
break;
2054+
case CURLOPT_MAIL_RCPT:
2055+
name = "CURLOPT_MAIL_RCPT";
2056+
break;
2057+
case CURLOPT_RESOLVE:
2058+
name = "CURLOPT_RESOLVE";
2059+
break;
2060+
case CURLOPT_PROXYHEADER:
2061+
name = "CURLOPT_PROXYHEADER";
2062+
break;
2063+
case CURLOPT_CONNECT_TO:
2064+
name = "CURLOPT_CONNECT_TO";
2065+
break;
2066+
}
20682067

2068+
if (Z_TYPE_P(zvalue) != IS_ARRAY) {
20692069
zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name);
20702070
return FAILURE;
20712071
}
@@ -2074,6 +2074,14 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
20742074
ZEND_HASH_FOREACH_VAL(ph, current) {
20752075
ZVAL_DEREF(current);
20762076
val = zval_get_tmp_string(current, &tmp_val);
2077+
2078+
if (zend_str_has_nul_byte(val)) {
2079+
curl_slist_free_all(slist);
2080+
zend_tmp_string_release(tmp_val);
2081+
zend_value_error("%s(): cURL option %s must not contain any null bytes", get_active_function_name(), name);
2082+
return FAILURE;
2083+
}
2084+
20772085
struct curl_slist *new_slist = curl_slist_append(slist, ZSTR_VAL(val));
20782086
zend_tmp_string_release(tmp_val);
20792087
if (!new_slist) {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
--TEST--
2+
curl_setopt() throws ValueError for NUL bytes in lists
3+
--EXTENSIONS--
4+
curl
5+
--FILE--
6+
<?php
7+
8+
$ch = curl_init();
9+
10+
$list_options = [
11+
"CURLOPT_HTTP200ALIASES",
12+
"CURLOPT_HTTPHEADER",
13+
"CURLOPT_POSTQUOTE",
14+
"CURLOPT_PREQUOTE",
15+
"CURLOPT_QUOTE",
16+
"CURLOPT_TELNETOPTIONS",
17+
"CURLOPT_MAIL_RCPT",
18+
"CURLOPT_RESOLVE",
19+
"CURLOPT_PROXYHEADER",
20+
"CURLOPT_CONNECT_TO",
21+
];
22+
23+
foreach ($list_options as $option) {
24+
try {
25+
curl_setopt($ch, constant($option), ["Something: foo\0bar"]);
26+
} catch (ValueError $exception) {
27+
echo $option . ": " . $exception->getMessage() . "\n\n";
28+
}
29+
}
30+
31+
$ch = null;
32+
?>
33+
--EXPECT--
34+
CURLOPT_HTTP200ALIASES: curl_setopt(): cURL option CURLOPT_HTTP200ALIASES must not contain any null bytes
35+
36+
CURLOPT_HTTPHEADER: curl_setopt(): cURL option CURLOPT_HTTPHEADER must not contain any null bytes
37+
38+
CURLOPT_POSTQUOTE: curl_setopt(): cURL option CURLOPT_POSTQUOTE must not contain any null bytes
39+
40+
CURLOPT_PREQUOTE: curl_setopt(): cURL option CURLOPT_PREQUOTE must not contain any null bytes
41+
42+
CURLOPT_QUOTE: curl_setopt(): cURL option CURLOPT_QUOTE must not contain any null bytes
43+
44+
CURLOPT_TELNETOPTIONS: curl_setopt(): cURL option CURLOPT_TELNETOPTIONS must not contain any null bytes
45+
46+
CURLOPT_MAIL_RCPT: curl_setopt(): cURL option CURLOPT_MAIL_RCPT must not contain any null bytes
47+
48+
CURLOPT_RESOLVE: curl_setopt(): cURL option CURLOPT_RESOLVE must not contain any null bytes
49+
50+
CURLOPT_PROXYHEADER: curl_setopt(): cURL option CURLOPT_PROXYHEADER must not contain any null bytes
51+
52+
CURLOPT_CONNECT_TO: curl_setopt(): cURL option CURLOPT_CONNECT_TO must not contain any null bytes

0 commit comments

Comments
 (0)