From a385cfa7ad7fe3621bb6095ff88bc8d74b358df0 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 29 Jun 2020 17:38:14 +0200 Subject: [PATCH] Fix #63208: BSTR to PHP string conversion not binary safe A `BSTR` is similar to a `zend_string`; it stores the length of the string just before the actual string, and thus the string may contain NUL bytes. However, `php_com_olestring_to_string()` is supposed to deal with arbitrary `OLECHAR*`s which may not be `BSTR`s, so we introduce `php_com_bstr_to_string()` and use it for the only case where we actually have to deal with `BSTR`s which may contain NUL bytes. Contrary to `php_com_olestring_to_string()` we return a `zend_string`, so we can save the re-allocation when converting to a `zval`. We also cater to `php_com_string_to_olestring()` not being binary safe, with basically the same fix we did for `php_com_olestring_to_string()`. --- NEWS | 3 ++ ext/com_dotnet/com_olechar.c | 55 ++++++++++++++++++++++++ ext/com_dotnet/com_variant.c | 17 ++------ ext/com_dotnet/php_com_dotnet_internal.h | 2 + ext/com_dotnet/tests/bug63208.phpt | 17 ++++++++ 5 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 ext/com_dotnet/tests/bug63208.phpt diff --git a/NEWS b/NEWS index 5ba6019106..2c8da20466 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ PHP NEWS . Fixed bug #79030 (Upgrade apache2handler's php_apache_sapi_get_request_time to return usec). (Herbert256) +- COM: + . Fixed bug #63208 (BSTR to PHP string conversion not binary safe). (cmb) + - Curl: . Fixed bug #79741 (curl_setopt CURLOPT_POSTFIELDS asserts on object with declared properties). (Nikita) diff --git a/ext/com_dotnet/com_olechar.c b/ext/com_dotnet/com_olechar.c index b9a332e4f5..1cd1017a02 100644 --- a/ext/com_dotnet/com_olechar.c +++ b/ext/com_dotnet/com_olechar.c @@ -103,3 +103,58 @@ PHP_COM_DOTNET_API char *php_com_olestring_to_string(OLECHAR *olestring, size_t return string; } + +BSTR php_com_string_to_bstr(zend_string *string, int codepage) +{ + BSTR bstr = NULL; + DWORD flags = codepage == CP_UTF8 ? 0 : MB_PRECOMPOSED | MB_ERR_INVALID_CHARS; + size_t mb_len = ZSTR_LEN(string); + int wc_len; + + if ((wc_len = MultiByteToWideChar(codepage, flags, ZSTR_VAL(string), (int)mb_len + 1, NULL, 0)) <= 0) { + goto fail; + } + if ((bstr = SysAllocStringLen(NULL, (UINT)(wc_len - 1))) == NULL) { + goto fail; + } + if ((wc_len = MultiByteToWideChar(codepage, flags, ZSTR_VAL(string), (int)mb_len + 1, bstr, wc_len)) <= 0) { + goto fail; + } + return bstr; + +fail: + char *msg = php_win32_error_to_msg(GetLastError()); + php_error_docref(NULL, E_WARNING, + "Could not convert string to unicode: `%s'", msg); + LocalFree(msg); + SysFreeString(bstr); + return SysAllocString(L""); +} + +zend_string *php_com_bstr_to_string(BSTR bstr, int codepage) +{ + zend_string *string = NULL; + UINT wc_len = SysStringLen(bstr); + int mb_len; + + mb_len = WideCharToMultiByte(codepage, 0, bstr, wc_len + 1, NULL, 0, NULL, NULL); + if (mb_len > 0) { + string = zend_string_alloc(mb_len - 1, 0); + mb_len = WideCharToMultiByte(codepage, 0, bstr, wc_len + 1, ZSTR_VAL(string), mb_len, NULL, NULL); + } + + if (mb_len <= 0) { + char *msg = php_win32_error_to_msg(GetLastError()); + + php_error_docref(NULL, E_WARNING, + "Could not convert string from unicode: `%s'", msg); + LocalFree(msg); + + if (string != NULL) { + zend_string_release(string); + } + string = ZSTR_EMPTY_ALLOC(); + } + + return string; +} diff --git a/ext/com_dotnet/com_variant.c b/ext/com_dotnet/com_variant.c index f4f7d5a9dd..901b0a866a 100644 --- a/ext/com_dotnet/com_variant.c +++ b/ext/com_dotnet/com_variant.c @@ -96,7 +96,6 @@ bogus: PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage) { - OLECHAR *olestring; php_com_dotnet_object *obj; zend_uchar ztype = IS_NULL; @@ -164,13 +163,7 @@ PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codep case IS_STRING: V_VT(v) = VT_BSTR; - olestring = php_com_string_to_olestring(Z_STRVAL_P(z), Z_STRLEN_P(z), codepage); - if (CP_UTF8 == codepage) { - V_BSTR(v) = SysAllocStringByteLen((char*)olestring, (UINT)(wcslen(olestring) * sizeof(OLECHAR))); - } else { - V_BSTR(v) = SysAllocStringByteLen((char*)olestring, (UINT)(Z_STRLEN_P(z) * sizeof(OLECHAR))); - } - efree(olestring); + V_BSTR(v) = php_com_string_to_bstr(Z_STR_P(z), codepage); break; case IS_RESOURCE: @@ -236,12 +229,8 @@ PHP_COM_DOTNET_API int php_com_zval_from_variant(zval *z, VARIANT *v, int codepa case VT_BSTR: olestring = V_BSTR(v); if (olestring) { - size_t len; - char *str = php_com_olestring_to_string(olestring, - &len, codepage); - ZVAL_STRINGL(z, str, len); - // TODO: avoid reallocation??? - efree(str); + zend_string *str = php_com_bstr_to_string(olestring, codepage); + ZVAL_STR(z, str); olestring = NULL; } break; diff --git a/ext/com_dotnet/php_com_dotnet_internal.h b/ext/com_dotnet/php_com_dotnet_internal.h index a2fe813683..663c0694fe 100644 --- a/ext/com_dotnet/php_com_dotnet_internal.h +++ b/ext/com_dotnet/php_com_dotnet_internal.h @@ -89,6 +89,8 @@ PHP_COM_DOTNET_API char *php_com_olestring_to_string(OLECHAR *olestring, size_t *string_len, int codepage); PHP_COM_DOTNET_API OLECHAR *php_com_string_to_olestring(char *string, size_t string_len, int codepage); +BSTR php_com_string_to_bstr(zend_string *string, int codepage); +zend_string *php_com_bstr_to_string(BSTR bstr, int codepage); /* com_com.c */ diff --git a/ext/com_dotnet/tests/bug63208.phpt b/ext/com_dotnet/tests/bug63208.phpt new file mode 100644 index 0000000000..ae62dbba98 --- /dev/null +++ b/ext/com_dotnet/tests/bug63208.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #63208 (BSTR to PHP string conversion not binary safe) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(14) "e0a48562006364" +string(14) "e0a48562006364" -- 2.49.0