Skip to content

Commit 125c1ff

Browse files
committed
Zend: Implement __unserialize() for Exception/Error
1 parent ca3a3d7 commit 125c1ff

File tree

6 files changed

+160
-27
lines changed

6 files changed

+160
-27
lines changed

Zend/zend_exceptions.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,106 @@ 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+
zval *tmp = zend_hash_str_find(ht, ZEND_STRL("\0*\0message"));
408+
if (tmp) {
409+
if (Z_TYPE_P(tmp) != IS_STRING) {
410+
zend_type_error("Cannot assign %s to property %s::$message of type string",
411+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
412+
RETURN_THROWS();
413+
}
414+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_MESSAGE_OFF, ZSTR_KNOWN(ZEND_STR_MESSAGE), tmp);
415+
zval_add_ref(tmp);
416+
if (UNEXPECTED(EG(exception))) {
417+
RETURN_THROWS();
418+
}
419+
}
420+
421+
tmp = zend_hash_str_find(ht, ZEND_STRL("\0*\0code"));
422+
if (tmp) {
423+
if (Z_TYPE_P(tmp) != IS_LONG) {
424+
zend_type_error("Cannot assign %s to property %s::$code of type int",
425+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
426+
RETURN_THROWS();
427+
}
428+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_CODE_OFF, ZSTR_KNOWN(ZEND_STR_CODE), tmp);
429+
if (UNEXPECTED(EG(exception))) {
430+
RETURN_THROWS();
431+
}
432+
}
433+
434+
tmp = zend_hash_str_find(ht, ZEND_STRL("\0*\0file"));
435+
if (tmp) {
436+
if (Z_TYPE_P(tmp) != IS_STRING) {
437+
zend_type_error("Cannot assign %s to property %s::$file of type string",
438+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
439+
RETURN_THROWS();
440+
}
441+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_FILE_OFF, ZSTR_KNOWN(ZEND_STR_FILE), tmp);
442+
zval_add_ref(tmp);
443+
if (UNEXPECTED(EG(exception))) {
444+
RETURN_THROWS();
445+
}
446+
}
447+
448+
tmp = zend_hash_str_find(ht, ZEND_STRL("\0*\0line"));
449+
if (tmp) {
450+
if (Z_TYPE_P(tmp) != IS_LONG) {
451+
zend_type_error("Cannot assign %s to property %s::$line of type int",
452+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
453+
RETURN_THROWS();
454+
}
455+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_LINE_OFF, ZSTR_KNOWN(ZEND_STR_LINE), tmp);
456+
if (UNEXPECTED(EG(exception))) {
457+
RETURN_THROWS();
458+
}
459+
}
460+
461+
if (instanceof_function(Z_OBJCE_P(ZEND_THIS), zend_ce_exception)) {
462+
tmp = zend_hash_str_find(ht, ZEND_STRL("\0Exception\0trace"));
463+
} else {
464+
tmp = zend_hash_str_find(ht, ZEND_STRL("\0Error\0trace"));
465+
}
466+
if (tmp) {
467+
if (Z_TYPE_P(tmp) != IS_ARRAY) {
468+
zend_type_error("Cannot assign %s to property %s::$trace of type array",
469+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
470+
RETURN_THROWS();
471+
}
472+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_TRACE_OFF, ZSTR_KNOWN(ZEND_STR_TRACE), tmp);
473+
zval_add_ref(tmp);
474+
if (UNEXPECTED(EG(exception))) {
475+
RETURN_THROWS();
476+
}
477+
}
478+
479+
if (instanceof_function(Z_OBJCE_P(ZEND_THIS), zend_ce_exception)) {
480+
tmp = zend_hash_str_find(ht, ZEND_STRL("\0Exception\0previous"));
481+
} else {
482+
tmp = zend_hash_str_find(ht, ZEND_STRL("\0Error\0previous"));
483+
}
484+
if (tmp) {
485+
if (Z_TYPE_P(tmp) != IS_NULL && !instanceof_function(Z_OBJCE_P(tmp), zend_ce_throwable)) {
486+
zend_type_error("Cannot assign %s to property %s::$previous of type ?Throwable",
487+
zend_zval_type_name(tmp), ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
488+
RETURN_THROWS();
489+
}
490+
zend_update_property_num_checked(NULL, Z_OBJ_P(ZEND_THIS), ZEND_EXCEPTION_PREVIOUS_OFF, ZSTR_KNOWN(ZEND_STR_PREVIOUS), tmp);
491+
zval_add_ref(tmp);
492+
if (UNEXPECTED(EG(exception))) {
493+
RETURN_THROWS();
494+
}
495+
}
496+
}
497+
/* }}} */
498+
399499
/* {{{ ErrorException constructor */
400500
ZEND_METHOD(ErrorException, __construct)
401501
{

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/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

0 commit comments

Comments
 (0)