Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ PHP NEWS
file fails after it was already truncated. (Ilia Alshanetsky)

- Standard:
. Fixed __unserialize() and __wakeup() being called for objects with a missing
closing brace. (Ilia Alshanetsky)
. Fixed bug GH-24399 (usleep() and time_nanosleep() ignore queued APCs on
Windows). (Nicolas Grekas)
. Fixed chown() and lchown() failing to resolve user names in ZTS builds
Expand Down
3 changes: 0 additions & 3 deletions ext/date/tests/unserialize-test.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ bool(false)

ossfuzz-55599.txt:
%s: unserialize(): Error at offset 26 of 26 bytes in %sunserialize-test.php on line %d
Error: Invalid serialization data for DateTime object
bool(false)


Expand All @@ -42,6 +41,4 @@ bool(false)

ossfuzz-56931.txt:
%s: unserialize(): Error at offset 39 of 39 bytes in %sunserialize-test.php on line %d

Deprecated: Creation of dynamic property DateInterval::$ is deprecated in %s on line %d
bool(false)
38 changes: 38 additions & 0 deletions ext/standard/tests/serialize/__unserialize_malformed.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
__unserialize() and destructors are not called for incomplete objects
--FILE--
<?php
class Test {
public function __unserialize(array $data): void {
echo "__unserialize: ";
var_dump($data);
}

public function __destruct() {
echo "__destruct\n";
}
}

foreach ([
'empty' => 'O:4:"Test":0:{',
'nonempty' => 'O:4:"Test":1:{i:0;i:42;',
'nested' => 'a:1:{i:0;O:4:"Test":0:{',
] as $name => $data) {
echo "$name: ";
var_dump(@unserialize($data));
}

echo "valid: ";
var_dump(unserialize('O:4:"Test":1:{i:0;i:42;}'));
?>
--EXPECT--
empty: bool(false)
nonempty: bool(false)
nested: bool(false)
valid: __unserialize: array(1) {
[0]=>
int(42)
}
object(Test)#1 (0) {
}
__destruct
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
Destructor of a nested object in a failed __unserialize() payload cannot reach freed slots
--FILE--
<?php
class T {
public function __unserialize(array $data): void {}
}

class D {
public function __destruct() {
var_dump(unserialize('R:2;'));
}
}

foreach ([
'missing brace' => 'O:1:"T":2:{i:0;s:5:"hello";i:1;O:1:"D":0:{}',
'malformed element' => 'O:1:"T":3:{i:0;s:5:"hello";i:1;O:1:"D":0:{}i:2;X',
] as $name => $data) {
echo "$name: ";
var_dump(@unserialize($data));
}
?>
--EXPECT--
missing brace: bool(false)
bool(false)
malformed element: bool(false)
bool(false)
38 changes: 38 additions & 0 deletions ext/standard/tests/serialize/__wakeup_malformed.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
__wakeup() and destructors are not called for incomplete objects
--FILE--
<?php
class Test {
public int $value = 0;

public function __wakeup(): void {
echo "__wakeup: $this->value\n";
}

public function __destruct() {
echo "__destruct\n";
}
}

foreach ([
'empty' => 'O:4:"Test":0:{',
'nonempty' => 'O:4:"Test":1:{s:5:"value";i:42;',
'nested' => 'a:1:{i:0;O:4:"Test":0:{',
] as $name => $data) {
echo "$name: ";
var_dump(@unserialize($data));
}

echo "valid: ";
var_dump(unserialize('O:4:"Test":1:{s:5:"value";i:42;}'));
?>
--EXPECT--
empty: bool(false)
nonempty: bool(false)
nested: bool(false)
valid: __wakeup: 42
object(Test)#1 (1) {
["value"]=>
int(42)
}
__destruct
12 changes: 7 additions & 5 deletions ext/standard/var_unserializer.re
Original file line number Diff line number Diff line change
Expand Up @@ -854,10 +854,11 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool
if (UNEXPECTED(!array_finit_size_mixed(&ary, elements))) {
return 0;
}
if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements)) {
if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements)
|| !finish_nested_data(UNSERIALIZE_PASSTHRU)) {
ZVAL_DEREF(rval);
GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
zval_ptr_dtor(&ary);
ZVAL_COPY_VALUE(var_tmp_var(var_hash), &ary);
return 0;
}

Expand All @@ -870,7 +871,7 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool
tmp++;
ZVAL_COPY_VALUE(tmp, &ary);

return finish_nested_data(UNSERIALIZE_PASSTHRU);
return 1;
}

has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY
Expand All @@ -884,7 +885,8 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool
if (UNEXPECTED(zend_hash_fextend(ht, zend_hash_num_elements(ht) + elements, HT_IS_PACKED(ht)) == FAILURE)) {
return 0;
}
if (!process_nested_object_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJ_P(rval))) {
if (!process_nested_object_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJ_P(rval))
|| !finish_nested_data(UNSERIALIZE_PASSTHRU)) {
if (has_wakeup) {
ZVAL_DEREF(rval);
GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
Expand All @@ -900,7 +902,7 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool
Z_EXTRA_P(wakeup_var) = VAR_WAKEUP_FLAG;
}

return finish_nested_data(UNSERIALIZE_PASSTHRU);
return 1;
}
#ifdef PHP_WIN32
# pragma optimize("", on)
Expand Down
Loading