diff options
author | Christoph M. Becker <cmbecker69@gmx.de> | 2020-06-29 17:38:14 +0200 |
---|---|---|
committer | Christoph M. Becker <cmbecker69@gmx.de> | 2020-06-29 19:05:02 +0200 |
commit | a385cfa7ad7fe3621bb6095ff88bc8d74b358df0 (patch) | |
tree | 75babdefc6eb3793530945a6a4aa43c6c36666ba /ext/com_dotnet | |
parent | 816b4c1235d70b1b83d26c415f044fc04a48875f (diff) | |
download | php-git-a385cfa7ad7fe3621bb6095ff88bc8d74b358df0.tar.gz |
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()`.
Diffstat (limited to 'ext/com_dotnet')
-rw-r--r-- | ext/com_dotnet/com_olechar.c | 55 | ||||
-rw-r--r-- | ext/com_dotnet/com_variant.c | 17 | ||||
-rw-r--r-- | ext/com_dotnet/php_com_dotnet_internal.h | 2 | ||||
-rw-r--r-- | ext/com_dotnet/tests/bug63208.phpt | 17 |
4 files changed, 77 insertions, 14 deletions
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-- +<?php +if (!extension_loaded('com_dotnet')) die('skip com_dotnet extension not available'); +?> +--FILE-- +<?php +$string = "\u{0905}b\0cd"; +$variant = new VARIANT($string, VT_ARRAY | VT_UI1, CP_UTF8); // Array of bytes +$converted = (string) $variant; +var_dump(bin2hex($string)); +var_dump(bin2hex($converted)); +?> +--EXPECT-- +string(14) "e0a48562006364" +string(14) "e0a48562006364" |