From 32d6e08972184538a1f8dac1d68ff991d6ca60fc Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Fri, 10 Jul 2026 06:59:18 +0000 Subject: [PATCH] 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. This was extracted from https://github.com/php/php-src/pull/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. --- ext/curl/interface.c | 76 ++++++++++--------- .../tests/curl_setopt_error_nul_byte.phpt | 52 +++++++++++++ 2 files changed, 94 insertions(+), 34 deletions(-) create mode 100644 ext/curl/tests/curl_setopt_error_nul_byte.phpt diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 1dff14b7c8c2..09c7009068b6 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2030,42 +2030,42 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue HashTable *ph; zend_string *val, *tmp_val; struct curl_slist *slist = NULL; + const char *name = NULL; - if (Z_TYPE_P(zvalue) != IS_ARRAY) { - const char *name = NULL; - switch (option) { - case CURLOPT_HTTPHEADER: - name = "CURLOPT_HTTPHEADER"; - break; - case CURLOPT_QUOTE: - name = "CURLOPT_QUOTE"; - break; - case CURLOPT_HTTP200ALIASES: - name = "CURLOPT_HTTP200ALIASES"; - break; - case CURLOPT_POSTQUOTE: - name = "CURLOPT_POSTQUOTE"; - break; - case CURLOPT_PREQUOTE: - name = "CURLOPT_PREQUOTE"; - break; - case CURLOPT_TELNETOPTIONS: - name = "CURLOPT_TELNETOPTIONS"; - break; - case CURLOPT_MAIL_RCPT: - name = "CURLOPT_MAIL_RCPT"; - break; - case CURLOPT_RESOLVE: - name = "CURLOPT_RESOLVE"; - break; - case CURLOPT_PROXYHEADER: - name = "CURLOPT_PROXYHEADER"; - break; - case CURLOPT_CONNECT_TO: - name = "CURLOPT_CONNECT_TO"; - break; - } + switch (option) { + case CURLOPT_HTTPHEADER: + name = "CURLOPT_HTTPHEADER"; + break; + case CURLOPT_QUOTE: + name = "CURLOPT_QUOTE"; + break; + case CURLOPT_HTTP200ALIASES: + name = "CURLOPT_HTTP200ALIASES"; + break; + case CURLOPT_POSTQUOTE: + name = "CURLOPT_POSTQUOTE"; + break; + case CURLOPT_PREQUOTE: + name = "CURLOPT_PREQUOTE"; + break; + case CURLOPT_TELNETOPTIONS: + name = "CURLOPT_TELNETOPTIONS"; + break; + case CURLOPT_MAIL_RCPT: + name = "CURLOPT_MAIL_RCPT"; + break; + case CURLOPT_RESOLVE: + name = "CURLOPT_RESOLVE"; + break; + case CURLOPT_PROXYHEADER: + name = "CURLOPT_PROXYHEADER"; + break; + case CURLOPT_CONNECT_TO: + name = "CURLOPT_CONNECT_TO"; + break; + } + if (Z_TYPE_P(zvalue) != IS_ARRAY) { zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name); return FAILURE; } @@ -2074,6 +2074,14 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue ZEND_HASH_FOREACH_VAL(ph, current) { ZVAL_DEREF(current); val = zval_get_tmp_string(current, &tmp_val); + + if (zend_str_has_nul_byte(val)) { + curl_slist_free_all(slist); + zend_tmp_string_release(tmp_val); + zend_value_error("%s(): cURL option %s must not contain any null bytes", get_active_function_name(), name); + return FAILURE; + } + struct curl_slist *new_slist = curl_slist_append(slist, ZSTR_VAL(val)); zend_tmp_string_release(tmp_val); if (!new_slist) { diff --git a/ext/curl/tests/curl_setopt_error_nul_byte.phpt b/ext/curl/tests/curl_setopt_error_nul_byte.phpt new file mode 100644 index 000000000000..23be973b82c2 --- /dev/null +++ b/ext/curl/tests/curl_setopt_error_nul_byte.phpt @@ -0,0 +1,52 @@ +--TEST-- +curl_setopt() throws ValueError for NUL bytes in lists +--EXTENSIONS-- +curl +--FILE-- +getMessage() . "\n\n"; + } +} + +$ch = null; +?> +--EXPECT-- +CURLOPT_HTTP200ALIASES: curl_setopt(): cURL option CURLOPT_HTTP200ALIASES must not contain any null bytes + +CURLOPT_HTTPHEADER: curl_setopt(): cURL option CURLOPT_HTTPHEADER must not contain any null bytes + +CURLOPT_POSTQUOTE: curl_setopt(): cURL option CURLOPT_POSTQUOTE must not contain any null bytes + +CURLOPT_PREQUOTE: curl_setopt(): cURL option CURLOPT_PREQUOTE must not contain any null bytes + +CURLOPT_QUOTE: curl_setopt(): cURL option CURLOPT_QUOTE must not contain any null bytes + +CURLOPT_TELNETOPTIONS: curl_setopt(): cURL option CURLOPT_TELNETOPTIONS must not contain any null bytes + +CURLOPT_MAIL_RCPT: curl_setopt(): cURL option CURLOPT_MAIL_RCPT must not contain any null bytes + +CURLOPT_RESOLVE: curl_setopt(): cURL option CURLOPT_RESOLVE must not contain any null bytes + +CURLOPT_PROXYHEADER: curl_setopt(): cURL option CURLOPT_PROXYHEADER must not contain any null bytes + +CURLOPT_CONNECT_TO: curl_setopt(): cURL option CURLOPT_CONNECT_TO must not contain any null bytes