Skip to content

Commit c9186f7

Browse files
committed
Zend: Implement __unserialize() for Exception/Error
1 parent b46aafa commit c9186f7

File tree

9 files changed

+165
-33
lines changed

9 files changed

+165
-33
lines changed

Zend/tests/serialize/bug70121.phpt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ OK
88
--EXPECTF--
99
Fatal error: Uncaught TypeError: Cannot assign stdClass to property Exception::$previous of type ?Throwable in %s:%d
1010
Stack trace:
11-
#0 %s(%d): unserialize('O:12:"DateInter...')
12-
#1 {main}
11+
#0 [internal function]: Exception->__unserialize(Array)
12+
#1 %s(%d): unserialize('O:12:"DateInter...')
13+
#2 {main}
1314
thrown in %s on line %d

Zend/zend_exceptions.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,95 @@ ZEND_METHOD(Exception, __wakeup)
396396
}
397397
/* }}} */
398398

399+
ZEND_METHOD(Exception, __unserialize)
400+
{
401+
HashTable *ht;
402+
403+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &ht) == FAILURE) {
404+
RETURN_THROWS();
405+
}
406+
407+
/* Fake strict_types as zend_update_property_ex() would coerce values compared to unserialize() */
408+
EG(current_execute_data)->func->common.fn_flags |= ZEND_ACC_STRICT_TYPES;
409+
410+
zend_string *key = NULL;
411+
zval *tmp;
412+
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, tmp) {
413+
if (UNEXPECTED(key == NULL)) {
414+
zend_throw_error(NULL, "Must have a string key");
415+
RETURN_THROWS();
416+
}
417+
if (ZSTR_VAL(key)[0] == '\0') {
418+
if (zend_string_equals_literal(key, "\0*\0message")) {
419+
if (Z_TYPE_P(tmp) != IS_STRING) {
420+
zend_type_error("Cannot assign %s to property %s::$message of type string",
421+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
422+
RETURN_THROWS();
423+
}
424+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_MESSAGE_OFF, ZSTR_KNOWN(ZEND_STR_MESSAGE), tmp);
425+
zval_add_ref(tmp);
426+
if (UNEXPECTED(EG(exception))) {
427+
RETURN_THROWS();
428+
}
429+
continue;
430+
}
431+
if (zend_string_equals_literal(key, "\0*\0code")) {
432+
if (Z_TYPE_P(tmp) != IS_LONG) {
433+
zend_type_error("Cannot assign %s to property %s::$code of type int",
434+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
435+
RETURN_THROWS();
436+
}
437+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_CODE_OFF, ZSTR_KNOWN(ZEND_STR_CODE), tmp);
438+
if (UNEXPECTED(EG(exception))) {
439+
RETURN_THROWS();
440+
}
441+
continue;
442+
}
443+
if (zend_string_equals_literal(key, "\0Exception\0previous") || zend_string_equals_literal(key, "\0Error\0previous")) {
444+
if (Z_TYPE_P(tmp) != IS_NULL && !instanceof_function(Z_OBJCE_P(tmp), zend_ce_throwable)) {
445+
zend_type_error("Cannot assign %s to property %s::$previous of type ?Throwable",
446+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
447+
RETURN_THROWS();
448+
}
449+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_PREVIOUS_OFF, ZSTR_KNOWN(ZEND_STR_PREVIOUS), tmp);
450+
zval_add_ref(tmp);
451+
if (UNEXPECTED(EG(exception))) {
452+
RETURN_THROWS();
453+
}
454+
continue;
455+
}
456+
if (zend_string_equals_literal(key, "\0Exception\0trace") || zend_string_equals_literal(key, "\0Error\0trace")) {
457+
if (Z_TYPE_P(tmp) != IS_ARRAY) {
458+
zend_type_error("Cannot assign %s to property %s::$trace of type array",
459+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
460+
RETURN_THROWS();
461+
}
462+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_TRACE_OFF, ZSTR_KNOWN(ZEND_STR_TRACE), tmp);
463+
zval_add_ref(tmp);
464+
if (UNEXPECTED(EG(exception))) {
465+
RETURN_THROWS();
466+
}
467+
continue;
468+
}
469+
if (zend_string_starts_with_cstr(key, ZEND_STRL("\0*\0"))) {
470+
const char *name = ZSTR_VAL(key) + sizeof("\0*\0")-1;
471+
zend_update_property(NULL, Z_OBJ_P(ZEND_THIS), name, strlen(name), tmp);
472+
if (UNEXPECTED(EG(exception))) {
473+
RETURN_THROWS();
474+
}
475+
continue;
476+
}
477+
// TODO: other private props?
478+
} else {
479+
zend_update_property_ex(NULL, Z_OBJ_P(ZEND_THIS), key, tmp);
480+
if (UNEXPECTED(EG(exception))) {
481+
RETURN_THROWS();
482+
}
483+
}
484+
} ZEND_HASH_FOREACH_END();
485+
}
486+
/* }}} */
487+
399488
/* {{{ ErrorException constructor */
400489
ZEND_METHOD(ErrorException, __construct)
401490
{

Zend/zend_exceptions.stub.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public function __construct(string $message = "", int $code = 0, ?Throwable $pre
4747
/** @tentative-return-type */
4848
public function __wakeup(): void {}
4949

50+
public function __unserialize(array $data): void {}
51+
5052
final public function getMessage(): string {}
5153

5254
/** @return int */
@@ -111,6 +113,11 @@ public function __construct(string $message = "", int $code = 0, ?Throwable $pre
111113
*/
112114
public function __wakeup(): void {}
113115

116+
/**
117+
* @implementation-alias Exception::__unserialize
118+
*/
119+
public function __unserialize(array $data): void {}
120+
114121
/** @implementation-alias Exception::getMessage */
115122
final public function getMessage(): string {}
116123

Zend/zend_exceptions_arginfo.h

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/soap/tests/bugs/bug73452.phpt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ echo unserialize($data);
1313
--EXPECTF--
1414
Fatal error: Uncaught TypeError: Cannot assign %s to property SoapFault::$faultcode of type ?string in %s:%d
1515
Stack trace:
16-
#0 %sbug73452.php(4): unserialize('O:9:"SoapFault"...')
17-
#1 {main}
16+
#0 [internal function]: Exception->__unserialize(Array)
17+
#1 %s(4): unserialize('O:9:"SoapFault"...')
18+
#2 {main}
1819
thrown in %s on line %d

ext/standard/tests/serialize/bug69152.phpt

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,25 @@
22
Bug #69152: Type Confusion Infoleak Vulnerability in unserialize()
33
--FILE--
44
<?php
5-
$x = unserialize('O:9:"exception":1:{s:16:"'."\0".'Exception'."\0".'trace";s:4:"ryat";}');
6-
echo $x;
7-
$x = unserialize('O:4:"test":1:{s:27:"__PHP_Incomplete_Class_Name";R:1;}');
8-
$x->test();
5+
try {
6+
$x = unserialize('O:9:"exception":1:{s:16:"'."\0".'Exception'."\0".'trace";s:4:"ryat";}');
7+
var_dump($x);
8+
} catch (Throwable $e) {
9+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
10+
}
11+
try {
12+
$x = unserialize('O:4:"test":1:{s:27:"__PHP_Incomplete_Class_Name";R:1;}');
13+
var_dump($x);
14+
$x->test();
15+
} catch (Throwable $e) {
16+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
17+
}
918

1019
?>
11-
--EXPECTF--
12-
Fatal error: Uncaught TypeError: Cannot assign string to property Exception::$trace of type array in %s:%d
13-
Stack trace:
14-
#0 %s(%d): unserialize('O:9:"exception"...')
15-
#1 {main}
16-
thrown in %s on line %d
20+
--EXPECT--
21+
TypeError: Cannot assign string to property Exception::$trace of type array
22+
object(__PHP_Incomplete_Class)#1 (1) {
23+
["__PHP_Incomplete_Class_Name"]=>
24+
*RECURSION*
25+
}
26+
Error: The script tried to call a method on an incomplete object. Please ensure that the class definition "unknown" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide an autoloader to load the class definition

ext/standard/tests/serialize/bug69793.phpt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
Bug #69793: Remotely triggerable stack exhaustion via recursive method calls
33
--FILE--
44
<?php
5-
$e = unserialize('O:9:"Exception":7:{s:17:"'."\0".'Exception'."\0".'string";s:1:"a";s:7:"'."\0".'*'."\0".'code";i:0;s:7:"'."\0".'*'."\0".'file";s:0:"";s:7:"'."\0".'*'."\0".'line";i:1337;s:16:"'."\0".'Exception'."\0".'trace";a:0:{}s:19:"'."\0".'Exception'."\0".'previous";i:10;s:10:"'."\0".'*'."\0".'message";N;}');
6-
7-
var_dump($e."");
5+
try {
6+
$e = unserialize('O:9:"Exception":7:{s:17:"'."\0".'Exception'."\0".'string";s:1:"a";s:7:"'."\0".'*'."\0".'code";i:0;s:7:"'."\0".'*'."\0".'file";s:0:"";s:7:"'."\0".'*'."\0".'line";i:1337;s:16:"'."\0".'Exception'."\0".'trace";a:0:{}s:19:"'."\0".'Exception'."\0".'previous";i:10;s:10:"'."\0".'*'."\0".'message";N;}');
7+
var_dump($e);
8+
var_dump($e."");
9+
} catch (Throwable $e) {
10+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
11+
}
812
?>
9-
--EXPECTF--
10-
Fatal error: Uncaught TypeError: Cannot assign int to property Exception::$previous of type ?Throwable in %s:%d
11-
Stack trace:
12-
#0 %s(%d): unserialize('O:9:"Exception"...')
13-
#1 {main}
14-
thrown in %s on line %d
13+
--EXPECT--
14+
TypeError: Cannot assign null to property Exception::$message of type string

ext/standard/tests/serialize/bug70963.phpt

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22
Bug #70963 (Unserialize shows UNKNOW in result)
33
--FILE--
44
<?php
5-
var_dump(unserialize('a:2:{i:0;O:9:"exception":1:{s:16:"'."\0".'Exception'."\0".'trace";s:4:"test";}i:1;R:3;}'));
6-
var_dump(unserialize('a:2:{i:0;O:9:"exception":1:{s:16:"'."\0".'Exception'."\0".'trace";s:4:"test";}i:1;r:3;}'));
5+
try {
6+
var_dump(unserialize('a:2:{i:0;O:9:"exception":1:{s:16:"'."\0".'Exception'."\0".'trace";s:4:"test";}i:1;R:3;}'));
7+
} catch (Throwable $e) {
8+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
9+
}
10+
try {
11+
var_dump(unserialize('a:2:{i:0;O:9:"exception":1:{s:16:"'."\0".'Exception'."\0".'trace";s:4:"test";}i:1;r:3;}'));
12+
} catch (Throwable $e) {
13+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
14+
}
715
?>
816
--EXPECTF--
9-
Fatal error: Uncaught TypeError: Cannot assign string to property Exception::$trace of type array in %s:%d
10-
Stack trace:
11-
#0 %s(%d): unserialize('a:2:{i:0;O:9:"e...')
12-
#1 {main}
13-
thrown in %s on line %d
17+
TypeError: Cannot assign string to property Exception::$trace of type array
18+
19+
Warning: unserialize(): Error at offset 72 of 73 bytes in %s on line %d
20+
TypeError: Cannot assign string to property Exception::$trace of type array

sapi/cli/tests/005.phpt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ string(183) "Class [ <internal:Core> class stdClass ] {
3737
}
3838

3939
"
40-
string(2232) "Class [ <internal:Core> class Exception implements Stringable, Throwable ] {
40+
string(2406) "Class [ <internal:Core> class Exception implements Stringable, Throwable ] {
4141

4242
- Constants [0] {
4343
}
@@ -58,7 +58,7 @@ string(2232) "Class [ <internal:Core> class Exception implements Stringable, Thr
5858
Property [ private ?Throwable $previous = NULL ]
5959
}
6060

61-
- Methods [11] {
61+
- Methods [12] {
6262
Method [ <internal:Core> private method __clone ] {
6363

6464
- Parameters [0] {
@@ -82,6 +82,14 @@ string(2232) "Class [ <internal:Core> class Exception implements Stringable, Thr
8282
- Tentative return [ void ]
8383
}
8484

85+
Method [ <internal:Core> public method __unserialize ] {
86+
87+
- Parameters [1] {
88+
Parameter #0 [ <required> array $data ]
89+
}
90+
- Return [ void ]
91+
}
92+
8593
Method [ <internal:Core, prototype Throwable> final public method getMessage ] {
8694

8795
- Parameters [0] {

0 commit comments

Comments
 (0)