From aaae77f7f113d4a0a684e2615ef798703ccd089e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 28 May 2020 12:23:37 +0200 Subject: [PATCH] Make Exception::$trace typed array property This is a private property, so we are allowed to add a type. The new declaration of the property is: private array $trace = []; This ensures that Exception::getTrace() does indeed return an array. Userland code that was modifying the property through refleciton may have to be adjusted to assign an array (instead of null, for example). Closes GH-5636. --- Zend/zend_API.c | 14 ++++------- Zend/zend_exceptions.c | 17 ++++++++++---- ext/standard/tests/serialize/bug69152.phpt | 6 +++-- ext/standard/tests/serialize/bug70963.phpt | 27 ++++------------------ sapi/cli/tests/005.phpt | 4 ++-- 5 files changed, 28 insertions(+), 40 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index ba5cb9c747a..241cba8151b 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3586,20 +3586,14 @@ ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name Z_PROP_FLAG_P(property_default_ptr) = Z_ISUNDEF_P(property) ? IS_PROP_UNINIT : 0; } if (ce->type & ZEND_INTERNAL_CLASS) { - switch(Z_TYPE_P(property)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error_noreturn(E_CORE_ERROR, "Internal zval's can't be arrays, objects or resources"); - break; - default: - break; - } - /* Must be interned to avoid ZTS data races */ if (is_persistent_class(ce)) { name = zend_new_interned_string(zend_string_copy(name)); } + + if (Z_REFCOUNTED_P(property)) { + zend_error_noreturn(E_CORE_ERROR, "Internal zvals cannot be refcounted"); + } } if (access_type & ZEND_ACC_PUBLIC) { diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 06c1c9897c2..21775a26ca2 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -606,10 +606,12 @@ ZEND_METHOD(Exception, getTraceAsString) base_ce = i_get_exception_base(object); trace = zend_read_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv); - if (Z_TYPE_P(trace) != IS_ARRAY) { - zend_type_error("Trace is not an array"); - return; + if (EG(exception)) { + RETURN_THROWS(); } + + /* Type should be guaranteed by property type. */ + ZEND_ASSERT(Z_TYPE_P(trace) == IS_ARRAY); ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) { if (Z_TYPE_P(frame) != IS_ARRAY) { zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index); @@ -736,12 +738,19 @@ ZEND_METHOD(Exception, __toString) static void declare_exception_properties(zend_class_entry *ce) { + zval val; + zend_declare_property_string(ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED); zend_declare_property_string(ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE); zend_declare_property_long(ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED); zend_declare_property_null(ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED); zend_declare_property_null(ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED); - zend_declare_property_null(ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE); + + ZVAL_EMPTY_ARRAY(&val); + zend_declare_typed_property( + ce, ZSTR_KNOWN(ZEND_STR_TRACE), &val, ZEND_ACC_PRIVATE, NULL, + (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_declare_property_null(ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE); } diff --git a/ext/standard/tests/serialize/bug69152.phpt b/ext/standard/tests/serialize/bug69152.phpt index b11591ffc21..f7f3b9ac8d3 100644 --- a/ext/standard/tests/serialize/bug69152.phpt +++ b/ext/standard/tests/serialize/bug69152.phpt @@ -9,6 +9,8 @@ $x->test(); ?> --EXPECTF-- -Fatal error: Uncaught TypeError: Trace is not an array in %s:%d -%a +Fatal error: Uncaught TypeError: Cannot assign string to property Exception::$trace of type array in %s:%d +Stack trace: +#0 %s(%d): unserialize('O:9:"exception"...') +#1 {main} thrown in %s on line %d diff --git a/ext/standard/tests/serialize/bug70963.phpt b/ext/standard/tests/serialize/bug70963.phpt index c4e2267b043..63f5845ae0d 100644 --- a/ext/standard/tests/serialize/bug70963.phpt +++ b/ext/standard/tests/serialize/bug70963.phpt @@ -6,25 +6,8 @@ var_dump(unserialize('a:2:{i:0;O:9:"exception":1:{s:16:"'."\0".'Exception'."\0". var_dump(unserialize('a:2:{i:0;O:9:"exception":1:{s:16:"'."\0".'Exception'."\0".'trace";s:4:"test";}i:1;r:3;}')); ?> --EXPECTF-- -array(2) { - [0]=> - object(Exception)#%d (6) { - ["message":protected]=> - string(0) "" - ["string":"Exception":private]=> - string(0) "" - ["code":protected]=> - int(0) - ["file":protected]=> - string(%d) "%s" - ["line":protected]=> - int(2) - ["previous":"Exception":private]=> - NULL - } - [1]=> - string(4) "test" -} - -Notice: unserialize(): Error at offset %d of %d bytes in %sbug70963.php on line 3 -bool(false) +Fatal error: Uncaught TypeError: Cannot assign string to property Exception::$trace of type array in %s:%d +Stack trace: +#0 %s(%d): unserialize('a:2:{i:0;O:9:"e...') +#1 {main} + thrown in %s on line %d diff --git a/sapi/cli/tests/005.phpt b/sapi/cli/tests/005.phpt index 530b6cf29b8..c037889c41c 100644 --- a/sapi/cli/tests/005.phpt +++ b/sapi/cli/tests/005.phpt @@ -37,7 +37,7 @@ string(183) "Class [ class stdClass ] { } " -string(2159) "Class [ class Exception implements Throwable, Stringable ] { +string(2166) "Class [ class Exception implements Throwable, Stringable ] { - Constants [0] { } @@ -54,7 +54,7 @@ string(2159) "Class [ class Exception implements Throwable, Stri Property [ protected $code = 0 ] Property [ protected $file = NULL ] Property [ protected $line = NULL ] - Property [ private $trace = NULL ] + Property [ private array $trace = Array ] Property [ private $previous = NULL ] }