From cdd131f6f302979fadbc14a250e729a04ad73937 Mon Sep 17 00:00:00 2001 From: lazerg Date: Sun, 8 Nov 2026 09:16:32 +0500 Subject: [PATCH 1/2] Fix GH-25299: Exception string code is lost after unserialize() --- Zend/tests/gh25299.phpt | 23 +++++++++++++++++++++++ Zend/zend_exceptions.c | 8 ++++---- 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/gh25299.phpt diff --git a/Zend/tests/gh25299.phpt b/Zend/tests/gh25299.phpt new file mode 100644 index 000000000000..b1fe486b0f58 --- /dev/null +++ b/Zend/tests/gh25299.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-25299 (Exception string code is lost after unserialize()) +--CREDITS-- +tacman +--FILE-- +getCode()); + +$e = unserialize('O:9:"Exception":1:{s:7:"' . "\0*\0" . 'code";a:0:{}}'); +var_dump($e->getCode()); + +?> +--EXPECTF-- +string(5) "HY000" + +Warning: Undefined property: Exception::$code in %s on line %d +NULL diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 6138e302bb5d..d42939796d01 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -347,9 +347,9 @@ ZEND_METHOD(Exception, __construct) /* }}} */ /* {{{ Exception unserialize checks */ -#define CHECK_EXC_TYPE(id, type) \ +#define CHECK_EXC_TYPE(id, type_mask) \ pvalue = zend_read_property_ex(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_KNOWN(id), 1, &value); \ - if (Z_TYPE_P(pvalue) != IS_NULL && Z_TYPE_P(pvalue) != type) { \ + if (Z_TYPE_P(pvalue) != IS_NULL && !((type_mask) & (1u << Z_TYPE_P(pvalue)))) { \ zend_unset_property(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_VAL(ZSTR_KNOWN(id)), ZSTR_LEN(ZSTR_KNOWN(id))); \ } @@ -359,8 +359,8 @@ ZEND_METHOD(Exception, __wakeup) zval value, *pvalue; zval *object = ZEND_THIS; - CHECK_EXC_TYPE(ZEND_STR_MESSAGE, IS_STRING); - CHECK_EXC_TYPE(ZEND_STR_CODE, IS_LONG); + CHECK_EXC_TYPE(ZEND_STR_MESSAGE, MAY_BE_STRING); + CHECK_EXC_TYPE(ZEND_STR_CODE, MAY_BE_LONG | MAY_BE_STRING); /* The type of all other properties is enforced through typed properties. */ } /* }}} */ From 39aaf239450775bc61f6c381f27f5ae6496f56e1 Mon Sep 17 00:00:00 2001 From: lazerg Date: Sun, 8 Nov 2026 11:31:01 +0500 Subject: [PATCH 2/2] Dereference exception message and code before the __wakeup type check --- Zend/tests/gh25299_ref.phpt | 22 ++++++++++++++++++++++ Zend/zend_exceptions.c | 1 + 2 files changed, 23 insertions(+) create mode 100644 Zend/tests/gh25299_ref.phpt diff --git a/Zend/tests/gh25299_ref.phpt b/Zend/tests/gh25299_ref.phpt new file mode 100644 index 000000000000..c4e0d84a821c --- /dev/null +++ b/Zend/tests/gh25299_ref.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-25299: Exception::__wakeup() keeps message and code held by reference +--FILE-- +message = &$this->m; + $this->code = &$this->c; + } +} + +$e = unserialize(serialize(new MyException())); +var_dump($e->getMessage(), $e->getCode()); + +?> +--EXPECT-- +string(1) "m" +int(5) diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index d42939796d01..e6ec1bf90ee5 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -349,6 +349,7 @@ ZEND_METHOD(Exception, __construct) /* {{{ Exception unserialize checks */ #define CHECK_EXC_TYPE(id, type_mask) \ pvalue = zend_read_property_ex(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_KNOWN(id), 1, &value); \ + ZVAL_DEREF(pvalue); \ if (Z_TYPE_P(pvalue) != IS_NULL && !((type_mask) & (1u << Z_TYPE_P(pvalue)))) { \ zend_unset_property(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_VAL(ZSTR_KNOWN(id)), ZSTR_LEN(ZSTR_KNOWN(id))); \ }