diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2019-02-26 15:32:18 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2019-06-05 14:25:07 +0200 |
commit | a31f46421d7bf6f55dd9ac5876b8e2eacf7e0708 (patch) | |
tree | 24ffd7c5ae5e321c3994048fdd0fd9f68ae7457c | |
parent | 528aa7932a839fc6319979c34aa372805d8dc41c (diff) | |
download | php-git-a31f46421d7bf6f55dd9ac5876b8e2eacf7e0708.tar.gz |
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.
113 files changed, 1389 insertions, 473 deletions
@@ -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-- <?php -function my_error_handler($errno, $errstr, $errfile, $errline) { - var_dump($errstr); -} - -set_error_handler('my_error_handler'); - class ObjectOne { public $x; @@ -17,6 +11,10 @@ class ObjectOne { $this->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-- <?php -function my_error_handler($errno, $errstr, $errfile, $errline) { - var_dump($errstr); -} - -set_error_handler('my_error_handler'); - class a { - public $a = 4; - function __call($a,$b) { - return "unknown method"; - } + public $a = 4; + function __call($name, $args) { + echo __METHOD__, "\n"; + } } $b = new a; -echo $b,"\n"; +var_dump($b); $c = unserialize(serialize($b)); -echo $c,"\n"; var_dump($c); ?> --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-- <?php register_shutdown_function(function(){echo("\n\n!!!shutdown!!!\n\n");}); -set_error_handler(function($errno, $errstr, $errfile, $errline){throw new Exception("Foo");}); class Bad { public function __toString() { - throw new Exception('Oops, I cannot do this'); + throw new Exception('I CAN DO THIS'); } } $bad = new Bad(); echo "$bad"; --EXPECTF-- -Fatal error: Method Bad::__toString() must not throw an exception, caught Exception: Oops, I cannot do this in %sbug60909_2.php on line %d +Fatal error: Uncaught Exception: I CAN DO THIS in %s:%d +Stack trace: +#0 %s(%d): Bad->__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-- <?php -function my_errorhandler($errno,$errormsg) { - global $my_var; - $my_var=0x12345; - echo $errormsg."\n"; - return true; +class Test { + public function __toString() { + global $my_var; + $my_var=0x12345; + return ""; + } } -$oldhandler = set_error_handler("my_errorhandler"); + $my_var = str_repeat("A",64); -$data = call_user_func_array("substr_replace",array(&$my_var, new StdClass(),1)); +$data = call_user_func_array("substr_replace",array(&$my_var, new Test(), 1)); echo "OK!"; +?> --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-- <?php -set_error_handler('myErrorHandler', E_RECOVERABLE_ERROR); -function myErrorHandler($errno, $errstr, $errfile, $errline) { - echo "Error: $errstr at $errfile($errline)\n"; - return true; -} + $x = function() { return 1; }; -print (string) $x; -print "\n"; -print $x; -print "\n"; -?> ---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-- +<?php + +class BadStr { + public function __toString() { + throw new Exception("Exception"); + } +} + +$str = "a"; +$num = 42; +$badStr = new BadStr; + +try { $x = $str . $badStr; } +catch (Exception $e) { echo $e->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-- <?php -function my_errorhandler($errno,$errormsg) { - global $my_var; - $my_var = 0; - return true; +class Test { + public function __toString() { + global $my_var; + $my_var = 0; + return ","; + } } -set_error_handler("my_errorhandler"); $my_var = str_repeat("A",64); -$data = call_user_func_array("explode",array(new StdClass(), &$my_var)); +$data = call_user_func_array("explode",array(new Test(), &$my_var)); $my_var=array(1,2,3); -$data = call_user_func_array("implode",array(&$my_var, new StdClass())); +$data = call_user_func_array("implode",array(&$my_var, new Test())); echo "Done.\n"; ?> --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-- +<?php + +class BadStr { + public function __toString() { + throw new Exception("Exception"); + } +} + +$badStr = new BadStr; + +$doc = new DOMDocument(); +$doc->loadXML( + '<root xmlns:ns="foo"><node attr="foo" /><node>Text</node><ns:node/><?pi foobar?></root>'); + +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 +<?xml version="1.0"?> +<root xmlns:ns="foo"><node attr="foo"/><node>Text</node><ns:node/><?pi foobar?></root> 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-- +<?php if (!extension_loaded('pdo_sqlite')) print 'skip not loaded'; ?> +--FILE-- +<?php + +class throws { + function __toString() { + throw new Exception("Sorry"); + } +} + +$db = new PDO('sqlite::memory:'); +$db->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-- +<?php + +class throws { + function __toString() { + throw new Exception("Sorry"); + } +} + +$db = new sqlite3(':memory:'); +$db->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-- <?php -function my_error_handler($errno, $errstr, $errfile, $errline) { - var_dump($errstr); -} - -set_error_handler('my_error_handler'); - class test1 { } @@ -33,7 +27,11 @@ class test3 echo "====test1====\n"; $o = new test1; print_r($o); -var_dump((string)$o); +try { + var_dump((string)$o); +} catch (Error $e) { + echo $e->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) { |