From a31f46421d7bf6f55dd9ac5876b8e2eacf7e0708 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 26 Feb 2019 15:32:18 +0100 Subject: [PATCH] Allow exceptions in __toString() RFC: https://wiki.php.net/rfc/tostring_exceptions And convert some object to string conversion related recoverable fatal errors into Error exceptions. Improve exception safety of internal code performing string conversions. --- UPGRADING | 5 + UPGRADING.INTERNALS | 10 ++ Zend/tests/bug26166.phpt | 25 ++-- Zend/tests/bug28444.phpt | 19 ++- Zend/tests/bug30791.phpt | 25 ++-- Zend/tests/bug60909_2.phpt | 9 +- Zend/tests/bug70967.phpt | 6 +- Zend/tests/bug72162.phpt | 6 +- Zend/tests/call_with_refs.phpt | 17 +-- Zend/tests/class_properties_const.phpt | 5 +- Zend/tests/closure_015.phpt | 28 ++-- Zend/tests/exception_009.phpt | 6 +- Zend/tests/exception_from_toString.phpt | 135 ++++++++++++++++++ Zend/tests/instanceof_001.phpt | 4 - Zend/tests/unexpected_ref_bug.phpt | 15 +- Zend/zend_API.c | 19 ++- Zend/zend_API.h | 5 +- Zend/zend_builtin_functions.c | 4 + Zend/zend_execute.c | 9 ++ Zend/zend_object_handlers.c | 53 ++++--- Zend/zend_operators.c | 61 ++++++-- Zend/zend_operators.h | 30 ++++ Zend/zend_string.h | 1 + Zend/zend_vm_def.h | 13 +- Zend/zend_vm_execute.h | 39 ++++- ext/date/php_date.c | 12 ++ ext/dba/dba.c | 6 + ext/dom/attr.c | 7 +- ext/dom/characterdata.c | 3 + ext/dom/document.c | 17 ++- ext/dom/node.c | 25 +++- ext/dom/processinginstruction.c | 3 + ext/dom/tests/toString_exceptions.phpt | 56 ++++++++ ext/exif/exif.c | 8 +- ext/gd/gd.c | 5 +- ext/iconv/iconv.c | 13 +- ext/imap/php_imap.c | 12 +- .../dateformat/dateformat_format_object.cpp | 4 +- ext/intl/timezone/timezone_class.cpp | 5 +- ext/intl/timezone/timezone_methods.cpp | 4 +- .../transliterator/transliterator_class.c | 9 +- .../transliterator/transliterator_methods.c | 5 +- ext/libxml/libxml.c | 5 +- ext/mbstring/mbstring.c | 43 ++++-- ext/mbstring/php_mbregex.c | 4 +- ext/mysqli/mysqli.c | 6 + ext/mysqli/mysqli_api.c | 9 +- ext/mysqlnd/mysqlnd_ps_codec.c | 5 +- ext/oci8/oci8_statement.c | 43 ++++-- ext/odbc/php_odbc.c | 4 +- ext/openssl/openssl.c | 82 +++++++---- ext/openssl/tests/bug38261.phpt | 9 +- .../tests/openssl_pkcs7_decrypt_error.phpt | 10 +- ext/openssl/xp_ssl.c | 40 ++++-- ext/pcntl/pcntl.c | 21 ++- ext/pcre/php_pcre.c | 5 + ext/pcre/tests/preg_replace_error1.phpt | 9 +- ext/pcre/tests/preg_replace_error2.phpt | 10 +- ext/pdo/pdo_sql_parser.re | 3 +- ext/pdo/pdo_stmt.c | 25 +++- ext/pdo/php_pdo_driver.h | 2 +- ext/pdo_firebird/firebird_driver.c | 9 ++ ext/pdo_firebird/firebird_statement.c | 4 +- ext/pdo_oci/oci_driver.c | 12 ++ ext/pdo_oci/oci_statement.c | 7 +- ext/pdo_pgsql/pgsql_driver.c | 5 +- ext/pdo_sqlite/sqlite_driver.c | 5 +- ext/pdo_sqlite/sqlite_statement.c | 8 +- .../tests/pdo_sqlite_tostring_exception.phpt | 45 ++++++ ext/pgsql/pgsql.c | 64 ++++----- ext/readline/readline.c | 16 ++- ext/reflection/php_reflection.c | 73 ++++++---- ext/reflection/tests/bug74673.phpt | 7 +- ext/session/session.c | 48 ++++--- ext/simplexml/simplexml.c | 19 ++- ext/snmp/snmp.c | 6 + ext/sockets/conversions.c | 5 +- ext/spl/spl_iterators.c | 35 +++-- ext/spl/tests/iterator_036.phpt | 7 +- .../tests/recursive_tree_iterator_007.phpt | 6 +- ext/sqlite3/sqlite3.c | 27 ++-- .../tests/exception_from_toString.phpt | 39 +++++ ext/standard/array.c | 9 +- ext/standard/assert.c | 22 ++- ext/standard/basic_functions.c | 5 +- ext/standard/filters.c | 11 +- ext/standard/formatted_print.c | 5 +- ext/standard/head.c | 20 +-- ext/standard/math.c | 5 +- ext/standard/pack.c | 7 +- ext/standard/password.c | 3 + ext/standard/proc_open.c | 16 ++- ext/standard/streamsfuncs.c | 4 +- ext/standard/string.c | 15 +- .../array/array_multisort_variation8.phpt | 6 +- .../get_class_methods_variation_001.phpt | 6 +- .../get_parent_class_variation_002.phpt | 5 +- .../tests/general_functions/type.phpt | 11 +- .../tests/math/base_convert_error.phpt | 9 +- ext/standard/tests/streams/bug61115.phpt | 8 +- ext/standard/tests/strings/strval_error.phpt | 10 +- ext/standard/var.c | 7 + ext/xml/xml.c | 5 +- ext/xmlreader/php_xmlreader.c | 9 ++ ext/xmlrpc/xmlrpc-epi-php.c | 13 +- ext/xsl/xsltprocessor.c | 24 +++- ext/zip/php_zip.c | 9 ++ ext/zlib/zlib.c | 6 +- main/streams/userspace.c | 5 +- tests/classes/tostring_001.phpt | 32 +++-- tests/classes/tostring_003.phpt | 5 +- tests/classes/tostring_004.phpt | 37 +++-- win32/codepage.c | 8 +- 113 files changed, 1389 insertions(+), 473 deletions(-) create mode 100644 Zend/tests/exception_from_toString.phpt create mode 100644 ext/dom/tests/toString_exceptions.phpt create mode 100644 ext/pdo_sqlite/tests/pdo_sqlite_tostring_exception.phpt create mode 100644 ext/sqlite3/tests/exception_from_toString.phpt diff --git a/UPGRADING b/UPGRADING index 2d63688969..49e4edf40e 100644 --- a/UPGRADING +++ b/UPGRADING @@ -187,6 +187,11 @@ PHP 7.4 UPGRADE NOTES . Support for WeakReferences has been added. RFC: https://wiki.php.net/rfc/weakrefs + . Throwing exceptions from __toString() is now permitted. Previously this + resulted in a fatal error. Existing recoverable fatals in string conversions + have been converted to Error exceptions. + RFC: https://wiki.php.net/rfc/tostring_exceptions + - CURL: . CURLFile now supports stream wrappers in addition to plain file names, if the extension has been built against libcurl >= 7.56.0. The streams may diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 6cd3463ef2..b53745b0f0 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -19,6 +19,8 @@ PHP 7.4 INTERNALS UPGRADE NOTES p. ZEND_EXT_FCALL_BEGIN can access arguments q. ZEND_COMPILE_IGNORE_USER_FUNCTIONS and ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS r. TSRM environment locking + s. Typed references support + t. Exceptions thrown by string conversions. 2. Build system changes a. Abstract @@ -198,6 +200,14 @@ PHP 7.4 INTERNALS UPGRADE NOTES behave as if they are safe, care should still be taken in multi-threaded environments. + s. Correct support for typed properties requires the use of new macros to + assign values to references. For more information see + https://wiki.php.net/rfc/typed_properties_v2#impact_on_extensions. + + t. convert_to_string() and zval_get_string() are now more likely to result in + an exception. For instructions on how to gracefully handle this see + https://wiki.php.net/rfc/tostring_exceptions#extension_guidelines. + ======================== 2. Build system changes ======================== diff --git a/Zend/tests/bug26166.phpt b/Zend/tests/bug26166.phpt index a77989c7c2..7a3be86c3f 100644 --- a/Zend/tests/bug26166.phpt +++ b/Zend/tests/bug26166.phpt @@ -31,12 +31,6 @@ echo $o; echo "===NONE===\n"; -function my_error_handler($errno, $errstr, $errfile, $errline) { - var_dump($errstr); -} - -set_error_handler('my_error_handler'); - class NoneTest { function __toString() { @@ -44,7 +38,11 @@ class NoneTest } $o = new NoneTest; -echo $o; +try { + echo $o; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} echo "===THROW===\n"; @@ -58,17 +56,16 @@ class ErrorTest $o = new ErrorTest; try { echo $o; -} -catch (Exception $e) { - echo "Got the exception\n"; +} catch (Exception $e) { + echo $e->getMessage(), "\n"; } ?> ===DONE=== ---EXPECTF-- +--EXPECT-- Hello World! ===NONE=== -string(%d) "Method NoneTest::__toString() must return a string value" +Method NoneTest::__toString() must return a string value ===THROW=== - -Fatal error: Method ErrorTest::__toString() must not throw an exception, caught Exception: This is an error! in %sbug26166.php on line %d +This is an error! +===DONE=== diff --git a/Zend/tests/bug28444.phpt b/Zend/tests/bug28444.phpt index b7e863da92..464aaef4c7 100644 --- a/Zend/tests/bug28444.phpt +++ b/Zend/tests/bug28444.phpt @@ -3,12 +3,6 @@ Bug #28444 (Cannot access undefined property for object with overloaded property --FILE-- x = $x; } + + function __toString() { + return "Object"; + } } class Overloaded @@ -55,8 +53,8 @@ var_dump($y->z->x = 6); ?> ===DONE=== ---EXPECTF-- -object(ObjectOne)#%d (1) { +--EXPECT-- +object(ObjectOne)#2 (1) { ["x"]=> int(2) } @@ -66,9 +64,8 @@ Overloaded::__set(y,3) int(3) Overloaded::__get(y) int(3) -string(58) "Object of class ObjectOne could not be converted to string" -Overloaded::__set(z,) -object(ObjectOne)#%d (1) { +Overloaded::__set(z,Object) +object(ObjectOne)#3 (1) { ["x"]=> int(4) } diff --git a/Zend/tests/bug30791.phpt b/Zend/tests/bug30791.phpt index e9991f3ad5..1b3b2ffe24 100644 --- a/Zend/tests/bug30791.phpt +++ b/Zend/tests/bug30791.phpt @@ -3,32 +3,25 @@ Bug #30791 (magic methods (__sleep/__wakeup/__toString) call __call if object is --FILE-- --EXPECT-- -string(50) "Object of class a could not be converted to string" - -string(50) "Object of class a could not be converted to string" - +object(a)#1 (1) { + ["a"]=> + int(4) +} object(a)#2 (1) { ["a"]=> int(4) diff --git a/Zend/tests/bug60909_2.phpt b/Zend/tests/bug60909_2.phpt index 1808b1c2fe..9660a9653f 100644 --- a/Zend/tests/bug60909_2.phpt +++ b/Zend/tests/bug60909_2.phpt @@ -3,18 +3,21 @@ Bug #60909 (custom error handler throwing Exception + fatal error = no shutdown --FILE-- __toString() +#1 {main} + thrown in %s on line %d !!!shutdown!!! diff --git a/Zend/tests/bug70967.phpt b/Zend/tests/bug70967.phpt index 89fc80b3f7..d254ea8d89 100644 --- a/Zend/tests/bug70967.phpt +++ b/Zend/tests/bug70967.phpt @@ -11,4 +11,8 @@ class A { echo (new A); ?> --EXPECTF-- -Fatal error: Method A::__toString() must not throw an exception, caught Error: Call to undefined function undefined_function() in %sbug70967.php on line %d +Fatal error: Uncaught Error: Call to undefined function undefined_function() in %s:%d +Stack trace: +#0 %s(%d): A->__toString() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/bug72162.phpt b/Zend/tests/bug72162.phpt index 493342d838..44237038a1 100644 --- a/Zend/tests/bug72162.phpt +++ b/Zend/tests/bug72162.phpt @@ -7,4 +7,8 @@ $var11 = new StdClass(); $var16 = error_reporting($var11); ?> --EXPECTF-- -Recoverable fatal error: Object of class stdClass could not be converted to string in %sbug72162.php on line %d +Fatal error: Uncaught Error: Object of class stdClass could not be converted to string in %s:%d +Stack trace: +#0 %s(%d): error_reporting(Object(stdClass)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/call_with_refs.phpt b/Zend/tests/call_with_refs.phpt index f7b18365b4..0e01779a21 100644 --- a/Zend/tests/call_with_refs.phpt +++ b/Zend/tests/call_with_refs.phpt @@ -2,16 +2,17 @@ Check call to non-ref function with call-time refs --FILE-- --EXPECT-- -Object of class stdClass could not be converted to string OK! diff --git a/Zend/tests/class_properties_const.phpt b/Zend/tests/class_properties_const.phpt index 8f607bcfe2..6f5471d20a 100644 --- a/Zend/tests/class_properties_const.phpt +++ b/Zend/tests/class_properties_const.phpt @@ -22,4 +22,7 @@ NULL Notice: Undefined property: A::$1 in %sclass_properties_const.php on line %d NULL -Recoverable fatal error: Object of class Closure could not be converted to string in %sclass_properties_const.php on line %d +Fatal error: Uncaught Error: Object of class Closure could not be converted to string in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/closure_015.phpt b/Zend/tests/closure_015.phpt index 33c732d723..f6903ebdb1 100644 --- a/Zend/tests/closure_015.phpt +++ b/Zend/tests/closure_015.phpt @@ -2,18 +2,20 @@ Closure 015: converting to string/unicode --FILE-- ---EXPECTF-- -Error: Object of class Closure could not be converted to string at %sclosure_015.php(8) +try { + print (string) $x; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + print $x; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} -Error: Object of class Closure could not be converted to string at %sclosure_015.php(10) +?> +--EXPECT-- +Object of class Closure could not be converted to string +Object of class Closure could not be converted to string diff --git a/Zend/tests/exception_009.phpt b/Zend/tests/exception_009.phpt index 32b048c40b..340bd99eb0 100644 --- a/Zend/tests/exception_009.phpt +++ b/Zend/tests/exception_009.phpt @@ -25,4 +25,8 @@ throw new my_exception; ?> --EXPECT-- -Recoverable fatal error: Object of class stdClass could not be converted to string in Unknown on line 0 +Fatal error: Uncaught Error: Object of class stdClass could not be converted to string in [no active file]:0 +Stack trace: +#0 [internal function]: Exception->__toString() +#1 {main} + thrown in [no active file] on line 0 diff --git a/Zend/tests/exception_from_toString.phpt b/Zend/tests/exception_from_toString.phpt new file mode 100644 index 0000000000..ee6dde9187 --- /dev/null +++ b/Zend/tests/exception_from_toString.phpt @@ -0,0 +1,135 @@ +--TEST-- +Test exceptions thrown from __toString() in various contexts +--FILE-- +getMessage(), "\n"; } +try { $x = $badStr . $str; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +try { $x = $str .= $badStr; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +var_dump($str); +try { $x = $num . $badStr; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +try { $x = $badStr . $num; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +try { $x = $num .= $badStr; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +var_dump($num); + +try { $x = $badStr .= $str; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +var_dump($badStr); +try { $x = $badStr .= $badStr; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +var_dump($badStr); + +try { $x = "x$badStr"; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +try { $x = "{$badStr}x"; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +try { $x = "$str$badStr"; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +try { $x = "$badStr$str"; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } + +try { $x = "x$badStr$str"; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +try { $x = "x$str$badStr"; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +try { $x = "{$str}x$badStr"; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +try { $x = "{$badStr}x$str"; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } + +try { $x = (string) $badStr; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } + +try { $x = include $badStr; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } + +try { echo $badStr; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } + +${""} = 42; +try { unset(${$badStr}); } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +var_dump(${""}); + +unset(${""}); +try { $x = ${$badStr}; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } + +try { $x = isset(${$badStr}); } +catch (Exception $e) { echo $e->getMessage(), "\n"; } + +$obj = new stdClass; +try { $x = $obj->{$badStr} = $str; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +var_dump($obj); + +try { $str[0] = $badStr; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +var_dump($str); + +$obj = new DateInterval('P1D'); +try { $x = $obj->{$badStr} = $str; } +catch (Exception $e) { echo $e->getMessage(), "\n"; } +var_dump(!isset($obj->{""})); + +try { strlen($badStr); } catch (Exception $e) { echo "Exception\n"; } +try { substr($badStr, 0); } catch (Exception $e) { echo "Exception\n"; } +try { new ArrayObject([], 0, $badStr); } catch (Exception $e) { echo "Exception\n"; } + +?> +--EXPECT-- +Exception +Exception +Exception +string(1) "a" +Exception +Exception +Exception +int(42) +Exception +object(BadStr)#1 (0) { +} +Exception +object(BadStr)#1 (0) { +} +Exception +Exception +Exception +Exception +Exception +Exception +Exception +Exception +Exception +Exception +Exception +Exception +int(42) +Exception +Exception +Exception +object(stdClass)#2 (0) { +} +Exception +string(1) "a" +Exception +bool(true) +Exception +Exception +Exception diff --git a/Zend/tests/instanceof_001.phpt b/Zend/tests/instanceof_001.phpt index 8c13a0478c..6bdcb896af 100644 --- a/Zend/tests/instanceof_001.phpt +++ b/Zend/tests/instanceof_001.phpt @@ -16,8 +16,6 @@ var_dump($c[0] instanceof stdClass); var_dump(@$inexistent instanceof stdClass); -var_dump("$a" instanceof stdClass); - ?> --EXPECTF-- bool(true) @@ -27,5 +25,3 @@ Deprecated: Function create_function() is deprecated in %s on line %d bool(true) bool(true) bool(false) - -Recoverable fatal error: Object of class stdClass could not be converted to string in %s on line %d diff --git a/Zend/tests/unexpected_ref_bug.phpt b/Zend/tests/unexpected_ref_bug.phpt index ba61ee582a..c7f66f05a0 100644 --- a/Zend/tests/unexpected_ref_bug.phpt +++ b/Zend/tests/unexpected_ref_bug.phpt @@ -2,16 +2,17 @@ Crash when function parameter modified via unexpected reference --FILE-- --EXPECT-- diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 10b47a1919..f88fc2caf8 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -334,7 +334,11 @@ ZEND_API int ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pc *pce = NULL; return 1; } - convert_to_string_ex(arg); + if (!try_convert_to_string(arg)) { + *pce = NULL; + return 0; + } + *pce = zend_lookup_class(Z_STR_P(arg)); if (ce_base) { if ((!*pce || !instanceof_function(*pce, ce_base))) { @@ -731,7 +735,11 @@ static const char *zend_parse_arg_impl(int arg_num, zval *arg, va_list *va, cons *pce = NULL; break; } - convert_to_string_ex(arg); + if (!try_convert_to_string(arg)) { + *pce = NULL; + return "valid class name"; + } + if ((lookup = zend_lookup_class(Z_STR_P(arg))) == NULL) { *pce = NULL; } else { @@ -817,6 +825,9 @@ static int zend_parse_arg(int arg_num, zval *arg, va_list *va, const char **spec expected_type = zend_parse_arg_impl(arg_num, arg, va, spec, &error, &severity); if (expected_type) { + if (EG(exception)) { + return FAILURE; + } if (!(flags & ZEND_PARSE_PARAMS_QUIET) && (*expected_type || error)) { const char *space; const char *class_name = get_active_class_name(&space); @@ -3218,7 +3229,7 @@ try_again: } if (obj == NULL || method == NULL || Z_TYPE_P(method) != IS_STRING) { - return zend_string_init("Array", sizeof("Array")-1, 0); + return ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED); } if (Z_TYPE_P(obj) == IS_STRING) { @@ -3226,7 +3237,7 @@ try_again: } else if (Z_TYPE_P(obj) == IS_OBJECT) { return zend_create_method_string(Z_OBJCE_P(obj)->name, Z_STR_P(method)); } else { - return zend_string_init("Array", sizeof("Array")-1, 0); + return ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED); } } case IS_OBJECT: diff --git a/Zend/zend_API.h b/Zend/zend_API.h index e8fa6e1e62..4732173654 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1136,7 +1136,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_exception(int num, cha int _min_num_args = (min_num_args); \ int _max_num_args = (max_num_args); \ int _num_args = EX_NUM_ARGS(); \ - int _i; \ + int _i = 0; \ zval *_real_arg, *_arg = NULL; \ zend_expected_type _expected_type = Z_EXPECTED_LONG; \ char *_error = NULL; \ @@ -1165,7 +1165,6 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_exception(int num, cha _error_code = ZPP_ERROR_FAILURE; \ break; \ } \ - _i = 0; \ _real_arg = ZEND_CALL_ARG(execute_data, 0); #define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args) \ @@ -1181,7 +1180,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_exception(int num, cha #define ZEND_PARSE_PARAMETERS_END_EX(failure) \ } while (0); \ if (UNEXPECTED(_error_code != ZPP_ERROR_OK)) { \ - if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \ + if (!(_flags & ZEND_PARSE_PARAMS_QUIET) && !EG(exception)) { \ if (_error_code == ZPP_ERROR_WRONG_CALLBACK) { \ if (_flags & ZEND_PARSE_PARAMS_THROW) { \ zend_wrong_callback_exception(_i, _error); \ diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 5333e3e315..160fb8a739 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -737,6 +737,10 @@ ZEND_FUNCTION(error_reporting) old_error_reporting = EG(error_reporting); if (ZEND_NUM_ARGS() != 0) { zend_string *new_val = zval_get_string(err); + if (UNEXPECTED(EG(exception))) { + return; + } + do { zend_ini_entry *p = EG(error_reporting_ini_entry); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 96d1f614e3..8adb6ef3b9 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1549,6 +1549,12 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, if (Z_TYPE_P(value) != IS_STRING) { /* Convert to string, just the time to pick the 1st byte */ zend_string *tmp = zval_get_string_func(value); + if (UNEXPECTED(EG(exception))) { + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return; + } string_len = ZSTR_LEN(tmp); c = (zend_uchar)ZSTR_VAL(tmp)[0]; @@ -4024,6 +4030,9 @@ static zend_never_inline zend_op_array* ZEND_FASTCALL zend_include_or_eval(zval if (Z_TYPE_P(inc_filename) != IS_STRING) { ZVAL_STR(&tmp_inc_filename, zval_get_string_func(inc_filename)); inc_filename = &tmp_inc_filename; + if (UNEXPECTED(EG(exception))) { + return NULL; + } } switch (type) { diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 41042941c7..10f5ac1524 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -657,7 +657,10 @@ ZEND_API zval *zend_std_read_property(zval *object, zval *member, int type, void uint32_t *guard = NULL; zobj = Z_OBJ_P(object); - name = zval_get_tmp_string(member, &tmp_name); + name = zval_try_get_tmp_string(member, &tmp_name); + if (UNEXPECTED(!name)) { + return &EG(uninitialized_zval); + } #if DEBUG_OBJECT_HANDLERS fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), ZSTR_VAL(name)); @@ -802,7 +805,10 @@ ZEND_API zval *zend_std_write_property(zval *object, zval *member, zval *value, ZEND_ASSERT(!Z_ISREF_P(value)); zobj = Z_OBJ_P(object); - name = zval_get_tmp_string(member, &tmp_name); + name = zval_try_get_tmp_string(member, &tmp_name); + if (UNEXPECTED(!name)) { + return value; + } property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__set != NULL), cache_slot, &prop_info); @@ -1011,7 +1017,10 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zval *object, zval *member, int typ zend_property_info *prop_info = NULL; zobj = Z_OBJ_P(object); - name = zval_get_tmp_string(member, &tmp_name); + name = zval_try_get_tmp_string(member, &tmp_name); + if (UNEXPECTED(!name)) { + return NULL; + } #if DEBUG_OBJECT_HANDLERS fprintf(stderr, "Ptr object #%d property: %s\n", Z_OBJ_HANDLE_P(object), ZSTR_VAL(name)); @@ -1073,7 +1082,10 @@ ZEND_API void zend_std_unset_property(zval *object, zval *member, void **cache_s zend_property_info *prop_info = NULL; zobj = Z_OBJ_P(object); - name = zval_get_tmp_string(member, &tmp_name); + name = zval_try_get_tmp_string(member, &tmp_name); + if (UNEXPECTED(!name)) { + return; + } property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__unset != NULL), cache_slot, &prop_info); @@ -1640,7 +1652,10 @@ ZEND_API int zend_std_has_property(zval *object, zval *member, int has_set_exist zend_property_info *prop_info = NULL; zobj = Z_OBJ_P(object); - name = zval_get_tmp_string(member, &tmp_name); + name = zval_try_get_tmp_string(member, &tmp_name); + if (UNEXPECTED(!name)) { + return 0; + } property_offset = zend_get_property_offset(zobj->ce, name, 1, cache_slot, &prop_info); @@ -1745,31 +1760,15 @@ ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int ty switch (type) { case IS_STRING: ce = Z_OBJCE_P(readobj); - if (ce->__tostring && - (zend_call_method_with_0_params(readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) { - if (UNEXPECTED(EG(exception) != NULL)) { - zval *msg, ex, rv; - zval_ptr_dtor(&retval); - ZVAL_OBJ(&ex, EG(exception)); - EG(exception) = NULL; - msg = zend_read_property(Z_OBJCE(ex), &ex, "message", sizeof("message") - 1, 1, &rv); - if (UNEXPECTED(Z_TYPE_P(msg) != IS_STRING)) { - ZVAL_EMPTY_STRING(&rv); - msg = &rv; - } - zend_error_noreturn(E_ERROR, - "Method %s::__toString() must not throw an exception, caught %s: %s", - ZSTR_VAL(ce->name), ZSTR_VAL(Z_OBJCE(ex)->name), Z_STRVAL_P(msg)); - return FAILURE; - } + if (ce->__tostring) { + zend_call_method_with_0_params(readobj, ce, &ce->__tostring, "__tostring", &retval); if (EXPECTED(Z_TYPE(retval) == IS_STRING)) { ZVAL_COPY_VALUE(writeobj, &retval); return SUCCESS; - } else { - zval_ptr_dtor(&retval); - ZVAL_EMPTY_STRING(writeobj); - zend_error(E_RECOVERABLE_ERROR, "Method %s::__toString() must return a string value", ZSTR_VAL(ce->name)); - return SUCCESS; + } + zval_ptr_dtor(&retval); + if (!EG(exception)) { + zend_throw_error(NULL, "Method %s::__toString() must return a string value", ZSTR_VAL(ce->name)); } } return FAILURE; diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 7b25eb1d52..896f6dcbe1 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -568,19 +568,33 @@ try_again: case IS_ARRAY: zend_error(E_NOTICE, "Array to string conversion"); zval_ptr_dtor(op); - ZVAL_NEW_STR(op, zend_string_init("Array", sizeof("Array")-1, 0)); + ZVAL_INTERNED_STR(op, ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED)); break; case IS_OBJECT: { - zval dst; - - convert_object_to_type(op, &dst, IS_STRING, convert_to_string); - zval_ptr_dtor(op); + zval tmp; - if (Z_TYPE(dst) == IS_STRING) { - ZVAL_COPY_VALUE(op, &dst); - } else { - ZVAL_NEW_STR(op, zend_string_init("Object", sizeof("Object")-1, 0)); + if (Z_OBJ_HT_P(op)->cast_object) { + if (Z_OBJ_HT_P(op)->cast_object(op, &tmp, IS_STRING) == SUCCESS) { + zval_ptr_dtor(op); + ZVAL_COPY_VALUE(op, &tmp); + return; + } + } else if (Z_OBJ_HT_P(op)->get) { + zval *z = Z_OBJ_HT_P(op)->get(op, &tmp); + if (Z_TYPE_P(z) != IS_OBJECT) { + zend_string *str = zval_get_string(z); + zval_ptr_dtor(z); + zval_ptr_dtor(op); + ZVAL_STR(op, str); + return; + } + zval_ptr_dtor(z); } + if (!EG(exception)) { + zend_throw_error(NULL, "Object of class %s could not be converted to string", ZSTR_VAL(Z_OBJCE_P(op)->name)); + } + zval_ptr_dtor(op); + ZVAL_EMPTY_STRING(op); break; } case IS_REFERENCE: @@ -591,6 +605,19 @@ try_again: } /* }}} */ +ZEND_API zend_bool ZEND_FASTCALL _try_convert_to_string(zval *op) +{ + if (Z_TYPE_P(op) != IS_STRING) { + zend_string *str = zval_get_string_func(op); + if (UNEXPECTED(EG(exception))) { + return 0; + } + zval_ptr_dtor(op); + ZVAL_STR(op, str); + } + return 1; +} + static void convert_scalar_to_array(zval *op) /* {{{ */ { HashTable *ht = zend_new_array(1); @@ -859,7 +886,7 @@ try_again: } case IS_ARRAY: zend_error(E_NOTICE, "Array to string conversion"); - return zend_string_init("Array", sizeof("Array")-1, 0); + return ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED); case IS_OBJECT: { zval tmp; if (Z_OBJ_HT_P(op)->cast_object) { @@ -875,7 +902,9 @@ try_again: } zval_ptr_dtor(z); } - zend_error(EG(exception) ? E_ERROR : E_RECOVERABLE_ERROR, "Object of class %s could not be converted to string", ZSTR_VAL(Z_OBJCE_P(op)->name)); + if (!EG(exception)) { + zend_throw_error(NULL, "Object of class %s could not be converted to string", ZSTR_VAL(Z_OBJCE_P(op)->name)); + } return ZSTR_EMPTY_ALLOC(); } case IS_REFERENCE: @@ -889,6 +918,16 @@ try_again: } /* }}} */ +ZEND_API zend_string* ZEND_FASTCALL zval_try_get_string_func(zval *op) /* {{{ */ +{ + zend_string *str = zval_get_string_func(op); + if (UNEXPECTED(EG(exception))) { + return NULL; + } + return str; +} +/* }}} */ + static zend_never_inline void ZEND_FASTCALL add_function_array(zval *result, zval *op1, zval *op2) /* {{{ */ { if ((result == op1) && (result == op2)) { diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 1effdd6c67..458ca66e2e 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -258,6 +258,7 @@ ZEND_API void multi_convert_to_string_ex(int argc, ...); ZEND_API zend_long ZEND_FASTCALL zval_get_long_func(zval *op); ZEND_API double ZEND_FASTCALL zval_get_double_func(zval *op); ZEND_API zend_string* ZEND_FASTCALL zval_get_string_func(zval *op); +ZEND_API zend_string* ZEND_FASTCALL zval_try_get_string_func(zval *op); static zend_always_inline zend_long zval_get_long(zval *op) { return EXPECTED(Z_TYPE_P(op) == IS_LONG) ? Z_LVAL_P(op) : zval_get_long_func(op); @@ -283,6 +284,35 @@ static zend_always_inline void zend_tmp_string_release(zend_string *tmp) { } } +/* Like zval_get_string, but returns NULL if the conversion fails with an exception. */ +static zend_always_inline zend_string *zval_try_get_string(zval *op) { + if (EXPECTED(Z_TYPE_P(op) == IS_STRING)) { + return Z_STR_P(op); + } else { + return zval_try_get_string_func(op); + } +} + +/* Like zval_get_tmp_string, but returns NULL if the conversion fails with an exception. */ +static zend_always_inline zend_string *zval_try_get_tmp_string(zval *op, zend_string **tmp) { + if (EXPECTED(Z_TYPE_P(op) == IS_STRING)) { + *tmp = NULL; + return Z_STR_P(op); + } else { + return *tmp = zval_try_get_string_func(op); + } +} + +/* Like convert_to_string(), but returns whether the conversion succeeded and does not modify the + * zval in-place if it fails. */ +ZEND_API zend_bool ZEND_FASTCALL _try_convert_to_string(zval *op); +static zend_always_inline zend_bool try_convert_to_string(zval *op) { + if (Z_TYPE_P(op) == IS_STRING) { + return 1; + } + return _try_convert_to_string(op); +} + /* Compatibility macros for 7.2 and below */ #define _zval_get_long(op) zval_get_long(op) #define _zval_get_double(op) zval_get_double(op) diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 20275f330a..b4a0ddef95 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -506,6 +506,7 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_NAME, "name") \ _(ZEND_STR_ARGV, "argv") \ _(ZEND_STR_ARGC, "argc") \ + _(ZEND_STR_ARRAY_CAPITALIZED, "Array") \ typedef enum _zend_known_string_id { diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b4d41dfa59..f31b05991d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1649,6 +1649,11 @@ ZEND_VM_HELPER(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED, int type) ZVAL_UNDEFINED_OP1(); } name = zval_get_tmp_string(varname, &tmp_name); + if (UNEXPECTED(EG(exception))) { + FREE_OP1(); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } } target_symbol_table = zend_get_target_symbol_table(opline->extended_value EXECUTE_DATA_CC); @@ -5909,6 +5914,10 @@ ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED, VAR_FETCH) varname = ZVAL_UNDEFINED_OP1(); } name = zval_get_tmp_string(varname, &tmp_name); + if (UNEXPECTED(EG(exception))) { + FREE_OP1(); + HANDLE_EXCEPTION(); + } } target_symbol_table = zend_get_target_symbol_table(opline->extended_value EXECUTE_DATA_CC); @@ -7923,7 +7932,9 @@ ZEND_VM_COLD_CONST_HANDLER(121, ZEND_STRLEN, CONST|TMPVAR|CV, ANY) } zval_ptr_dtor(&tmp); } - zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + if (!EG(exception)) { + zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + } ZVAL_NULL(EX_VAR(opline->result.var)); } while (0); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index b967d281dc..178f7b335a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4184,7 +4184,9 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CONST } zval_ptr_dtor(&tmp); } - zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + if (!EG(exception)) { + zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + } ZVAL_NULL(EX_VAR(opline->result.var)); } while (0); } @@ -8694,6 +8696,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ ZVAL_UNDEFINED_OP1(); } name = zval_get_tmp_string(varname, &tmp_name); + if (UNEXPECTED(EG(exception))) { + + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } } target_symbol_table = zend_get_target_symbol_table(opline->extended_value EXECUTE_DATA_CC); @@ -9205,6 +9212,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_UNUSED_HA varname = ZVAL_UNDEFINED_OP1(); } name = zval_get_tmp_string(varname, &tmp_name); + if (UNEXPECTED(EG(exception))) { + + HANDLE_EXCEPTION(); + } } target_symbol_table = zend_get_target_symbol_table(opline->extended_value EXECUTE_DATA_CC); @@ -12959,7 +12970,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_TMPVAR_HANDLER(ZEN } zval_ptr_dtor(&tmp); } - zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + if (!EG(exception)) { + zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + } ZVAL_NULL(EX_VAR(opline->result.var)); } while (0); } @@ -16093,6 +16106,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ ZVAL_UNDEFINED_OP1(); } name = zval_get_tmp_string(varname, &tmp_name); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(free_op1); + HANDLE_EXCEPTION(); + } } target_symbol_table = zend_get_target_symbol_table(opline->extended_value EXECUTE_DATA_CC); @@ -16213,6 +16230,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_H varname = ZVAL_UNDEFINED_OP1(); } name = zval_get_tmp_string(varname, &tmp_name); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(free_op1); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } } target_symbol_table = zend_get_target_symbol_table(opline->extended_value EXECUTE_DATA_CC); @@ -40041,7 +40063,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CV_HANDLER(ZEND_OP } zval_ptr_dtor(&tmp); } - zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + if (!EG(exception)) { + zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + } ZVAL_NULL(EX_VAR(opline->result.var)); } while (0); } @@ -48775,6 +48799,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ ZVAL_UNDEFINED_OP1(); } name = zval_get_tmp_string(varname, &tmp_name); + if (UNEXPECTED(EG(exception))) { + + HANDLE_EXCEPTION(); + } } target_symbol_table = zend_get_target_symbol_table(opline->extended_value EXECUTE_DATA_CC); @@ -49597,6 +49625,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDL varname = ZVAL_UNDEFINED_OP1(); } name = zval_get_tmp_string(varname, &tmp_name); + if (UNEXPECTED(EG(exception))) { + + ZVAL_UNDEF(EX_VAR(opline->result.var)); + HANDLE_EXCEPTION(); + } } target_symbol_table = zend_get_target_symbol_table(opline->extended_value EXECUTE_DATA_CC); diff --git a/ext/date/php_date.c b/ext/date/php_date.c index f7db8a26d7..3e8fb419d8 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -2043,6 +2043,9 @@ static int date_interval_has_property(zval *object, zval *member, int type, void ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; cache_slot = NULL; + if (EG(exception)) { + return 0; + } } obj = Z_PHPINTERVAL_P(object); @@ -4167,6 +4170,9 @@ static zval *date_interval_read_property(zval *object, zval *member, int type, v ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; cache_slot = NULL; + if (EG(exception)) { + return &EG(uninitialized_zval); + } } obj = Z_PHPINTERVAL_P(object); @@ -4235,6 +4241,9 @@ static zval *date_interval_write_property(zval *object, zval *member, zval *valu ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; cache_slot = NULL; + if (EG(exception)) { + return value; + } } obj = Z_PHPINTERVAL_P(object); @@ -4286,6 +4295,9 @@ static zval *date_interval_get_property_ptr_ptr(zval *object, zval *member, int ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; cache_slot = NULL; + if (EG(exception)) { + return NULL; + } } if(zend_binary_strcmp("y", sizeof("y") - 1, Z_STRVAL_P(member), Z_STRLEN_P(member)) == 0 || diff --git a/ext/dba/dba.c b/ext/dba/dba.c index ede12f0bd6..952993582b 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -672,6 +672,12 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, int persistent) keylen += Z_STRLEN(args[i]); } + /* Exception during string conversion */ + if (EG(exception)) { + FREENOW; + return; + } + if (persistent) { zend_resource *le; diff --git a/ext/dom/attr.c b/ext/dom/attr.c index f59b46799f..0255708517 100644 --- a/ext/dom/attr.c +++ b/ext/dom/attr.c @@ -160,12 +160,15 @@ int dom_attr_value_write(dom_object *obj, zval *newval) return FAILURE; } + str = zval_get_string(newval); + if (EG(exception)) { + return FAILURE; + } + if (attrp->children) { node_list_unlink(attrp->children); } - str = zval_get_string(newval); - xmlNodeSetContentLen((xmlNodePtr) attrp, (xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str) + 1); zend_string_release_ex(str, 0); diff --git a/ext/dom/characterdata.c b/ext/dom/characterdata.c index f473240498..7676370677 100644 --- a/ext/dom/characterdata.c +++ b/ext/dom/characterdata.c @@ -105,6 +105,9 @@ int dom_characterdata_data_write(dom_object *obj, zval *newval) } str = zval_get_string(newval); + if (EG(exception)) { + return FAILURE; + } xmlNodeSetContentLen(nodep, (xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str) + 1); diff --git a/ext/dom/document.c b/ext/dom/document.c index 95b077b648..235323e1d7 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -338,6 +338,9 @@ int dom_document_encoding_write(dom_object *obj, zval *newval) } str = zval_get_string(newval); + if (EG(exception)) { + return FAILURE; + } handler = xmlFindCharEncodingHandler(Z_STRVAL_P(newval)); @@ -431,12 +434,15 @@ int dom_document_version_write(dom_object *obj, zval *newval) return FAILURE; } + str = zval_get_string(newval); + if (EG(exception)) { + return FAILURE; + } + if (docp->version != NULL) { xmlFree((xmlChar *) docp->version ); } - str = zval_get_string(newval); - docp->version = xmlStrdup((const xmlChar *) ZSTR_VAL(str)); zend_string_release_ex(str, 0); @@ -659,12 +665,15 @@ int dom_document_document_uri_write(dom_object *obj, zval *newval) return FAILURE; } + str = zval_get_string(newval); + if (EG(exception)) { + return FAILURE; + } + if (docp->URL != NULL) { xmlFree((xmlChar *) docp->URL); } - str = zval_get_string(newval); - docp->URL = xmlStrdup((const xmlChar *) ZSTR_VAL(str)); zend_string_release_ex(str, 0); diff --git a/ext/dom/node.c b/ext/dom/node.c index 03e61efa67..44e6f58b30 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -323,12 +323,18 @@ int dom_node_node_value_read(dom_object *obj, zval *retval) int dom_node_node_value_write(dom_object *obj, zval *newval) { xmlNode *nodep = dom_object_get_node(obj); + zend_string *str; if (nodep == NULL) { php_dom_throw_error(INVALID_STATE_ERR, 0); return FAILURE; } + str = zval_get_string(newval); + if (EG(exception)) { + return FAILURE; + } + /* Access to Element node is implemented as a convenience method */ switch (nodep->type) { case XML_ELEMENT_NODE: @@ -342,16 +348,13 @@ int dom_node_node_value_write(dom_object *obj, zval *newval) case XML_COMMENT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: - { - zend_string *str = zval_get_string(newval); - xmlNodeSetContentLen(nodep, (xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str) + 1); - zend_string_release_ex(str, 0); - break; - } + xmlNodeSetContentLen(nodep, (xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str) + 1); + break; default: break; } + zend_string_release_ex(str, 0); return SUCCESS; } @@ -722,6 +725,10 @@ int dom_node_prefix_write(dom_object *obj, zval *newval) } } str = zval_get_string(newval); + if (EG(exception)) { + return FAILURE; + } + prefix = ZSTR_VAL(str); if (nsnode && nodep->ns != NULL && !xmlStrEqual(nodep->ns->prefix, (xmlChar *)prefix)) { strURI = (char *) nodep->ns->href; @@ -854,6 +861,11 @@ int dom_node_text_content_write(dom_object *obj, zval *newval) return FAILURE; } + str = zval_get_string(newval); + if (EG(exception)) { + return FAILURE; + } + if (nodep->type == XML_ELEMENT_NODE || nodep->type == XML_ATTRIBUTE_NODE) { if (nodep->children) { node_list_unlink(nodep->children); @@ -862,7 +874,6 @@ int dom_node_text_content_write(dom_object *obj, zval *newval) } } - str = zval_get_string(newval); /* we have to use xmlNodeAddContent() to get the same behavior as with xmlNewText() */ xmlNodeSetContent(nodep, (xmlChar *) ""); xmlNodeAddContent(nodep, (xmlChar *) ZSTR_VAL(str)); diff --git a/ext/dom/processinginstruction.c b/ext/dom/processinginstruction.c index 0487abc373..103bfd74f0 100644 --- a/ext/dom/processinginstruction.c +++ b/ext/dom/processinginstruction.c @@ -139,6 +139,9 @@ int dom_processinginstruction_data_write(dom_object *obj, zval *newval) } str = zval_get_string(newval); + if (EG(exception)) { + return FAILURE; + } xmlNodeSetContentLen(nodep, (xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str) + 1); diff --git a/ext/dom/tests/toString_exceptions.phpt b/ext/dom/tests/toString_exceptions.phpt new file mode 100644 index 0000000000..e59532d48c --- /dev/null +++ b/ext/dom/tests/toString_exceptions.phpt @@ -0,0 +1,56 @@ +--TEST-- +Handling of exceptions during __toString +--FILE-- +loadXML( + 'Text'); + +try { $doc->encoding = $badStr; } catch (Exception $e) { echo "Exception\n"; } +try { $doc->version = $badStr; } catch (Exception $e) { echo "Exception\n"; } +try { $doc->documentURI = $badStr; } catch (Exception $e) { echo "Exception\n"; } +$root = $doc->childNodes[0]; + +$node = $root->childNodes[0]; +$attrs = $node->attributes; +$attr = $attrs[0]; +try { $attr->value = $badStr; } catch (Exception $e) { echo "Exception\n"; } +try { $attr->nodeValue = $badStr; } catch (Exception $e) { echo "Exception\n"; } + +$node2 = $root->childNodes[1]; +try { $node2->nodeValue = $badStr; } catch (Exception $e) { echo "Exception\n"; } +try { $node2->textContent = $badStr; } catch (Exception $e) { echo "Exception\n"; } +$data = $node2->childNodes[0]; +try { $data->data = $badStr; } catch (Exception $e) { echo "Exception\n"; } + +$node3 = $root->childNodes[2]; +try { $node3->prefix = $badStr; } catch (Exception $e) { echo "Exception\n"; } + +$pi = $root->childNodes[3]; +try { $pi->data = $badStr; } catch (Exception $e) { echo "Exception\n"; } + +echo $doc->saveXML(); + +?> +--EXPECT-- +Exception +Exception +Exception +Exception +Exception +Exception +Exception +Exception +Exception +Exception + +Text diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 1fd42fe096..0ec5b1a1b8 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -4419,7 +4419,9 @@ PHP_FUNCTION(exif_read_data) ret = exif_read_from_stream(&ImageInfo, p_stream, read_thumbnail, read_all); } else { - convert_to_string(stream); + if (!try_convert_to_string(stream)) { + return; + } if (!Z_STRLEN_P(stream)) { exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_WARNING, "Filename cannot be empty"); @@ -4589,7 +4591,9 @@ PHP_FUNCTION(exif_thumbnail) ret = exif_read_from_stream(&ImageInfo, p_stream, 1, 0); } else { - convert_to_string(stream); + if (!try_convert_to_string(stream)) { + return; + } if (!Z_STRLEN_P(stream)) { exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_WARNING, "Filename cannot be empty"); diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 786589d263..9aa3953918 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -2298,7 +2298,10 @@ PHP_FUNCTION(imagecreatefromstring) return; } - convert_to_string_ex(data); + if (!try_convert_to_string(data)) { + return; + } + if (Z_STRLEN_P(data) < sizeof(sig)) { php_error_docref(NULL, E_WARNING, "Empty string or invalid image"); RETURN_FALSE; diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c index bb40b6a215..688e3a2552 100644 --- a/ext/iconv/iconv.c +++ b/ext/iconv/iconv.c @@ -2243,7 +2243,7 @@ PHP_FUNCTION(iconv_mime_encode) if (pref != NULL) { zval *pzval; - if ((pzval = zend_hash_str_find(Z_ARRVAL_P(pref), "scheme", sizeof("scheme") - 1)) != NULL) { + if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "scheme", sizeof("scheme") - 1)) != NULL) { if (Z_TYPE_P(pzval) == IS_STRING && Z_STRLEN_P(pzval) > 0) { switch (Z_STRVAL_P(pzval)[0]) { case 'B': case 'b': @@ -2257,7 +2257,7 @@ PHP_FUNCTION(iconv_mime_encode) } } - if ((pzval = zend_hash_str_find(Z_ARRVAL_P(pref), "input-charset", sizeof("input-charset") - 1)) != NULL && Z_TYPE_P(pzval) == IS_STRING) { + if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "input-charset", sizeof("input-charset") - 1)) != NULL && Z_TYPE_P(pzval) == IS_STRING) { if (Z_STRLEN_P(pzval) >= ICONV_CSNMAXLEN) { php_error_docref(NULL, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); RETURN_FALSE; @@ -2269,7 +2269,7 @@ PHP_FUNCTION(iconv_mime_encode) } - if ((pzval = zend_hash_str_find(Z_ARRVAL_P(pref), "output-charset", sizeof("output-charset") - 1)) != NULL && Z_TYPE_P(pzval) == IS_STRING) { + if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "output-charset", sizeof("output-charset") - 1)) != NULL && Z_TYPE_P(pzval) == IS_STRING) { if (Z_STRLEN_P(pzval) >= ICONV_CSNMAXLEN) { php_error_docref(NULL, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); RETURN_FALSE; @@ -2280,13 +2280,16 @@ PHP_FUNCTION(iconv_mime_encode) } } - if ((pzval = zend_hash_str_find(Z_ARRVAL_P(pref), "line-length", sizeof("line-length") - 1)) != NULL) { + if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "line-length", sizeof("line-length") - 1)) != NULL) { line_len = zval_get_long(pzval); } - if ((pzval = zend_hash_str_find(Z_ARRVAL_P(pref), "line-break-chars", sizeof("line-break-chars") - 1)) != NULL) { + if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "line-break-chars", sizeof("line-break-chars") - 1)) != NULL) { if (Z_TYPE_P(pzval) != IS_STRING) { tmp_str = zval_get_string_func(pzval); + if (EG(exception)) { + return; + } lfchars = ZSTR_VAL(tmp_str); } else { lfchars = Z_STRVAL_P(pzval); diff --git a/ext/imap/php_imap.c b/ext/imap/php_imap.c index 3cee4e023e..b5f12f6eee 100644 --- a/ext/imap/php_imap.c +++ b/ext/imap/php_imap.c @@ -2060,7 +2060,9 @@ PHP_FUNCTION(imap_delete) RETURN_FALSE; } - convert_to_string_ex(sequence); + if (!try_convert_to_string(sequence)) { + return; + } mail_setflag_full(imap_le_struct->imap_stream, Z_STRVAL_P(sequence), "\\DELETED", (argc == 3 ? flags : NIL)); RETVAL_TRUE; @@ -2084,7 +2086,9 @@ PHP_FUNCTION(imap_undelete) RETURN_FALSE; } - convert_to_string_ex(sequence); + if (!try_convert_to_string(sequence)) { + return; + } mail_clearflag_full(imap_le_struct->imap_stream, Z_STRVAL_P(sequence), "\\DELETED", (argc == 3 ? flags : NIL)); RETVAL_TRUE; @@ -2503,7 +2507,9 @@ PHP_FUNCTION(imap_savebody) break; default: - convert_to_string_ex(out); + if (!try_convert_to_string(out)) { + return; + } writer = php_stream_open_wrapper(Z_STRVAL_P(out), "wb", REPORT_ERRORS, NULL); break; } diff --git a/ext/intl/dateformat/dateformat_format_object.cpp b/ext/intl/dateformat/dateformat_format_object.cpp index c2429fb592..90e148bca0 100644 --- a/ext/intl/dateformat/dateformat_format_object.cpp +++ b/ext/intl/dateformat/dateformat_format_object.cpp @@ -142,7 +142,9 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object) } dateStyle = timeStyle = (DateFormat::EStyle)Z_LVAL_P(format); } else { - convert_to_string_ex(format); + if (!try_convert_to_string(format)) { + return; + } if (Z_STRLEN_P(format) == 0) { intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_format_object: the format is empty", 0); diff --git a/ext/intl/timezone/timezone_class.cpp b/ext/intl/timezone/timezone_class.cpp index aabb0f3f55..695cc7d3ea 100644 --- a/ext/intl/timezone/timezone_class.cpp +++ b/ext/intl/timezone/timezone_class.cpp @@ -179,7 +179,10 @@ U_CFUNC TimeZone *timezone_process_timezone_argument(zval *zv_timezone, UnicodeString id, gottenId; UErrorCode status = U_ZERO_ERROR; /* outside_error may be NULL */ - convert_to_string_ex(zv_timezone); + if (!try_convert_to_string(zv_timezone)) { + zval_ptr_dtor_str(&local_zv_tz); + return NULL; + } if (intl_stringFromChar(id, Z_STRVAL_P(zv_timezone), Z_STRLEN_P(zv_timezone), &status) == FAILURE) { spprintf(&message, 0, "%s: Time zone identifier given is not a " diff --git a/ext/intl/timezone/timezone_methods.cpp b/ext/intl/timezone/timezone_methods.cpp index 3f91db3130..6e1d22324b 100644 --- a/ext/intl/timezone/timezone_methods.cpp +++ b/ext/intl/timezone/timezone_methods.cpp @@ -177,7 +177,9 @@ double_offset: } else if (Z_TYPE_P(arg) == IS_OBJECT || Z_TYPE_P(arg) == IS_STRING) { zend_long lval; double dval; - convert_to_string_ex(arg); + if (!try_convert_to_string(arg)) { + return; + } switch (is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), &lval, &dval, 0)) { case IS_DOUBLE: zval_ptr_dtor(arg); diff --git a/ext/intl/transliterator/transliterator_class.c b/ext/intl/transliterator/transliterator_class.c index a662dcee7d..a619ed4309 100644 --- a/ext/intl/transliterator/transliterator_class.c +++ b/ext/intl/transliterator/transliterator_class.c @@ -189,7 +189,7 @@ err: } /* }}} */ -#define TRANSLITERATOR_PROPERTY_HANDLER_PROLOG \ +#define TRANSLITERATOR_PROPERTY_HANDLER_PROLOG(return_fail) \ zval tmp_member; \ if( Z_TYPE_P( member ) != IS_STRING ) \ { \ @@ -197,6 +197,7 @@ err: zval_get_string_func(member)); \ member = &tmp_member; \ cache_slot = NULL; \ + if (EG(exception)) { return_fail; } \ } #define TRANSLITERATOR_PROPERTY_HANDLER_EPILOG \ @@ -210,7 +211,7 @@ static zval *Transliterator_get_property_ptr_ptr( zval *object, zval *member, in { zval *retval; - TRANSLITERATOR_PROPERTY_HANDLER_PROLOG; + TRANSLITERATOR_PROPERTY_HANDLER_PROLOG(return NULL); if(zend_binary_strcmp( "id", sizeof( "id" ) - 1, Z_STRVAL_P( member ), Z_STRLEN_P( member ) ) == 0 ) @@ -233,7 +234,7 @@ static zval *Transliterator_read_property( zval *object, zval *member, int type, { zval *retval; - TRANSLITERATOR_PROPERTY_HANDLER_PROLOG; + TRANSLITERATOR_PROPERTY_HANDLER_PROLOG(return &EG(uninitialized_zval)); if( ( type != BP_VAR_R && type != BP_VAR_IS ) && ( zend_binary_strcmp( "id", sizeof( "id" ) - 1, @@ -258,7 +259,7 @@ static zval *Transliterator_read_property( zval *object, zval *member, int type, static zval *Transliterator_write_property( zval *object, zval *member, zval *value, void **cache_slot ) { zend_class_entry *scope; - TRANSLITERATOR_PROPERTY_HANDLER_PROLOG; + TRANSLITERATOR_PROPERTY_HANDLER_PROLOG(return value); if (EG(fake_scope)) { scope = EG(fake_scope); diff --git a/ext/intl/transliterator/transliterator_methods.c b/ext/intl/transliterator/transliterator_methods.c index 25d0b9a4da..50dd344671 100644 --- a/ext/intl/transliterator/transliterator_methods.c +++ b/ext/intl/transliterator/transliterator_methods.c @@ -330,9 +330,8 @@ PHP_FUNCTION( transliterator_transliterate ) else { /* not a transliterator object as first argument */ int res; - if(Z_TYPE_P( arg1 ) != IS_STRING ) - { - convert_to_string( arg1 ); + if( !try_convert_to_string( arg1 ) ) { + return; } object = &tmp_object; res = create_transliterator( Z_STRVAL_P( arg1 ), Z_STRLEN_P( arg1 ), diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 7471065494..15ef274ee4 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -662,8 +662,9 @@ is_string: } } else if (Z_TYPE(retval) != IS_NULL) { /* retval not string nor resource nor null; convert to string */ - convert_to_string(&retval); - goto is_string; + if (try_convert_to_string(&retval)) { + goto is_string; + } } /* else is null; don't try anything */ } diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 69d5fa9101..e3611562e5 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -826,8 +826,13 @@ php_mb_parse_encoding_array(zval *array, const mbfl_encoding ***return_list, siz bauto = 0; n = 0; ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) { - convert_to_string_ex(hash_entry); - if (strcasecmp(Z_STRVAL_P(hash_entry), "auto") == 0) { + zend_string *encoding_str = zval_get_string(hash_entry); + if (EG(exception)) { + ret = FAILURE; + break; + } + + if (strcasecmp(ZSTR_VAL(encoding_str), "auto") == 0) { if (!bauto) { const enum mbfl_no_encoding *src = MBSTRG(default_detect_order_list); const size_t identify_list_size = MBSTRG(default_detect_order_list_size); @@ -840,7 +845,7 @@ php_mb_parse_encoding_array(zval *array, const mbfl_encoding ***return_list, siz } } } else { - const mbfl_encoding *encoding = mbfl_name2encoding(Z_STRVAL_P(hash_entry)); + const mbfl_encoding *encoding = mbfl_name2encoding(ZSTR_VAL(encoding_str)); if (encoding) { *entry++ = encoding; n++; @@ -849,6 +854,7 @@ php_mb_parse_encoding_array(zval *array, const mbfl_encoding ***return_list, siz } } i--; + zend_string_release(encoding_str); } ZEND_HASH_FOREACH_END(); if (n > 0) { if (return_list) { @@ -2000,7 +2006,9 @@ PHP_FUNCTION(mb_detect_order) } break; default: - convert_to_string_ex(arg1); + if (!try_convert_to_string(arg1)) { + return; + } if (FAILURE == php_mb_parse_encoding_list(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1), &list, &size, 0)) { if (list) { efree(list); @@ -3328,7 +3336,9 @@ PHP_FUNCTION(mb_convert_encoding) } if (Z_TYPE_P(input) != IS_STRING && Z_TYPE_P(input) != IS_ARRAY) { - convert_to_string(input); + if (!try_convert_to_string(input)) { + return; + } } if (arg_old) { @@ -3338,8 +3348,13 @@ PHP_FUNCTION(mb_convert_encoding) _from_encodings = NULL; ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) { - - convert_to_string_ex(hash_entry); + zend_string *encoding_str = zval_get_string(hash_entry); + if (EG(exception)) { + if (_from_encodings) { + efree(_from_encodings); + } + return; + } if ( _from_encodings) { l = strlen(_from_encodings); @@ -3350,6 +3365,7 @@ PHP_FUNCTION(mb_convert_encoding) } else { _from_encodings = estrdup(Z_STRVAL_P(hash_entry)); } + zend_string_release(encoding_str); } ZEND_HASH_FOREACH_END(); if (_from_encodings != NULL && !strlen(_from_encodings)) { @@ -3359,7 +3375,10 @@ PHP_FUNCTION(mb_convert_encoding) s_free = _from_encodings; break; default: - convert_to_string(arg_old); + if (!try_convert_to_string(arg_old)) { + return; + } + _from_encodings = Z_STRVAL_P(arg_old); break; } @@ -3535,7 +3554,9 @@ PHP_FUNCTION(mb_detect_encoding) } break; default: - convert_to_string(encoding_list); + if (!try_convert_to_string(encoding_list)) { + return; + } if (FAILURE == php_mb_parse_encoding_list(Z_STRVAL_P(encoding_list), Z_STRLEN_P(encoding_list), &list, &size, 0)) { if (list) { efree(list); @@ -3944,7 +3965,9 @@ PHP_FUNCTION(mb_convert_variables) php_mb_parse_encoding_array(zfrom_enc, &elist, &elistsz, 0); break; default: - convert_to_string_ex(zfrom_enc); + if (!try_convert_to_string(zfrom_enc)) { + return; + } php_mb_parse_encoding_list(Z_STRVAL_P(zfrom_enc), Z_STRLEN_P(zfrom_enc), &elist, &elistsz, 0); break; } diff --git a/ext/mbstring/php_mbregex.c b/ext/mbstring/php_mbregex.c index 83cd25d89d..9fac8233ba 100644 --- a/ext/mbstring/php_mbregex.c +++ b/ext/mbstring/php_mbregex.c @@ -924,7 +924,9 @@ static void _php_mb_regex_ereg_exec(INTERNAL_FUNCTION_PARAMETERS, int icase) if (Z_TYPE_P(arg_pattern) == IS_DOUBLE) { convert_to_long_ex(arg_pattern); /* get rid of decimal places */ } - convert_to_string_ex(arg_pattern); + if (!try_convert_to_string(arg_pattern)) { + return; + } /* don't bother doing an extended regex with just a number */ } diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 394a838512..9964ea0be4 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -309,6 +309,9 @@ zval *mysqli_read_property(zval *object, zval *member, int type, void **cache_sl if (Z_TYPE_P(member) != IS_STRING) { ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; + if (EG(exception)) { + return &EG(uninitialized_zval); + } } if (obj->prop_handler != NULL) { @@ -342,6 +345,9 @@ zval *mysqli_write_property(zval *object, zval *member, zval *value, void **cach if (Z_TYPE_P(member) != IS_STRING) { ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; + if (EG(exception)) { + return value; + } } obj = Z_MYSQLI_P(object); diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 1a7303c987..13d21e9312 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -899,7 +899,10 @@ PHP_FUNCTION(mysqli_stmt_execute) if (!(stmt->param.is_null[i] = (Z_ISNULL_P(param)))) { switch (stmt->stmt->params[i].buffer_type) { case MYSQL_TYPE_VAR_STRING: - convert_to_string_ex(param); + if (!try_convert_to_string(param)) { + return; + } + stmt->stmt->params[i].buffer = Z_STRVAL_P(param); stmt->stmt->params[i].buffer_length = Z_STRLEN_P(param); break; @@ -1781,7 +1784,9 @@ PHP_FUNCTION(mysqli_options) if (expected_type != Z_TYPE_P(mysql_value)) { switch (expected_type) { case IS_STRING: - convert_to_string_ex(mysql_value); + if (!try_convert_to_string(mysql_value)) { + return; + } break; case IS_LONG: convert_to_long_ex(mysql_value); diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c index 641c7ee783..7da46a90f8 100644 --- a/ext/mysqlnd/mysqlnd_ps_codec.c +++ b/ext/mysqlnd/mysqlnd_ps_codec.c @@ -777,7 +777,10 @@ use_string: } the_var = &((*copies_param)[i]); } - convert_to_string_ex(the_var); + + if (!try_convert_to_string(the_var)) { + goto end; + } *data_size += Z_STRLEN_P(the_var); break; } diff --git a/ext/oci8/oci8_statement.c b/ext/oci8/oci8_statement.c index 4face29e4a..91f6223373 100644 --- a/ext/oci8/oci8_statement.c +++ b/ext/oci8/oci8_statement.c @@ -1189,7 +1189,9 @@ int php_oci_bind_by_name(php_oci_statement *statement, char *name, size_t name_l return 1; } if (Z_TYPE_P(param) != IS_NULL) { - convert_to_string(param); + if (!try_convert_to_string(param)) { + return 1; + } } if ((maxlength == -1) || (maxlength == 0)) { if (type == SQLT_LNG) { @@ -1390,8 +1392,10 @@ sb4 php_oci_bind_in_callback( *alenp = -1; *indpp = (dvoid *)&phpbind->indicator; } else if ((phpbind->descriptor == 0) && (phpbind->statement == 0)) { - /* "normal string bind */ - convert_to_string(val); + /* "normal" string bind */ + if (!try_convert_to_string(val)) { + return OCI_ERROR; + } *bufpp = Z_STRVAL_P(val); *alenp = (ub4) Z_STRLEN_P(val); @@ -1483,7 +1487,6 @@ sb4 php_oci_bind_out_callback( *indpp = &phpbind->indicator; retval = OCI_CONTINUE; } else { - convert_to_string(val); zval_ptr_dtor(val); { @@ -1741,7 +1744,9 @@ php_oci_bind *php_oci_bind_array_helper_string(zval *var, zend_long max_table_le if (maxlength == -1) { zend_hash_internal_pointer_reset(hash); while ((entry = zend_hash_get_current_data(hash)) != NULL) { - convert_to_string_ex(entry); + if (!try_convert_to_string(entry)) { + return NULL; + } if (maxlength == -1 || Z_STRLEN_P(entry) > (size_t) maxlength) { maxlength = Z_STRLEN_P(entry) + 1; @@ -1767,7 +1772,14 @@ php_oci_bind *php_oci_bind_array_helper_string(zval *var, zend_long max_table_le for (i = 0; i < bind->array.current_length; i++) { if ((entry = zend_hash_get_current_data(hash)) != NULL) { - convert_to_string_ex(entry); + if (!try_convert_to_string(entry)) { + efree(bind->array.elements); + efree(bind->array.element_lengths); + efree(bind->array.indicators); + efree(bind); + return NULL; + } + bind->array.element_lengths[i] = (ub2) Z_STRLEN_P(entry); if (Z_STRLEN_P(entry) == 0) { bind->array.indicators[i] = -1; @@ -1782,8 +1794,14 @@ php_oci_bind *php_oci_bind_array_helper_string(zval *var, zend_long max_table_le for (i = 0; i < max_table_length; i++) { if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) { int element_length; + if (!try_convert_to_string(entry)) { + efree(bind->array.elements); + efree(bind->array.element_lengths); + efree(bind->array.indicators); + efree(bind); + return NULL; + } - convert_to_string_ex(entry); element_length = ((size_t) maxlength > Z_STRLEN_P(entry)) ? (int) Z_STRLEN_P(entry) : (int) maxlength; memcpy((text *)bind->array.elements + i*maxlength, Z_STRVAL_P(entry), element_length); @@ -1912,9 +1930,16 @@ php_oci_bind *php_oci_bind_array_helper_date(zval *var, zend_long max_table_leng bind->array.element_lengths[i] = sizeof(OCIDate); } if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) { + zend_string *entry_str = zval_get_string(entry); + if (EG(exception)) { + efree(bind->array.element_lengths); + efree(bind->array.elements); + efree(bind); + return NULL; + } - convert_to_string_ex(entry); - PHP_OCI_CALL_RETURN(errstatus, OCIDateFromText, (connection->err, (CONST text *)Z_STRVAL_P(entry), (ub4) Z_STRLEN_P(entry), NULL, 0, NULL, 0, &oci_date)); + PHP_OCI_CALL_RETURN(errstatus, OCIDateFromText, (connection->err, (CONST text *)ZSTR_VAL(entry_str), (ub4) ZSTR_LEN(entry_str), NULL, 0, NULL, 0, &oci_date)); + zend_string_release(entry_str); if (errstatus != OCI_SUCCESS) { /* failed to convert string to date */ diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 37257c3e12..6bd24781b4 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -1343,9 +1343,7 @@ PHP_FUNCTION(odbc_execute) } otype = Z_TYPE_P(tmp); - convert_to_string_ex(tmp); - if (Z_TYPE_P(tmp) != IS_STRING) { - php_error_docref(NULL, E_WARNING,"Error converting parameter"); + if (!try_convert_to_string(tmp)) { SQLFreeStmt(result->stmt, SQL_RESET_PARAMS); for (i = 0; i < result->numparams; i++) { if (params[i].fp != -1) { diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 8cf294b361..7f287c8823 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -1725,7 +1725,9 @@ static X509 * php_openssl_x509_from_zval(zval * val, int makeresource, zend_reso } /* force it to be a string and check if it refers to a file */ - convert_to_string_ex(val); + if (!try_convert_to_string(val)) { + return NULL; + } if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) { @@ -2671,32 +2673,37 @@ static X509_STORE *php_openssl_setup_verify(zval *calist) if (calist && (Z_TYPE_P(calist) == IS_ARRAY)) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(calist), item) { - convert_to_string_ex(item); + zend_string *str = zval_get_string(item); + if (EG(exception)) { + return NULL; + } - if (VCWD_STAT(Z_STRVAL_P(item), &sb) == -1) { - php_error_docref(NULL, E_WARNING, "unable to stat %s", Z_STRVAL_P(item)); + if (VCWD_STAT(ZSTR_VAL(str), &sb) == -1) { + php_error_docref(NULL, E_WARNING, "unable to stat %s", ZSTR_VAL(str)); + zend_string_release(str); continue; } if ((sb.st_mode & S_IFREG) == S_IFREG) { file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); - if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, Z_STRVAL_P(item), X509_FILETYPE_PEM)) { + if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, ZSTR_VAL(str), X509_FILETYPE_PEM)) { php_openssl_store_errors(); - php_error_docref(NULL, E_WARNING, "error loading file %s", Z_STRVAL_P(item)); + php_error_docref(NULL, E_WARNING, "error loading file %s", ZSTR_VAL(str)); } else { nfiles++; } file_lookup = NULL; } else { dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); - if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, Z_STRVAL_P(item), X509_FILETYPE_PEM)) { + if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, ZSTR_VAL(str), X509_FILETYPE_PEM)) { php_openssl_store_errors(); - php_error_docref(NULL, E_WARNING, "error loading directory %s", Z_STRVAL_P(item)); + php_error_docref(NULL, E_WARNING, "error loading directory %s", ZSTR_VAL(str)); } else { ndirs++; } dir_lookup = NULL; } + zend_string_release(str); } ZEND_HASH_FOREACH_END(); } if (nfiles == 0) { @@ -3145,23 +3152,25 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z /* apply values from the dn hash */ ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(dn), strindex, item) { if (strindex) { - int nid; - - convert_to_string_ex(item); - - nid = OBJ_txt2nid(ZSTR_VAL(strindex)); + int nid = OBJ_txt2nid(ZSTR_VAL(strindex)); if (nid != NID_undef) { + zend_string *str_item = zval_get_string(item); + if (EG(exception)) { + return FAILURE; + } if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, - (unsigned char*)Z_STRVAL_P(item), -1, -1, 0)) + (unsigned char*)ZSTR_VAL(str_item), -1, -1, 0)) { php_openssl_store_errors(); php_error_docref(NULL, E_WARNING, "dn: add_entry_by_NID %d -> %s (failed; check error" " queue and value of string_mask OpenSSL option " "if illegal characters are reported)", - nid, Z_STRVAL_P(item)); + nid, ZSTR_VAL(str_item)); + zend_string_release(str_item); return FAILURE; } + zend_string_release(str_item); } else { php_error_docref(NULL, E_WARNING, "dn: %s is not a recognized name", ZSTR_VAL(strindex)); } @@ -3226,15 +3235,19 @@ static int php_openssl_make_REQ(struct php_x509_request * req, X509_REQ * csr, z continue; } - convert_to_string_ex(item); - nid = OBJ_txt2nid(ZSTR_VAL(strindex)); if (nid != NID_undef) { - if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)Z_STRVAL_P(item), -1, -1, 0)) { + zend_string *str_item = zval_get_string(item); + if (EG(exception)) { + return FAILURE; + } + if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_UTF8, (unsigned char*)ZSTR_VAL(str_item), -1, -1, 0)) { php_openssl_store_errors(); - php_error_docref(NULL, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, Z_STRVAL_P(item)); + php_error_docref(NULL, E_WARNING, "attribs: add_entry_by_NID %d -> %s (failed)", nid, ZSTR_VAL(str_item)); + zend_string_release(str_item); return FAILURE; } + zend_string_release(str_item); } else { php_error_docref(NULL, E_WARNING, "dn: %s is not a recognized name", ZSTR_VAL(strindex)); } @@ -3803,7 +3816,10 @@ static EVP_PKEY * php_openssl_evp_from_zval( passphrase_len = Z_STRLEN_P(zphrase); } else { ZVAL_COPY(&tmp, zphrase); - convert_to_string(&tmp); + if (!try_convert_to_string(&tmp)) { + return NULL; + } + passphrase = Z_STRVAL(tmp); passphrase_len = Z_STRLEN(tmp); } @@ -3864,7 +3880,9 @@ static EVP_PKEY * php_openssl_evp_from_zval( if (!(Z_TYPE_P(val) == IS_STRING || Z_TYPE_P(val) == IS_OBJECT)) { TMP_CLEAN; } - convert_to_string_ex(val); + if (!try_convert_to_string(val)) { + TMP_CLEAN; + } if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) { filename = Z_STRVAL_P(val) + (sizeof("file://") - 1); @@ -5351,13 +5369,16 @@ PHP_FUNCTION(openssl_pkcs7_encrypt) /* tack on extra headers */ if (zheaders) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, zcertval) { - convert_to_string_ex(zcertval); - + zend_string *str = zval_get_string(zcertval); + if (EG(exception)) { + goto clean_exit; + } if (strindex) { - BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), Z_STRVAL_P(zcertval)); + BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str)); } else { - BIO_printf(outfile, "%s\n", Z_STRVAL_P(zcertval)); + BIO_printf(outfile, "%s\n", ZSTR_VAL(str)); } + zend_string_release(str); } ZEND_HASH_FOREACH_END(); } @@ -5566,13 +5587,16 @@ PHP_FUNCTION(openssl_pkcs7_sign) int ret; ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), strindex, hval) { - convert_to_string_ex(hval); - + zend_string *str = zval_get_string(hval); + if (EG(exception)) { + goto clean_exit; + } if (strindex) { - ret = BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), Z_STRVAL_P(hval)); + ret = BIO_printf(outfile, "%s: %s\n", ZSTR_VAL(strindex), ZSTR_VAL(str)); } else { - ret = BIO_printf(outfile, "%s\n", Z_STRVAL_P(hval)); + ret = BIO_printf(outfile, "%s\n", ZSTR_VAL(str)); } + zend_string_release(str); if (ret < 0) { php_openssl_store_errors(); } diff --git a/ext/openssl/tests/bug38261.phpt b/ext/openssl/tests/bug38261.phpt index fa25d93d62..e6e345d5ea 100644 --- a/ext/openssl/tests/bug38261.phpt +++ b/ext/openssl/tests/bug38261.phpt @@ -19,7 +19,11 @@ var_dump(openssl_x509_parse($t)); var_dump(openssl_x509_parse(array())); var_dump(openssl_x509_parse()); var_dump(openssl_x509_parse($cert)); -var_dump(openssl_x509_parse(new stdClass)); +try { + var_dump(openssl_x509_parse(new stdClass)); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} ?> --EXPECTF-- @@ -30,5 +34,4 @@ bool(false) Warning: openssl_x509_parse() expects at least 1 parameter, 0 given in %sbug38261.php on line %d NULL bool(false) - -Recoverable fatal error: Object of class stdClass could not be converted to string in %sbug38261.php on line %d +Object of class stdClass could not be converted to string diff --git a/ext/openssl/tests/openssl_pkcs7_decrypt_error.phpt b/ext/openssl/tests/openssl_pkcs7_decrypt_error.phpt index f892c6854f..63dabe8f32 100644 --- a/ext/openssl/tests/openssl_pkcs7_decrypt_error.phpt +++ b/ext/openssl/tests/openssl_pkcs7_decrypt_error.phpt @@ -15,7 +15,11 @@ $b = 1; $c = new stdclass; $d = new stdclass; -var_dump(openssl_pkcs7_decrypt($a, $b, $c, $d)); +try { + var_dump(openssl_pkcs7_decrypt($a, $b, $c, $d)); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} var_dump($c); var_dump(openssl_pkcs7_decrypt($b, $b, $b, $b)); @@ -26,9 +30,7 @@ var_dump(openssl_pkcs7_decrypt($a, $b, 0, 0)); echo "Done\n"; ?> --EXPECT-- -string(57) "Object of class stdClass could not be converted to string" -string(66) "openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert" -bool(false) +Object of class stdClass could not be converted to string object(stdClass)#1 (0) { } string(66) "openssl_pkcs7_decrypt(): unable to coerce parameter 3 to x509 cert" diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index d5276cd5b9..ad08e1f13a 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -97,7 +97,9 @@ #define GET_VER_OPT(name) \ (PHP_STREAM_CONTEXT(stream) && (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", name)) != NULL) #define GET_VER_OPT_STRING(name, str) \ - if (GET_VER_OPT(name)) { convert_to_string_ex(val); str = Z_STRVAL_P(val); } + if (GET_VER_OPT(name)) { \ + if (try_convert_to_string(val)) str = Z_STRVAL_P(val); \ + } #define GET_VER_OPT_LONG(name, num) \ if (GET_VER_OPT(name)) { num = zval_get_long(val); } @@ -1251,7 +1253,10 @@ static int php_openssl_set_server_dh_param(php_stream * stream, SSL_CTX *ctx) /* return SUCCESS; } - convert_to_string_ex(zdhpath); + if (!try_convert_to_string(zdhpath)) { + return FAILURE; + } + bio = BIO_new_file(Z_STRVAL_P(zdhpath), PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY)); if (bio == NULL) { @@ -1295,7 +1300,10 @@ static int php_openssl_set_server_ecdh_curve(php_stream *stream, SSL_CTX *ctx) / curve_nid = NID_X9_62_prime256v1; #endif } else { - convert_to_string_ex(zvcurve); + if (!try_convert_to_string(zvcurve)) { + return FAILURE; + } + curve_nid = OBJ_sn2nid(Z_STRVAL_P(zvcurve)); if (curve_nid == NID_undef) { php_error_docref(NULL, E_WARNING, "invalid ecdh_curve specified"); @@ -1465,6 +1473,7 @@ static int php_openssl_enable_server_sni(php_stream *stream, php_openssl_netstre if (Z_TYPE_P(current) == IS_ARRAY) { zval *local_pk, *local_cert; + zend_string *local_pk_str, *local_cert_str; char resolved_cert_path_buff[MAXPATHLEN], resolved_pk_path_buff[MAXPATHLEN]; local_cert = zend_hash_str_find(Z_ARRVAL_P(current), "local_cert", sizeof("local_cert")-1); @@ -1474,14 +1483,21 @@ static int php_openssl_enable_server_sni(php_stream *stream, php_openssl_netstre ); return FAILURE; } - convert_to_string_ex(local_cert); - if (!VCWD_REALPATH(Z_STRVAL_P(local_cert), resolved_cert_path_buff)) { + + local_cert_str = zval_get_string(local_cert); + if (EG(exception)) { + return FAILURE; + } + if (!VCWD_REALPATH(ZSTR_VAL(local_cert_str), resolved_cert_path_buff)) { php_error_docref(NULL, E_WARNING, "failed setting local cert chain file `%s'; file not found", - Z_STRVAL_P(local_cert) + ZSTR_VAL(local_cert_str) ); + zend_string_release(local_cert_str); return FAILURE; } + zend_string_release(local_cert_str); + local_pk = zend_hash_str_find(Z_ARRVAL_P(current), "local_pk", sizeof("local_pk")-1); if (local_pk == NULL) { php_error_docref(NULL, E_WARNING, @@ -1489,14 +1505,20 @@ static int php_openssl_enable_server_sni(php_stream *stream, php_openssl_netstre ); return FAILURE; } - convert_to_string_ex(local_pk); - if (!VCWD_REALPATH(Z_STRVAL_P(local_pk), resolved_pk_path_buff)) { + + local_pk_str = zval_get_string(local_pk); + if (EG(exception)) { + return FAILURE; + } + if (!VCWD_REALPATH(ZSTR_VAL(local_pk_str), resolved_pk_path_buff)) { php_error_docref(NULL, E_WARNING, "failed setting local private key file `%s'; file not found", - Z_STRVAL_P(local_pk) + ZSTR_VAL(local_pk_str) ); + zend_string_release(local_pk_str); return FAILURE; } + zend_string_release(local_pk_str); ctx = php_openssl_create_sni_server_ctx(resolved_cert_path_buff, resolved_pk_path_buff); diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 63751d4b17..300de1e7d4 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -967,6 +967,7 @@ PHP_FUNCTION(pcntl_exec) if (ZEND_NUM_ARGS() > 1) { /* Build argument list */ + SEPARATE_ARRAY(args); args_hash = Z_ARRVAL_P(args); argc = zend_hash_num_elements(args_hash); @@ -975,20 +976,25 @@ PHP_FUNCTION(pcntl_exec) current_arg = argv+1; ZEND_HASH_FOREACH_VAL(args_hash, element) { if (argi >= argc) break; - convert_to_string_ex(element); + if (!try_convert_to_string(element)) { + efree(argv); + return; + } + *current_arg = Z_STRVAL_P(element); argi++; current_arg++; } ZEND_HASH_FOREACH_END(); - *(current_arg) = NULL; + *current_arg = NULL; } else { argv = emalloc(2 * sizeof(char *)); - *argv = path; - *(argv+1) = NULL; + argv[0] = path; + argv[1] = NULL; } if ( ZEND_NUM_ARGS() == 3 ) { /* Build environment pair list */ + SEPARATE_ARRAY(envs); envs_hash = Z_ARRVAL_P(envs); envc = zend_hash_num_elements(envs_hash); @@ -1001,7 +1007,12 @@ PHP_FUNCTION(pcntl_exec) zend_string_addref(key); } - convert_to_string_ex(element); + if (!try_convert_to_string(element)) { + zend_string_release(key); + efree(argv); + efree(envp); + return; + } /* Length of element + equal sign + length of key + null */ pair_length = Z_STRLEN_P(element) + ZSTR_LEN(key) + 2; diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index 05f8d9f817..46794fe1d1 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -1529,6 +1529,11 @@ PHPAPI zend_string *php_pcre_replace(zend_string *regex, pcre_cache_entry *pce; /* Compiled regular expression */ zend_string *result; /* Function result */ + /* Abort on pending exception, e.g. thrown from __toString(). */ + if (UNEXPECTED(EG(exception))) { + return NULL; + } + /* Compile regex or get it from cache. */ if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) { return NULL; diff --git a/ext/pcre/tests/preg_replace_error1.phpt b/ext/pcre/tests/preg_replace_error1.phpt index 8e20108b88..780556956a 100644 --- a/ext/pcre/tests/preg_replace_error1.phpt +++ b/ext/pcre/tests/preg_replace_error1.phpt @@ -24,7 +24,11 @@ foreach($regex_array as $regex_value) { var_dump(preg_replace($regex_value, $replace, $subject)); } $regex_value = new stdclass(); //Object -var_dump(preg_replace($regex_value, $replace, $subject)); +try { + var_dump(preg_replace($regex_value, $replace, $subject)); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} ?> --EXPECTF-- *** Testing preg_replace() : error conditions*** @@ -54,5 +58,4 @@ string(1) "a" Arg value is /[a-zA-Z]/ string(1) "1" - -Recoverable fatal error: Object of class stdClass could not be converted to string in %spreg_replace_error1.php on line %d +Object of class stdClass could not be converted to string diff --git a/ext/pcre/tests/preg_replace_error2.phpt b/ext/pcre/tests/preg_replace_error2.phpt index 8c826587ea..a334b2fefd 100644 --- a/ext/pcre/tests/preg_replace_error2.phpt +++ b/ext/pcre/tests/preg_replace_error2.phpt @@ -19,7 +19,11 @@ foreach($replace as $value) { var_dump(preg_replace($regex, $value, $subject)); } $value = new stdclass(); //Object -var_dump(preg_replace($regex, $value, $subject)); +try { + var_dump(preg_replace($regex, $value, $subject)); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} echo "Done"; ?> --EXPECTF-- @@ -32,5 +36,5 @@ Arg value is: Array Warning: preg_replace(): Parameter mismatch, pattern is a string while replacement is an array in %spreg_replace_error2.php on line %d bool(false) - -Recoverable fatal error: Object of class stdClass could not be converted to string in %spreg_replace_error2.php on line %d +Object of class stdClass could not be converted to string +Done diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re index 5950cdc38e..b04e2fb928 100644 --- a/ext/pdo/pdo_sql_parser.re +++ b/ext/pdo/pdo_sql_parser.re @@ -269,7 +269,8 @@ safe: default: buf = zval_get_string(parameter); - if (!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf), + if (EG(exception) || + !stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf), ZSTR_LEN(buf), &plc->quoted, &plc->qlen, param_type)) { /* bork */ diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 92e594c76b..bd93ca3cb2 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -307,7 +307,9 @@ static int really_register_bound_param(struct pdo_bound_param_data *param, pdo_s ZVAL_STRINGL(parameter, p, len); efree(p); } else { - convert_to_string(parameter); + if (!try_convert_to_string(parameter)) { + return 0; + } } } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT && (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE)) { convert_to_long(parameter); @@ -911,7 +913,9 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value, enum pdo_ fetch_value(stmt, &val, i++, NULL); if (Z_TYPE(val) != IS_NULL) { - convert_to_string(&val); + if (!try_convert_to_string(&val)) { + return 0; + } if ((cep = zend_lookup_class(Z_STR(val))) == NULL) { stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR; } else { @@ -2180,7 +2184,9 @@ static zval *dbstmt_prop_write(zval *object, zval *member, zval *value, void **c { pdo_stmt_t *stmt = Z_PDO_STMT_P(object); - convert_to_string(member); + if (!try_convert_to_string(member)) { + return value; + } if (strcmp(Z_STRVAL_P(member), "queryString") == 0) { pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only"); @@ -2194,7 +2200,9 @@ static void dbstmt_prop_delete(zval *object, zval *member, void **cache_slot) { pdo_stmt_t *stmt = Z_PDO_STMT_P(object); - convert_to_string(member); + if (!try_convert_to_string(member)) { + return; + } if (strcmp(Z_STRVAL_P(member), "queryString") == 0) { pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "property queryString is read only"); @@ -2459,7 +2467,10 @@ static zval *row_prop_read(zval *object, zval *member, int type, void **cache_sl fetch_value(stmt, rv, lval, NULL); } } else { - convert_to_string(member); + if (!try_convert_to_string(member)) { + return &EG(uninitialized_zval); + } + /* TODO: replace this with a hash of available column names to column * numbers */ for (colno = 0; colno < stmt->column_count; colno++) { @@ -2511,7 +2522,9 @@ static int row_prop_exists(zval *object, zval *member, int check_empty, void **c return lval >=0 && lval < stmt->column_count; } } else { - convert_to_string(member); + if (!try_convert_to_string(member)) { + return 0; + } } /* TODO: replace this with a hash of available column names to column diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index d17b168c2d..d057857fa6 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -215,7 +215,7 @@ static inline zend_string *pdo_attr_strval(zval *options, enum pdo_attribute_typ zval *v; if (options && (v = zend_hash_index_find(Z_ARRVAL_P(options), option_name))) { - return zval_get_string(v); + return zval_try_get_string(v); } return defval ? zend_string_copy(defval) : NULL; } diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 6c83717e65..a4ff7e5c5a 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -466,6 +466,9 @@ static int firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *v case PDO_FB_ATTR_DATE_FORMAT: { zend_string *str = zval_get_string(val); + if (EG(exception)) { + return 0; + } if (H->date_format) { efree(H->date_format); } @@ -477,6 +480,9 @@ static int firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *v case PDO_FB_ATTR_TIME_FORMAT: { zend_string *str = zval_get_string(val); + if (EG(exception)) { + return 0; + } if (H->time_format) { efree(H->time_format); } @@ -488,6 +494,9 @@ static int firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *v case PDO_FB_ATTR_TIMESTAMP_FORMAT: { zend_string *str = zval_get_string(val); + if (EG(exception)) { + return 0; + } if (H->timestamp_format) { efree(H->timestamp_format); } diff --git a/ext/pdo_firebird/firebird_statement.c b/ext/pdo_firebird/firebird_statement.c index af71007b56..aee748d0e1 100644 --- a/ext/pdo_firebird/firebird_statement.c +++ b/ext/pdo_firebird/firebird_statement.c @@ -728,7 +728,9 @@ static int firebird_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *v default: return 0; case PDO_ATTR_CURSOR_NAME: - convert_to_string(val); + if (!try_convert_to_string(val)) { + return 0; + } if (isc_dsql_set_cursor_name(S->H->isc_status, &S->stmt, Z_STRVAL_P(val),0)) { RECORD_ERROR(stmt); diff --git a/ext/pdo_oci/oci_driver.c b/ext/pdo_oci/oci_driver.c index 607069008d..f121b4791b 100644 --- a/ext/pdo_oci/oci_driver.c +++ b/ext/pdo_oci/oci_driver.c @@ -461,6 +461,9 @@ static int oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) / { #if (OCI_MAJOR_VERSION >= 10) zend_string *action = zval_get_string(val); + if (EG(exception)) { + return 0; + } H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action), @@ -479,6 +482,9 @@ static int oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) / { #if (OCI_MAJOR_VERSION >= 10) zend_string *client_info = zval_get_string(val); + if (EG(exception)) { + return 0; + } H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(client_info), (ub4) ZSTR_LEN(client_info), @@ -497,6 +503,9 @@ static int oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) / { #if (OCI_MAJOR_VERSION >= 10) zend_string *identifier = zval_get_string(val); + if (EG(exception)) { + return 0; + } H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(identifier), (ub4) ZSTR_LEN(identifier), @@ -515,6 +524,9 @@ static int oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) / { #if (OCI_MAJOR_VERSION >= 10) zend_string *module = zval_get_string(val); + if (EG(exception)) { + return 0; + } H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module), diff --git a/ext/pdo_oci/oci_statement.c b/ext/pdo_oci/oci_statement.c index 79733c2c57..b983f20116 100644 --- a/ext/pdo_oci/oci_statement.c +++ b/ext/pdo_oci/oci_statement.c @@ -221,7 +221,9 @@ static sb4 oci_bind_input_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dv *alenp = -1; } else if (!P->thing) { /* regular string bind */ - convert_to_string(parameter); + if (!try_convert_to_string(parameter)) { + return OCI_ERROR; + } *bufpp = Z_STRVAL_P(parameter); *alenp = (ub4) Z_STRLEN_P(parameter); } @@ -260,8 +262,7 @@ static sb4 oci_bind_output_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, d return OCI_CONTINUE; } - convert_to_string(parameter); - zval_ptr_dtor_str(parameter); + zval_ptr_dtor(parameter); Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1); P->used_for_output = 1; diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index e8b748a36f..5aae5d0a38 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -592,7 +592,10 @@ static PHP_METHOD(PDO, pgsqlCopyFromArray) PQclear(pgsql_result); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) { size_t query_len; - convert_to_string_ex(tmp); + if (!try_convert_to_string(tmp)) { + efree(query); + return; + } if (buffer_len < Z_STRLEN_P(tmp)) { buffer_len = Z_STRLEN_P(tmp); diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index 02342e9fe4..9e86a82323 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -411,7 +411,10 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, break; default: - convert_to_string_ex(&retval); + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); break; } diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index 186bf182b1..d8a68efb05 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -153,7 +153,9 @@ static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_d pdo_sqlite_error_stmt(stmt); return 0; } else { - convert_to_string(parameter); + if (!try_convert_to_string(parameter)) { + return 0; + } } if (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1, @@ -176,7 +178,9 @@ static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_d return 1; } } else { - convert_to_string(parameter); + if (!try_convert_to_string(parameter)) { + return 0; + } if (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_tostring_exception.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_tostring_exception.phpt new file mode 100644 index 0000000000..b1cd78eee7 --- /dev/null +++ b/ext/pdo_sqlite/tests/pdo_sqlite_tostring_exception.phpt @@ -0,0 +1,45 @@ +--TEST-- +__toString() exception during PDO Sqlite parameter binding +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE t(id int, v varchar(255))'); + +$stmt = $db->prepare('INSERT INTO t VALUES(:i, :v)'); +$param1 = 1234; +$stmt->bindValue('i', $param1); +$param2 = "foo"; +$stmt->bindParam('v', $param2); + +$param2 = new throws; + +try { + $stmt->execute(); +} catch (Exception $e) { + echo "Exception thrown ...\n"; +} + +try { + $stmt->execute(); +} catch (Exception $e) { + echo "Exception thrown ...\n"; +} + +$query = $db->query("SELECT * FROM t"); +while ($row = $query->fetch(PDO::FETCH_ASSOC)) { + print_r($row); +} + +?> +--EXPECT-- +Exception thrown ... +Exception thrown ... diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 1be592a319..9dd08f249a 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -1330,6 +1330,11 @@ static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) smart_str_appendl(&str, Z_STRVAL(args[i]), Z_STRLEN(args[i])); } + /* Exception thrown during a string conversion. */ + if (EG(exception)) { + goto cleanup; + } + smart_str_0(&str); if (ZEND_NUM_ARGS() == 1) { /* new style, using connection string */ @@ -3045,7 +3050,6 @@ static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type) switch (Z_TYPE_P(field)) { case IS_STRING: - convert_to_string_ex(field); field_offset = PQfnumber(pgsql_result, Z_STRVAL_P(field)); if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) { php_error_docref(NULL, E_WARNING, "Bad column offset specified"); @@ -4280,22 +4284,23 @@ PHP_FUNCTION(pg_copy_from) PQclear(pgsql_result); #if HAVE_PQPUTCOPYDATA ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) { - zval tmp; - ZVAL_COPY(&tmp, value); - convert_to_string_ex(&tmp); - query = (char *)emalloc(Z_STRLEN(tmp) + 2); - strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2); - if(Z_STRLEN(tmp) > 0 && *(query + Z_STRLEN(tmp) - 1) != '\n') { - strlcat(query, "\n", Z_STRLEN(tmp) + 2); + zend_string *tmp = zval_get_string(value); + if (EG(exception)) { + return; + } + query = (char *)emalloc(ZSTR_LEN(tmp) + 2); + strlcpy(query, ZSTR_VAL(tmp), ZSTR_LEN(tmp) + 2); + if (ZSTR_LEN(tmp) > 0 && *(query + ZSTR_LEN(tmp) - 1) != '\n') { + strlcat(query, "\n", ZSTR_LEN(tmp) + 2); } if (PQputCopyData(pgsql, query, (int)strlen(query)) != 1) { efree(query); - zval_ptr_dtor_str(&tmp); + zend_string_release(tmp); PHP_PQ_ERROR("copy failed: %s", pgsql); RETURN_FALSE; } efree(query); - zval_ptr_dtor_str(&tmp); + zend_string_release(tmp); } ZEND_HASH_FOREACH_END(); if (PQputCopyEnd(pgsql, NULL) != 1) { @@ -4304,22 +4309,23 @@ PHP_FUNCTION(pg_copy_from) } #else ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) { - zval tmp; - ZVAL_COPY(&tmp, value); - convert_to_string_ex(&tmp); - query = (char *)emalloc(Z_STRLEN(tmp) + 2); - strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2); - if(Z_STRLEN(tmp) > 0 && *(query + Z_STRLEN(tmp) - 1) != '\n') { - strlcat(query, "\n", Z_STRLEN(tmp) + 2); + zend_string *tmp = zval_get_string(value); + if (EG(exception)) { + return; + } + query = (char *)emalloc(ZSTR_LEN(tmp) + 2); + strlcpy(query, ZSTR_LVAL(tmp), ZSTR_LEN(tmp) + 2); + if (ZSTR_LEN(tmp) > 0 && *(query + ZSTR_LEN(tmp) - 1) != '\n') { + strlcat(query, "\n", ZSTR_LEN(tmp) + 2); } if (PQputline(pgsql, query)==EOF) { efree(query); - zval_ptr_dtor_str(&tmp); + zend_string_release(tmp); PHP_PQ_ERROR("copy failed: %s", pgsql); RETURN_FALSE; } efree(query); - zval_ptr_dtor_str(&tmp); + zend_string_release(tmp); } ZEND_HASH_FOREACH_END(); if (PQputline(pgsql, "\\.\n") == EOF) { @@ -5167,17 +5173,13 @@ PHP_FUNCTION(pg_send_execute) if (Z_TYPE_P(tmp) == IS_NULL) { params[i] = NULL; } else { - zval tmp_val; - ZVAL_COPY(&tmp_val, tmp); - convert_to_string(&tmp_val); - if (Z_TYPE(tmp_val) != IS_STRING) { - php_error_docref(NULL, E_WARNING,"Error converting parameter"); - zval_ptr_dtor(&tmp_val); + zend_string *tmp_str = zval_get_string(tmp); + if (EG(exception)) { _php_pgsql_free_params(params, num_params); - RETURN_FALSE; + return; } - params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val)); - zval_ptr_dtor(&tmp_val); + params[i] = estrndup(ZSTR_VAL(tmp_str), ZSTR_LEN(tmp_str)); + zend_string_release(tmp_str); } i++; @@ -6103,8 +6105,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con break; case IS_LONG: - ZVAL_LONG(&new_val, Z_LVAL_P(val)); - convert_to_string_ex(&new_val); + ZVAL_STR(&new_val, zend_long_to_str(Z_LVAL_P(val))); break; case IS_DOUBLE: @@ -6412,8 +6413,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con break; case IS_LONG: - ZVAL_LONG(&new_val, Z_LVAL_P(val)); - convert_to_string_ex(&new_val); + ZVAL_STR(&new_val, zend_long_to_str(Z_LVAL_P(val))); break; case IS_DOUBLE: diff --git a/ext/readline/readline.c b/ext/readline/readline.c index b66928ebd6..9c018bf9c8 100644 --- a/ext/readline/readline.c +++ b/ext/readline/readline.c @@ -279,7 +279,9 @@ PHP_FUNCTION(readline_info) oldstr = rl_line_buffer; if (value) { /* XXX if (rl_line_buffer) free(rl_line_buffer); */ - convert_to_string_ex(value); + if (!try_convert_to_string(value)) { + return; + } rl_line_buffer = strdup(Z_STRVAL_P(value)); } RETVAL_STRING(SAFE_STRING(oldstr)); @@ -302,7 +304,9 @@ PHP_FUNCTION(readline_info) } else if (!strcasecmp(what, "pending_input")) { oldval = rl_pending_input; if (value) { - convert_to_string_ex(value); + if (!try_convert_to_string(value)) { + return; + } rl_pending_input = Z_STRVAL_P(value)[0]; } RETVAL_LONG(oldval); @@ -319,7 +323,9 @@ PHP_FUNCTION(readline_info) } else if (!strcasecmp(what, "completion_append_character")) { oldval = rl_completion_append_character; if (value) { - convert_to_string_ex(value) + if (!try_convert_to_string(value)) { + return; + } rl_completion_append_character = (int)Z_STRVAL_P(value)[0]; } RETVAL_INTERNED_STR( @@ -342,7 +348,9 @@ PHP_FUNCTION(readline_info) oldstr = (char*)rl_readline_name; if (value) { /* XXX if (rl_readline_name) free(rl_readline_name); */ - convert_to_string_ex(value); + if (!try_convert_to_string(value)) { + return; + } rl_readline_name = strdup(Z_STRVAL_P(value)); } RETVAL_STRING(SAFE_STRING(oldstr)); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index e6ca3322cb..4623efb3cf 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2263,7 +2263,7 @@ ZEND_METHOD(reflection_parameter, __construct) case IS_ARRAY: { zval *classref; zval *method; - zend_string *lcname; + zend_string *name, *lcname; if (((classref = zend_hash_index_find(Z_ARRVAL_P(reference), 0)) == NULL) || ((method = zend_hash_index_find(Z_ARRVAL_P(reference), 1)) == NULL)) @@ -2275,27 +2275,38 @@ ZEND_METHOD(reflection_parameter, __construct) if (Z_TYPE_P(classref) == IS_OBJECT) { ce = Z_OBJCE_P(classref); } else { - convert_to_string_ex(classref); - if ((ce = zend_lookup_class(Z_STR_P(classref))) == NULL) { + name = zval_get_string(classref); + if (EG(exception)) { + return; + } + if ((ce = zend_lookup_class(name)) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, - "Class %s does not exist", Z_STRVAL_P(classref)); + "Class %s does not exist", ZSTR_VAL(name)); + zend_string_release(name); return; } + zend_string_release(name); + } + + name = zval_get_string(method); + if (EG(exception)) { + return; } - convert_to_string_ex(method); - lcname = zend_string_tolower(Z_STR_P(method)); + lcname = zend_string_tolower(name); if (Z_TYPE_P(classref) == IS_OBJECT && is_closure_invoke(ce, lcname) && (fptr = zend_get_closure_invoke_method(Z_OBJ_P(classref))) != NULL) { /* nothing to do. don't set is_closure since is the invoke handler, not the closure itself */ } else if ((fptr = zend_hash_find_ptr(&ce->function_table, lcname)) == NULL) { + zend_string_release(name); zend_string_release(lcname); zend_throw_exception_ex(reflection_exception_ptr, 0, "Method %s::%s() does not exist", ZSTR_VAL(ce->name), Z_STRVAL_P(method)); return; } + zend_string_release(name); zend_string_release(lcname); } break; @@ -2329,29 +2340,23 @@ ZEND_METHOD(reflection_parameter, __construct) if (Z_TYPE_P(parameter) == IS_LONG) { position= (int)Z_LVAL_P(parameter); if (position < 0 || (uint32_t)position >= num_args) { - if (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { - if (fptr->type != ZEND_OVERLOADED_FUNCTION) { - zend_string_release_ex(fptr->common.function_name, 0); - } - zend_free_trampoline(fptr); - } - if (is_closure) { - zval_ptr_dtor(reference); - } _DO_THROW("The parameter specified by its offset could not be found"); - return; + goto failure; } } else { uint32_t i; - position= -1; - convert_to_string_ex(parameter); + position = -1; + if (!try_convert_to_string(parameter)) { + goto failure; + } + if (fptr->type == ZEND_INTERNAL_FUNCTION && !(fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { for (i = 0; i < num_args; i++) { if (arg_info[i].name) { if (strcmp(((zend_internal_arg_info*)arg_info)[i].name, Z_STRVAL_P(parameter)) == 0) { - position= i; + position = i; break; } @@ -2361,24 +2366,15 @@ ZEND_METHOD(reflection_parameter, __construct) for (i = 0; i < num_args; i++) { if (arg_info[i].name) { if (strcmp(ZSTR_VAL(arg_info[i].name), Z_STRVAL_P(parameter)) == 0) { - position= i; + position = i; break; } } } } if (position == -1) { - if (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { - if (fptr->type != ZEND_OVERLOADED_FUNCTION) { - zend_string_release_ex(fptr->common.function_name, 0); - } - zend_free_trampoline(fptr); - } - if (is_closure) { - zval_ptr_dtor(reference); - } _DO_THROW("The parameter specified by its name could not be found"); - return; + goto failure; } } @@ -2406,6 +2402,18 @@ ZEND_METHOD(reflection_parameter, __construct) } else { ZVAL_NULL(prop_name); } + return; + +failure: + if (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { + if (fptr->type != ZEND_OVERLOADED_FUNCTION) { + zend_string_release_ex(fptr->common.function_name, 0); + } + zend_free_trampoline(fptr); + } + if (is_closure) { + zval_ptr_dtor(reference); + } } /* }}} */ @@ -3692,7 +3700,10 @@ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_ob ZVAL_COPY(&intern->obj, argument); } } else { - convert_to_string_ex(argument); + if (!try_convert_to_string(argument)) { + return; + } + if ((ce = zend_lookup_class(Z_STR_P(argument))) == NULL) { if (!EG(exception)) { zend_throw_exception_ex(reflection_exception_ptr, -1, "Class %s does not exist", Z_STRVAL_P(argument)); diff --git a/ext/reflection/tests/bug74673.phpt b/ext/reflection/tests/bug74673.phpt index 8e4e8e3a18..47f7604e8b 100644 --- a/ext/reflection/tests/bug74673.phpt +++ b/ext/reflection/tests/bug74673.phpt @@ -19,4 +19,9 @@ $class = new ReflectionClass('A'); echo $class; ?> --EXPECTF-- -Fatal error: Method ReflectionClass::__toString() must not throw an exception, caught Exception: in %sbug74673.php on line %d +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 [internal function]: {closure}(2, 'Use of undefine...', %s, %d, Array) +#1 %s(%d): ReflectionClass->__toString() +#2 {main} + thrown in %s on line %d diff --git a/ext/session/session.c b/ext/session/session.c index bf3ddee0d5..671968e8da 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -1752,35 +1752,36 @@ static PHP_FUNCTION(session_set_cookie_params) lifetime = zval_get_string(lifetime_or_options); } + /* Exception during string conversion */ + if (EG(exception)) { + goto cleanup; + } + if (lifetime) { ini_name = zend_string_init("session.cookie_lifetime", sizeof("session.cookie_lifetime") - 1, 0); result = zend_alter_ini_entry(ini_name, lifetime, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); - zend_string_release(lifetime); zend_string_release_ex(ini_name, 0); if (result == FAILURE) { - RETURN_FALSE; + RETVAL_FALSE; + goto cleanup; } } if (path) { ini_name = zend_string_init("session.cookie_path", sizeof("session.cookie_path") - 1, 0); result = zend_alter_ini_entry(ini_name, path, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); - if (found > 0) { - zend_string_release(path); - } zend_string_release_ex(ini_name, 0); if (result == FAILURE) { - RETURN_FALSE; + RETVAL_FALSE; + goto cleanup; } } if (domain) { ini_name = zend_string_init("session.cookie_domain", sizeof("session.cookie_domain") - 1, 0); result = zend_alter_ini_entry(ini_name, domain, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); - if (found > 0) { - zend_string_release(domain); - } zend_string_release_ex(ini_name, 0); if (result == FAILURE) { - RETURN_FALSE; + RETVAL_FALSE; + goto cleanup; } } if (!secure_null) { @@ -1788,7 +1789,8 @@ static PHP_FUNCTION(session_set_cookie_params) result = zend_alter_ini_entry_chars(ini_name, secure ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); zend_string_release_ex(ini_name, 0); if (result == FAILURE) { - RETURN_FALSE; + RETVAL_FALSE; + goto cleanup; } } if (!httponly_null) { @@ -1796,22 +1798,29 @@ static PHP_FUNCTION(session_set_cookie_params) result = zend_alter_ini_entry_chars(ini_name, httponly ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); zend_string_release_ex(ini_name, 0); if (result == FAILURE) { - RETURN_FALSE; + RETVAL_FALSE; + goto cleanup; } } if (samesite) { ini_name = zend_string_init("session.cookie_samesite", sizeof("session.cookie_samesite") - 1, 0); result = zend_alter_ini_entry(ini_name, samesite, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); - if (found > 0) { - zend_string_release(samesite); - } zend_string_release_ex(ini_name, 0); if (result == FAILURE) { - RETURN_FALSE; + RETVAL_FALSE; + goto cleanup; } } - RETURN_TRUE; + RETVAL_TRUE; + +cleanup: + if (lifetime) zend_string_release(lifetime); + if (found > 0) { + if (path) zend_string_release(path); + if (domain) zend_string_release(domain); + if (samesite) zend_string_release(samesite); + } } /* }}} */ @@ -2364,7 +2373,10 @@ static PHP_FUNCTION(session_cache_expire) RETVAL_LONG(PS(cache_expire)); if (expires) { - convert_to_string_ex(expires); + if (!try_convert_to_string(expires)) { + return; + } + ini_name = zend_string_init("session.cache_expire", sizeof("session.cache_expire") - 1, 0); zend_alter_ini_entry(ini_name, Z_STR_P(expires), ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME); zend_string_release_ex(ini_name, 0); diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index fb55b6b195..01c119888e 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -260,6 +260,9 @@ long_dim: if (Z_TYPE_P(member) != IS_STRING) { ZVAL_STR(&tmp_zv, zval_get_string_func(member)); member = &tmp_zv; + if (EG(exception)) { + return &EG(uninitialized_zval); + } } name = Z_STRVAL_P(member); } @@ -455,6 +458,10 @@ long_dim: } else { if (Z_TYPE_P(member) != IS_STRING) { trim_str = zval_get_string_func(member); + if (EG(exception)) { + return &EG(error_zval); + } + ZVAL_STR(&tmp_zv, php_trim(trim_str, NULL, 0, 3)); zend_string_release_ex(trim_str, 0); member = &tmp_zv; @@ -672,10 +679,12 @@ static zval *sxe_property_get_adr(zval *object, zval *member, int fetch_type, vo char *name; SXE_ITER type; - sxe = Z_SXEOBJ_P(object); + if (!try_convert_to_string(member)) { + return NULL; + } + sxe = Z_SXEOBJ_P(object); GET_NODE(sxe, node); - convert_to_string(member); name = Z_STRVAL_P(member); node = sxe_get_element_by_name(sxe, node, &name, &type); if (node) { @@ -713,6 +722,9 @@ static int sxe_prop_dim_exists(zval *object, zval *member, int check_empty, zend if (Z_TYPE_P(member) != IS_STRING && Z_TYPE_P(member) != IS_LONG) { ZVAL_STR(&tmp_zv, zval_get_string_func(member)); member = &tmp_zv; + if (EG(exception)) { + return 0; + } } sxe = Z_SXEOBJ_P(object); @@ -832,6 +844,9 @@ static void sxe_prop_dim_delete(zval *object, zval *member, zend_bool elements, if (Z_TYPE_P(member) != IS_STRING && Z_TYPE_P(member) != IS_LONG) { ZVAL_STR(&tmp_zv, zval_get_string_func(member)); member = &tmp_zv; + if (EG(exception)) { + return; + } } sxe = Z_SXEOBJ_P(object); diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 34e951b6ec..69ff9b00f4 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -1920,6 +1920,9 @@ zval *php_snmp_read_property(zval *object, zval *member, int type, void **cache_ if (Z_TYPE_P(member) != IS_STRING) { ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; + if (EG(exception)) { + return &EG(uninitialized_zval); + } } hnd = zend_hash_find_ptr(&php_snmp_properties, Z_STR_P(member)); @@ -1954,6 +1957,9 @@ zval *php_snmp_write_property(zval *object, zval *member, zval *value, void **ca if (Z_TYPE_P(member) != IS_STRING) { ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; + if (EG(exception)) { + return value; + } } obj = Z_SNMP_P(object); diff --git a/ext/sockets/conversions.c b/ext/sockets/conversions.c index a9f287a8ea..f24e0926ce 100644 --- a/ext/sockets/conversions.c +++ b/ext/sockets/conversions.c @@ -332,7 +332,10 @@ double_case: zend_long lval; double dval; - convert_to_string(&lzval); + if (!try_convert_to_string(&lzval)) { + ctx->err.has_error = 1; + break; + } switch (is_numeric_string(Z_STRVAL(lzval), Z_STRLEN(lzval), &lval, &dval, 0)) { case IS_DOUBLE: diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 697d5f1d91..defb9fdb88 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -1066,23 +1066,18 @@ static void spl_recursive_tree_iterator_get_entry(spl_recursive_it_object *objec { zend_object_iterator *iterator = object->iterators[object->level].iterator; zval *data; - zend_error_handling error_handling; data = iterator->funcs->get_current_data(iterator); - - /* Replace exception handling so the catchable fatal error that is thrown when a class - * without __toString is converted to string is converted into an exception. */ - zend_replace_error_handling(EH_THROW, spl_ce_UnexpectedValueException, &error_handling); if (data) { ZVAL_DEREF(data); + /* TODO: Remove this special case? */ if (Z_TYPE_P(data) == IS_ARRAY) { - ZVAL_STRINGL(return_value, "Array", sizeof("Array")-1); + RETVAL_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED)); } else { ZVAL_COPY(return_value, data); convert_to_string(return_value); } } - zend_restore_error_handling(&error_handling); } static void spl_recursive_tree_iterator_get_postfix(spl_recursive_it_object *object, zval *return_value) @@ -2006,7 +2001,7 @@ SPL_METHOD(RegexIterator, accept) spl_dual_it_object *intern; zend_string *result, *subject; size_t count = 0; - zval zcount, *replacement, tmp_replacement, rv; + zval zcount, rv; pcre2_match_data *match_data; pcre2_code *re; int rc; @@ -2030,6 +2025,11 @@ SPL_METHOD(RegexIterator, accept) subject = zval_get_string(&intern->current.data); } + /* Exception during string conversion. */ + if (EG(exception)) { + return; + } + switch (intern->u.regex.mode) { case REGIT_MODE_MAX: /* won't happen but makes compiler happy */ @@ -2061,14 +2061,14 @@ SPL_METHOD(RegexIterator, accept) RETVAL_BOOL(count > 1); break; - case REGIT_MODE_REPLACE: - replacement = zend_read_property(intern->std.ce, ZEND_THIS, "replacement", sizeof("replacement")-1, 1, &rv); - if (Z_TYPE_P(replacement) != IS_STRING) { - ZVAL_COPY(&tmp_replacement, replacement); - convert_to_string(&tmp_replacement); - replacement = &tmp_replacement; + case REGIT_MODE_REPLACE: { + zval *replacement = zend_read_property(intern->std.ce, ZEND_THIS, "replacement", sizeof("replacement")-1, 1, &rv); + zend_string *replacement_str = zval_get_string(replacement); + if (EG(exception)) { + return; } - result = php_pcre_replace_impl(intern->u.regex.pce, subject, ZSTR_VAL(subject), ZSTR_LEN(subject), Z_STR_P(replacement), -1, &count); + + result = php_pcre_replace_impl(intern->u.regex.pce, subject, ZSTR_VAL(subject), ZSTR_LEN(subject), replacement_str, -1, &count); if (intern->u.regex.flags & REGIT_USE_KEY) { zval_ptr_dtor(&intern->current.key); @@ -2078,10 +2078,9 @@ SPL_METHOD(RegexIterator, accept) ZVAL_STR(&intern->current.data, result); } - if (replacement == &tmp_replacement) { - zval_ptr_dtor(replacement); - } + zend_string_release(replacement_str); RETVAL_BOOL(count > 0); + } } if (intern->u.regex.flags & REGIT_INVERTED) { diff --git a/ext/spl/tests/iterator_036.phpt b/ext/spl/tests/iterator_036.phpt index 74d393b677..07a1f79134 100644 --- a/ext/spl/tests/iterator_036.phpt +++ b/ext/spl/tests/iterator_036.phpt @@ -18,4 +18,9 @@ test(new CachingIterator($ar, 0)); ?> ===DONE=== --EXPECTF-- -Fatal error: Method CachingIterator::__toString() must not throw an exception, caught BadMethodCallException: CachingIterator does not fetch string value (see CachingIterator::__construct) in %siterator_036.php on line %d +Fatal error: Uncaught BadMethodCallException: CachingIterator does not fetch string value (see CachingIterator::__construct) in %s:%d +Stack trace: +#0 %s(%d): CachingIterator->__toString() +#1 %s(%d): test(Object(CachingIterator)) +#2 {main} + thrown in %s on line %d diff --git a/ext/spl/tests/recursive_tree_iterator_007.phpt b/ext/spl/tests/recursive_tree_iterator_007.phpt index 7374a066fa..a017254df0 100644 --- a/ext/spl/tests/recursive_tree_iterator_007.phpt +++ b/ext/spl/tests/recursive_tree_iterator_007.phpt @@ -22,12 +22,12 @@ try { foreach(new RecursiveTreeIterator($it) as $k => $v) { echo "[$k] => $v\n"; } -} catch (UnexpectedValueException $e) { - echo "UnexpectedValueException thrown\n"; +} catch (Error $e) { + echo $e->getMessage(), "\n"; } ?> ===DONE=== --EXPECT-- -UnexpectedValueException thrown +Object of class stdClass could not be converted to string ===DONE=== diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index b993d622ff..c3ffeabc00 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -801,10 +801,16 @@ static int sqlite3_do_callback(struct php_sqlite3_fci *fc, zval *cb, int argc, s sqlite3_result_double(context, Z_DVAL(retval)); break; - default: - convert_to_string_ex(&retval); - sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + default: { + zend_string *str = zval_get_string(&retval); + if (EG(exception)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, ZSTR_VAL(str), ZSTR_LEN(str), SQLITE_TRANSIENT); + zend_string_release(str); break; + } } } else { sqlite3_result_error(context, "failed to invoke callback", 0); @@ -1480,13 +1486,18 @@ static int php_sqlite3_bind_params(php_sqlite3_stmt *stmt_obj) /* {{{ */ break; } - case SQLITE3_TEXT: - convert_to_string(parameter); - return_code = sqlite3_bind_text(stmt_obj->stmt, param->param_number, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC); + case SQLITE3_TEXT: { + zend_string *str = zval_get_string(parameter); + if (EG(exception)) { + return FAILURE; + } + return_code = sqlite3_bind_text(stmt_obj->stmt, param->param_number, ZSTR_VAL(str), ZSTR_LEN(str), SQLITE_TRANSIENT); if (return_code != SQLITE_OK) { php_sqlite3_error(stmt_obj->db_obj, "Unable to bind parameter number " ZEND_LONG_FMT " (%d)", param->param_number, return_code); } + zend_string_release(str); break; + } case SQLITE_NULL: return_code = sqlite3_bind_null(stmt_obj->stmt, param->param_number); @@ -1526,7 +1537,7 @@ PHP_METHOD(sqlite3stmt, getSQL) bind_rc = php_sqlite3_bind_params(stmt_obj); - if (bind_rc == FAILURE) { + if (bind_rc == FAILURE || EG(exception)) { RETURN_FALSE; } @@ -1718,7 +1729,7 @@ PHP_METHOD(sqlite3stmt, execute) /* Bind parameters to the statement */ bind_rc = php_sqlite3_bind_params(stmt_obj); - if (bind_rc == FAILURE) { + if (bind_rc == FAILURE || EG(exception)) { RETURN_FALSE; } diff --git a/ext/sqlite3/tests/exception_from_toString.phpt b/ext/sqlite3/tests/exception_from_toString.phpt new file mode 100644 index 0000000000..1d6ed39f2a --- /dev/null +++ b/ext/sqlite3/tests/exception_from_toString.phpt @@ -0,0 +1,39 @@ +--TEST-- +Check that exceptions from __toString() are handled correctly +--FILE-- +exec('CREATE TABLE t(id int, v varchar(255))'); + +$stmt = $db->prepare('INSERT INTO t VALUES(:i, :v)'); +$stmt->bindValue('i', 1234); +$stmt->bindValue('v', new throws); + +try { + $stmt->execute(); +} catch (Exception $e) { + echo "Exception thrown ...\n"; +} + +try { + $stmt->execute(); +} catch (Exception $e) { + echo "Exception thrown ...\n"; +} + +$query = $db->query("SELECT * FROM t"); +while ($row = $query->fetchArray(SQLITE3_ASSOC)) { + print_r($row); +} + +?> +--EXPECT-- +Exception thrown ... +Exception thrown ... diff --git a/ext/standard/array.c b/ext/standard/array.c index 5448a1815d..71a7cf17e7 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2458,7 +2458,10 @@ PHP_FUNCTION(extract) } if (prefix) { - convert_to_string(prefix); + if (!try_convert_to_string(prefix)) { + return; + } + if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) { php_error_docref(NULL, E_WARNING, "prefix is not a valid identifier"); return; @@ -4134,7 +4137,9 @@ zend_bool array_column_param_helper(zval *param, return 1; case IS_OBJECT: - convert_to_string_ex(param); + if (!try_convert_to_string(param)) { + return 0; + } /* fallthrough */ case IS_STRING: return 1; diff --git a/ext/standard/assert.c b/ext/standard/assert.c index eb1b59db74..6e21af6dd5 100644 --- a/ext/standard/assert.c +++ b/ext/standard/assert.c @@ -300,6 +300,10 @@ PHP_FUNCTION(assert_options) oldint = ASSERTG(active); if (ac == 2) { zend_string *value_str = zval_get_string(value); + if (EG(exception)) { + return; + } + key = zend_string_init("assert.active", sizeof("assert.active")-1, 0); zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); zend_string_release_ex(key, 0); @@ -312,6 +316,10 @@ PHP_FUNCTION(assert_options) oldint = ASSERTG(bail); if (ac == 2) { zend_string *value_str = zval_get_string(value); + if (EG(exception)) { + return; + } + key = zend_string_init("assert.bail", sizeof("assert.bail")-1, 0); zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); zend_string_release_ex(key, 0); @@ -324,6 +332,10 @@ PHP_FUNCTION(assert_options) oldint = ASSERTG(quiet_eval); if (ac == 2) { zend_string *value_str = zval_get_string(value); + if (EG(exception)) { + return; + } + key = zend_string_init("assert.quiet_eval", sizeof("assert.quiet_eval")-1, 0); zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); zend_string_release_ex(key, 0); @@ -336,6 +348,10 @@ PHP_FUNCTION(assert_options) oldint = ASSERTG(warning); if (ac == 2) { zend_string *value_str = zval_get_string(value); + if (EG(exception)) { + return; + } + key = zend_string_init("assert.warning", sizeof("assert.warning")-1, 0); zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); zend_string_release_ex(key, 0); @@ -361,8 +377,12 @@ PHP_FUNCTION(assert_options) case ASSERT_EXCEPTION: oldint = ASSERTG(exception); if (ac == 2) { - zend_string *key = zend_string_init("assert.exception", sizeof("assert.exception")-1, 0); zend_string *val = zval_get_string(value); + if (EG(exception)) { + return; + } + + key = zend_string_init("assert.exception", sizeof("assert.exception")-1, 0); zend_alter_ini_entry_ex(key, val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); zend_string_release_ex(val, 0); zend_string_release_ex(key, 0); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 920974fe48..8d4dec48c1 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -5357,7 +5357,10 @@ PHP_FUNCTION(highlight_string) Z_PARAM_OPTIONAL Z_PARAM_BOOL(i) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - convert_to_string_ex(expr); + + if (!try_convert_to_string(expr)) { + return; + } if (i) { php_output_start_default(); diff --git a/ext/standard/filters.c b/ext/standard/filters.c index 9d90558f59..83fcf2dcbf 100644 --- a/ext/standard/filters.c +++ b/ext/standard/filters.c @@ -250,8 +250,6 @@ static php_stream_filter *strfilter_strip_tags_create(const char *filtername, zv php_error_docref(NULL, E_DEPRECATED, "The string.strip_tags filter is deprecated"); - inst = pemalloc(sizeof(php_strip_tags_filter), persistent); - if (filterparams != NULL) { if (Z_TYPE_P(filterparams) == IS_ARRAY) { smart_str tags_ss = {0}; @@ -268,8 +266,17 @@ static php_stream_filter *strfilter_strip_tags_create(const char *filtername, zv } else { allowed_tags = zval_get_string(filterparams); } + + /* Exception during string conversion. */ + if (EG(exception)) { + if (allowed_tags) { + zend_string_release(allowed_tags); + } + return NULL; + } } + inst = pemalloc(sizeof(php_strip_tags_filter), persistent); if (php_strip_tags_filter_ctor(inst, allowed_tags, persistent) == SUCCESS) { filter = php_stream_filter_alloc(&strfilter_strip_tags_ops, inst, persistent); } else { diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index b804cda500..4ee68adefa 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -400,7 +400,10 @@ php_formatted_print(zval *z_format, zval *args, int argc) int always_sign; size_t format_len; - convert_to_string_ex(z_format); + if (!try_convert_to_string(z_format)) { + return NULL; + } + format = Z_STRVAL_P(z_format); format_len = Z_STRLEN_P(z_format); result = zend_string_alloc(size, 0); diff --git a/ext/standard/head.c b/ext/standard/head.c index e8b5d5b171..f1b2fa4e8b 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -258,10 +258,12 @@ PHP_FUNCTION(setcookie) } } - if (php_setcookie(name, value, expires, path, domain, secure, httponly, samesite, 1) == SUCCESS) { - RETVAL_TRUE; - } else { - RETVAL_FALSE; + if (!EG(exception)) { + if (php_setcookie(name, value, expires, path, domain, secure, httponly, samesite, 1) == SUCCESS) { + RETVAL_TRUE; + } else { + RETVAL_FALSE; + } } if (expires_or_options && Z_TYPE_P(expires_or_options) == IS_ARRAY) { @@ -311,10 +313,12 @@ PHP_FUNCTION(setrawcookie) } } - if (php_setcookie(name, value, expires, path, domain, secure, httponly, samesite, 0) == SUCCESS) { - RETVAL_TRUE; - } else { - RETVAL_FALSE; + if (!EG(exception)) { + if (php_setcookie(name, value, expires, path, domain, secure, httponly, samesite, 0) == SUCCESS) { + RETVAL_TRUE; + } else { + RETVAL_FALSE; + } } if (expires_or_options && Z_TYPE_P(expires_or_options) == IS_ARRAY) { diff --git a/ext/standard/math.c b/ext/standard/math.c index cefcd38769..840df9103e 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -1090,7 +1090,10 @@ PHP_FUNCTION(base_convert) Z_PARAM_LONG(frombase) Z_PARAM_LONG(tobase) ZEND_PARSE_PARAMETERS_END(); - convert_to_string_ex(number); + + if (!try_convert_to_string(number)) { + return; + } if (frombase < 2 || frombase > 36) { php_error_docref(NULL, E_WARNING, "Invalid `from base' (" ZEND_LONG_FMT ")", frombase); diff --git a/ext/standard/pack.c b/ext/standard/pack.c index 5f375585d4..3c0dc009f1 100644 --- a/ext/standard/pack.c +++ b/ext/standard/pack.c @@ -297,7 +297,12 @@ PHP_FUNCTION(pack) } if (arg < 0) { - convert_to_string(&argv[currentarg]); + if (!try_convert_to_string(&argv[currentarg])) { + efree(formatcodes); + efree(formatargs); + return; + } + arg = Z_STRLEN(argv[currentarg]); if (code == 'Z') { /* add one because Z is always NUL-terminated: diff --git a/ext/standard/password.c b/ext/standard/password.c index 3f1ff6dd55..52ff1518e6 100644 --- a/ext/standard/password.c +++ b/ext/standard/password.c @@ -140,6 +140,9 @@ static zend_string* php_password_get_salt(zval *unused_, size_t required_salt_le case IS_DOUBLE: case IS_OBJECT: buffer = zval_get_string(option_buffer); + if (EG(exception)) { + return NULL; + } break; case IS_FALSE: case IS_TRUE: diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index 326cfc1431..d76e1595f3 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -552,7 +552,9 @@ PHP_FUNCTION(proc_open) } else { if ((ztype = zend_hash_index_find(Z_ARRVAL_P(descitem), 0)) != NULL) { - convert_to_string_ex(ztype); + if (!try_convert_to_string(ztype)) { + goto exit_fail; + } } else { php_error_docref(NULL, E_WARNING, "Missing handle qualifier in array"); goto exit_fail; @@ -563,7 +565,9 @@ PHP_FUNCTION(proc_open) zval *zmode; if ((zmode = zend_hash_index_find(Z_ARRVAL_P(descitem), 1)) != NULL) { - convert_to_string_ex(zmode); + if (!try_convert_to_string(zmode)) { + goto exit_fail; + } } else { php_error_docref(NULL, E_WARNING, "Missing mode parameter for 'pipe'"); goto exit_fail; @@ -602,14 +606,18 @@ PHP_FUNCTION(proc_open) descriptors[ndesc].mode = DESC_FILE; if ((zfile = zend_hash_index_find(Z_ARRVAL_P(descitem), 1)) != NULL) { - convert_to_string_ex(zfile); + if (!try_convert_to_string(zfile)) { + goto exit_fail; + } } else { php_error_docref(NULL, E_WARNING, "Missing file name parameter for 'file'"); goto exit_fail; } if ((zmode = zend_hash_index_find(Z_ARRVAL_P(descitem), 2)) != NULL) { - convert_to_string_ex(zmode); + if (!try_convert_to_string(zmode)) { + goto exit_fail; + } } else { php_error_docref(NULL, E_WARNING, "Missing mode parameter for 'file'"); goto exit_fail; diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 4fa705bed9..24d8357d7f 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -1576,7 +1576,9 @@ PHP_FUNCTION(stream_is_local) } wrapper = stream->wrapper; } else { - convert_to_string_ex(zstream); + if (!try_convert_to_string(zstream)) { + return; + } wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0); } diff --git a/ext/standard/string.c b/ext/standard/string.c index e0141ffaea..a7aa02fcbd 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -2494,6 +2494,10 @@ PHP_FUNCTION(substr_replace) convert_to_long_ex(from); } + if (EG(exception)) { + return; + } + if (argc > 3) { if (Z_TYPE_P(len) != IS_ARRAY) { convert_to_long_ex(len); @@ -3518,7 +3522,9 @@ PHP_FUNCTION(strtr) php_strtr_array(return_value, str, pats); } } else { - convert_to_string_ex(from); + if (!try_convert_to_string(from)) { + return; + } RETURN_STR(php_strtr_ex(str, Z_STRVAL_P(from), @@ -4438,6 +4444,10 @@ static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, int case_sensit convert_to_string_ex(replace); } + if (EG(exception)) { + return; + } + /* if subject is an array */ if (Z_TYPE_P(subject) == IS_ARRAY) { array_init(return_value); @@ -4840,6 +4850,9 @@ PHP_FUNCTION(setlocale) } loc = zval_get_string(plocale); + if (EG(exception)) { + return; + } if (!strcmp("0", ZSTR_VAL(loc))) { zend_string_release_ex(loc, 0); diff --git a/ext/standard/tests/array/array_multisort_variation8.phpt b/ext/standard/tests/array/array_multisort_variation8.phpt index 6d89dd0c6c..00b0ccb012 100644 --- a/ext/standard/tests/array/array_multisort_variation8.phpt +++ b/ext/standard/tests/array/array_multisort_variation8.phpt @@ -34,7 +34,6 @@ $inputs = array( 'empty string DQ' => "", 'string DQ' => "string", 'instance of classWithToString' => new classWithToString(), - 'instance of classWithoutToString' => new classWithoutToString(), 'undefined var' => @$undefined_var, ); @@ -46,14 +45,11 @@ var_dump($inputs); --EXPECT-- *** Testing array_multisort() : usage variation - test sort order of all types*** bool(true) -array(10) { +array(9) { ["uppercase NULL"]=> NULL ["empty string DQ"]=> string(0) "" - ["instance of classWithoutToString"]=> - object(classWithoutToString)#2 (0) { - } ["undefined var"]=> NULL ["float -10.5"]=> diff --git a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt index 16a728e088..dd852ef61f 100644 --- a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt +++ b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt @@ -76,10 +76,9 @@ $values = array( // loop through each element of the array for class foreach($values as $value) { - echo "\nArg value $value \n"; + echo "\nArg value " . (is_object($value) ? get_class($value) : $value) . " \n"; var_dump( get_class_methods($value) ); }; - echo "Done"; ?> --EXPECTF-- @@ -163,9 +162,8 @@ NULL Arg value string NULL -Error: 4096 - Object of class stdClass could not be converted to string, %s(76) -Arg value +Arg value stdClass array(0) { } diff --git a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt index 8dde525ae8..0ad5756837 100644 --- a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt +++ b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt @@ -77,7 +77,7 @@ $values = array( // loop through each element of the array for object foreach($values as $value) { - echo "\nArg value $value \n"; + echo "\nArg value " . (is_object($value) ? get_class($value) : $value) . " \n"; var_dump( get_parent_class($value) ); }; @@ -166,9 +166,8 @@ bool(false) Arg value String In autoload(String) bool(false) -Error: 4096 - Object of class stdClass could not be converted to string, %s(77) -Arg value +Arg value stdClass bool(false) Arg value diff --git a/ext/standard/tests/general_functions/type.phpt b/ext/standard/tests/general_functions/type.phpt index 90bfb45f28..0b460b08d1 100644 --- a/ext/standard/tests/general_functions/type.phpt +++ b/ext/standard/tests/general_functions/type.phpt @@ -47,7 +47,11 @@ foreach ($array as $var) { foreach ($types as $type) { foreach ($array as $var) { - var_dump(settype($var, $type)); + try { + var_dump(settype($var, $type)); + } catch (Error $e) { + echo "Error: ", $e->getMessage(), "\n"; + } var_dump($var); } } @@ -344,7 +348,6 @@ bool(true) string(14) "Resource id #%d" bool(true) string(14) "Resource id #%d" -string(57) "Object of class stdClass could not be converted to string" -bool(true) -string(6) "Object" +Error: Object of class stdClass could not be converted to string +string(0) "" Done diff --git a/ext/standard/tests/math/base_convert_error.phpt b/ext/standard/tests/math/base_convert_error.phpt index f27d0a66cf..96e774b51f 100644 --- a/ext/standard/tests/math/base_convert_error.phpt +++ b/ext/standard/tests/math/base_convert_error.phpt @@ -22,7 +22,11 @@ base_convert(1234, 1, 10); base_convert(1234, 10, 37); echo "Incorrect input\n"; -base_convert(new classA(), 8, 10); +try { + base_convert(new classA(), 8, 10); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} ?> --EXPECTF-- @@ -39,5 +43,4 @@ Warning: base_convert(): Invalid `from base' (1) in %s on line %d Warning: base_convert(): Invalid `to base' (37) in %s on line %d Incorrect input - -Recoverable fatal error: Object of class classA could not be converted to string in %s on line %d +Object of class classA could not be converted to string diff --git a/ext/standard/tests/streams/bug61115.phpt b/ext/standard/tests/streams/bug61115.phpt index 5cfc9c2ac3..3caffde232 100644 --- a/ext/standard/tests/streams/bug61115.phpt +++ b/ext/standard/tests/streams/bug61115.phpt @@ -7,7 +7,11 @@ $arrayLarge = array_fill(0, 113663, '*'); $resourceFileTemp = fopen('php://temp', 'r+'); stream_context_set_params($resourceFileTemp, array()); -preg_replace('', function() {}, $resourceFileTemp); +try { + preg_replace('', function() {}, $resourceFileTemp); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} ?> --EXPECTF-- -Recoverable fatal error: Object of class Closure could not be converted to string in %s on line %d +Object of class Closure could not be converted to string diff --git a/ext/standard/tests/strings/strval_error.phpt b/ext/standard/tests/strings/strval_error.phpt index 37ecfd14d5..4e1ece6016 100644 --- a/ext/standard/tests/strings/strval_error.phpt +++ b/ext/standard/tests/strings/strval_error.phpt @@ -29,7 +29,11 @@ var_dump( strval() ); // Testing strval with a object which has no toString() method echo "\n-- Testing strval() function with object which has not toString() method --\n"; -var_dump( strval(new MyClass()) ); +try { + var_dump( strval(new MyClass()) ); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} ?> ===DONE=== @@ -47,5 +51,5 @@ Warning: strval() expects exactly 1 parameter, 0 given in %s on line %d NULL -- Testing strval() function with object which has not toString() method -- - -Recoverable fatal error: Object of class MyClass could not be converted to string in %s on line %d +Object of class MyClass could not be converted to string +===DONE=== diff --git a/ext/standard/var.c b/ext/standard/var.c index ca0273c213..410c0fdeb9 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -1222,6 +1222,13 @@ PHP_FUNCTION(unserialize) zend_hash_add_empty_element(class_hash, lcname); zend_string_release_ex(lcname, 0); } ZEND_HASH_FOREACH_END(); + + /* Exception during string conversion. */ + if (EG(exception)) { + zend_hash_destroy(class_hash); + FREE_HASHTABLE(class_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + } } php_var_unserialize_set_allowed_classes(var_hash, class_hash); } diff --git a/ext/xml/xml.c b/ext/xml/xml.c index f60f08bd57..fb60883226 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -1629,7 +1629,10 @@ PHP_FUNCTION(xml_parser_set_option) break; case PHP_XML_OPTION_TARGET_ENCODING: { const xml_encoding *enc; - convert_to_string_ex(val); + if (!try_convert_to_string(val)) { + return; + } + enc = xml_get_encoding((XML_Char*)Z_STRVAL_P(val)); if (enc == NULL) { php_error_docref(NULL, E_WARNING, "Unsupported target encoding \"%s\"", Z_STRVAL_P(val)); diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index 3ce3fed0e1..b3b202af39 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -124,6 +124,9 @@ zval *xmlreader_get_property_ptr_ptr(zval *object, zval *member, int type, void if (Z_TYPE_P(member) != IS_STRING) { ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; + if (EG(exception)) { + return NULL; + } } obj = Z_XMLREADER_P(object); @@ -155,6 +158,9 @@ zval *xmlreader_read_property(zval *object, zval *member, int type, void **cache if (Z_TYPE_P(member) != IS_STRING) { ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; + if (EG(exception)) { + return &EG(uninitialized_zval); + } } obj = Z_XMLREADER_P(object); @@ -190,6 +196,9 @@ zval *xmlreader_write_property(zval *object, zval *member, zval *value, void **c if (Z_TYPE_P(member) != IS_STRING) { ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; + if (EG(exception)) { + return value; + } } obj = Z_XMLREADER_P(object); diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c index 8d9ff87611..51dc647b19 100644 --- a/ext/xmlrpc/xmlrpc-epi-php.c +++ b/ext/xmlrpc/xmlrpc-epi-php.c @@ -522,7 +522,9 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep } break; case xmlrpc_datetime: - convert_to_string(&val); + if (!try_convert_to_string(&val)) { + return NULL; + } xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL(val)); break; case xmlrpc_boolean: @@ -538,7 +540,9 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL(val)); break; case xmlrpc_string: - convert_to_string(&val); + if (!try_convert_to_string(&val)) { + return NULL; + } xReturn = XMLRPC_CreateValueString(key, Z_STRVAL(val), Z_STRLEN(val)); break; case xmlrpc_vector: @@ -925,7 +929,10 @@ static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) STRUCT_XMLRPC_ERROR err = {0}; /* return value should be a string */ - convert_to_string(&retval); + if (!try_convert_to_string(&retval)) { + zend_string_release_ex(php_function_name, 0); + break; + } xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL(retval), &err); diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c index 18443f9efa..919006041a 100644 --- a/ext/xsl/xsltprocessor.c +++ b/ext/xsl/xsltprocessor.c @@ -150,7 +150,10 @@ static char **php_xsl_xslt_make_params(HashTable *parht, int xpath_params) return NULL; } else { if (Z_TYPE_P(value) != IS_STRING) { - convert_to_string(value); + if (!try_convert_to_string(value)) { + efree(params); + return NULL; + } } if (!xpath_params) { @@ -753,13 +756,16 @@ PHP_FUNCTION(xsl_xsltprocessor_set_parameter) if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "sa", &namespace, &namespace_len, &array_value) == SUCCESS) { intern = Z_XSL_P(id); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(array_value), string_key, entry) { + zval tmp; if (string_key == NULL) { php_error_docref(NULL, E_WARNING, "Invalid parameter array"); RETURN_FALSE; } - convert_to_string_ex(entry); - Z_TRY_ADDREF_P(entry); - zend_hash_update(intern->parameter, string_key, entry); + ZVAL_STR(&tmp, zval_get_string(entry)); + if (EG(exception)) { + return; + } + zend_hash_update(intern->parameter, string_key, &tmp); } ZEND_HASH_FOREACH_END(); RETURN_TRUE; } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "sSS", &namespace, &namespace_len, &name, &value) == SUCCESS) { @@ -841,9 +847,13 @@ PHP_FUNCTION(xsl_xsltprocessor_register_php_functions) intern = Z_XSL_P(id); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array_value), entry) { - convert_to_string_ex(entry); - ZVAL_LONG(&new_string ,1); - zend_hash_update(intern->registered_phpfunctions, Z_STR_P(entry), &new_string); + zend_string *str = zval_get_string(entry); + if (EG(exception)) { + return; + } + ZVAL_LONG(&new_string, 1); + zend_hash_update(intern->registered_phpfunctions, str, &new_string); + zend_string_release(str); } ZEND_HASH_FOREACH_END(); intern->registerPhpFunctions = 2; diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index df3eade995..c2b41309c0 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -876,6 +876,9 @@ static zval *php_zip_get_property_ptr_ptr(zval *object, zval *member, int type, ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; cache_slot = NULL; + if (EG(exception)) { + return NULL; + } } obj = Z_ZIP_P(object); @@ -907,6 +910,9 @@ static zval *php_zip_read_property(zval *object, zval *member, int type, void ** ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; cache_slot = NULL; + if (EG(exception)) { + return &EG(uninitialized_zval); + } } obj = Z_ZIP_P(object); @@ -943,6 +949,9 @@ static int php_zip_has_property(zval *object, zval *member, int type, void **cac ZVAL_STR(&tmp_member, zval_get_string_func(member)); member = &tmp_member; cache_slot = NULL; + if (EG(exception)) { + return 0; + } } obj = Z_ZIP_P(object); diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index f3bda6398f..9060dcb2e2 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -777,7 +777,7 @@ static zend_bool zlib_create_dictionary_string(HashTable *options, char **dict, size_t i; *++ptr = zval_get_string(cur); - if (!*ptr || ZSTR_LEN(*ptr) == 0) { + if (!*ptr || ZSTR_LEN(*ptr) == 0 || EG(exception)) { if (*ptr) { efree(*ptr); } @@ -785,7 +785,9 @@ static zend_bool zlib_create_dictionary_string(HashTable *options, char **dict, efree(ptr); } efree(strings); - php_error_docref(NULL, E_WARNING, "dictionary entries must be non-empty strings"); + if (!EG(exception)) { + php_error_docref(NULL, E_WARNING, "dictionary entries must be non-empty strings"); + } return 0; } for (i = 0; i < ZSTR_LEN(*ptr); i++) { diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 73b71863a4..bdd9b1b48e 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -669,7 +669,10 @@ static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count) } if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { - convert_to_string(&retval); + if (!try_convert_to_string(&retval)) { + return -1; + } + didread = Z_STRLEN(retval); if (didread > count) { php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " read, " ZEND_LONG_FMT " max) - excess data will be lost", diff --git a/tests/classes/tostring_001.phpt b/tests/classes/tostring_001.phpt index 53144ca207..418aa1fc25 100644 --- a/tests/classes/tostring_001.phpt +++ b/tests/classes/tostring_001.phpt @@ -3,12 +3,6 @@ ZE2 __toString() --FILE-- getMessage(), "\n"; +} var_dump($o); echo "====test2====\n"; @@ -70,7 +68,11 @@ echo sprintf("%s", $o); echo "====test10====\n"; $o = new test3; var_dump($o); -echo $o; +try { + echo $o; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} ?> ====DONE==== @@ -79,9 +81,8 @@ echo $o; test1 Object ( ) -string(54) "Object of class test1 could not be converted to string" -string(0) "" -object(test1)#%d (0) { +Object of class test1 could not be converted to string +object(test1)#1 (0) { } ====test2==== test2 Object @@ -89,7 +90,7 @@ test2 Object ) test2::__toString() Converted -object(test2)#%d (0) { +object(test2)#3 (0) { } ====test3==== test2::__toString() @@ -113,7 +114,8 @@ test2::__toString() Converted ====test7==== test2::__toString() -string(19) "Illegal offset type" + +Warning: Illegal offset type in %s on line %d ====test8==== test2::__toString() string(9) "Converted" @@ -123,8 +125,8 @@ string(9) "Converted" test2::__toString() Converted ====test10==== -object(test3)#%d (0) { +object(test3)#1 (0) { } test3::__toString() -string(53) "Method test3::__toString() must return a string value" +Method test3::__toString() must return a string value ====DONE==== diff --git a/tests/classes/tostring_003.phpt b/tests/classes/tostring_003.phpt index e3bc7f8f2c..4dad51f2cf 100644 --- a/tests/classes/tostring_003.phpt +++ b/tests/classes/tostring_003.phpt @@ -29,5 +29,6 @@ catch(Exception $e) ?> ====DONE==== ---EXPECTF-- -Fatal error: Method Test::__toString() must not throw an exception, caught Exception: Damn! in %stostring_003.php on line %d +--EXPECT-- +string(5) "Damn!" +====DONE==== diff --git a/tests/classes/tostring_004.phpt b/tests/classes/tostring_004.phpt index 907f7bc306..4134c702b6 100644 --- a/tests/classes/tostring_004.phpt +++ b/tests/classes/tostring_004.phpt @@ -12,12 +12,19 @@ error_reporting(8191); echo "Object with no __toString():\n"; $obj = new stdClass; echo "Try 1:\n"; -printf($obj); +try { + printf($obj); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} printf("\n"); echo "\nTry 2:\n"; -printf($obj . "\n"); - +try { + printf($obj . "\n"); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} echo "\n\nObject with bad __toString():\n"; class badToString { @@ -25,30 +32,38 @@ class badToString { return 0; } } + $obj = new badToString; echo "Try 1:\n"; -printf($obj); +try { + printf($obj); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} printf("\n"); echo "\nTry 2:\n"; -printf($obj . "\n"); +try { + printf($obj . "\n"); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} ?> --EXPECT-- Object with no __toString(): Try 1: -Error: 4096 - Object of class stdClass could not be converted to string -Object +Object of class stdClass could not be converted to string -Try 2: -Error: 4096 - Object of class stdClass could not be converted to string +Try 2: +Object of class stdClass could not be converted to string Object with bad __toString(): Try 1: -Error: 4096 - Method badToString::__toString() must return a string value +Method badToString::__toString() must return a string value Try 2: -Error: 4096 - Method badToString::__toString() must return a string value +Method badToString::__toString() must return a string value diff --git a/win32/codepage.c b/win32/codepage.c index 86a4c33007..48f60595b4 100644 --- a/win32/codepage.c +++ b/win32/codepage.c @@ -657,7 +657,9 @@ PHP_FUNCTION(sapi_windows_cp_conv) RETURN_NULL(); } } else { - convert_to_string(z_in_cp); + if (!try_convert_to_string(z_in_cp)) { + return; + } in_cp = php_win32_cp_get_by_enc(Z_STRVAL_P(z_in_cp)); if (!in_cp) { @@ -678,7 +680,9 @@ PHP_FUNCTION(sapi_windows_cp_conv) RETURN_NULL(); } } else { - convert_to_string(z_out_cp); + if (!try_convert_to_string(z_out_cp)) { + return; + } out_cp = php_win32_cp_get_by_enc(Z_STRVAL_P(z_out_cp)); if (!out_cp) { -- 2.40.0