]> granicus.if.org Git - php/commitdiff
Fix #63208: BSTR to PHP string conversion not binary safe
authorChristoph M. Becker <cmbecker69@gmx.de>
Mon, 29 Jun 2020 15:38:14 +0000 (17:38 +0200)
committerChristoph M. Becker <cmbecker69@gmx.de>
Mon, 29 Jun 2020 17:05:02 +0000 (19:05 +0200)
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
ext/com_dotnet/com_olechar.c
ext/com_dotnet/com_variant.c
ext/com_dotnet/php_com_dotnet_internal.h
ext/com_dotnet/tests/bug63208.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 5ba60191067f58b25f1ab09855fcf165582e5176..2c8da20466cdebd120a9635ae0ce1a8ec83320d1 100644 (file)
--- 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)
index b9a332e4f54421abd66ef9739289ead423c3e5bd..1cd1017a0244bf3d903aa70c7074421292e95fc9 100644 (file)
@@ -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;
+}
index f4f7d5a9dde3cd36b5f7afbae0caa8de1dd0c3e7..901b0a866a17f29aadb4ed1e2f18283b3e82ec88 100644 (file)
@@ -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;
index a2fe81368322c19edff92a146c183812b4cb4f5d..663c0694fe10a615e9f77089387169eea3205118 100644 (file)
@@ -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 (file)
index 0000000..ae62dbb
--- /dev/null
@@ -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"