Skip to content

Commit b1df1b7

Browse files
committed
Fix type checks during unserialization
1 parent dbf7d82 commit b1df1b7

File tree

2 files changed

+50
-27
lines changed

2 files changed

+50
-27
lines changed

ext/standard/tests/serialize/typed_property_refs.phpt

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,56 @@ class C {
1818
public string $b;
1919
}
2020

21+
class D {
22+
public int $a;
23+
public float $b;
24+
}
25+
2126
var_dump(unserialize('O:1:"A":2:{s:1:"a";i:1;s:1:"b";R:2;}'));
2227
var_dump(unserialize('O:1:"B":2:{s:1:"a";i:1;s:1:"b";R:2;}'));
23-
var_dump(unserialize('O:1:"A":2:{s:1:"a";N;s:1:"b";R:2;}'));
24-
var_dump(unserialize('O:1:"B":2:{s:1:"a";N;s:1:"b";R:2;}'));
25-
var_dump(unserialize('O:1:"C":2:{s:1:"a";i:1;s:1:"b";R:2;}'));
26-
var_dump(unserialize('O:1:"C":2:{s:1:"b";s:1:"x";s:1:"a";R:2;}'));
28+
29+
try {
30+
var_dump(unserialize('O:1:"A":2:{s:1:"a";N;s:1:"b";R:2;}'));
31+
} catch (TypeError $e) {
32+
echo $e->getMessage(), "\n";
33+
}
34+
try {
35+
var_dump(unserialize('O:1:"B":2:{s:1:"a";N;s:1:"b";R:2;}'));
36+
} catch (TypeError $e) {
37+
echo $e->getMessage(), "\n";
38+
}
39+
try {
40+
var_dump(unserialize('O:1:"C":2:{s:1:"a";i:1;s:1:"b";R:2;}'));
41+
} catch (TypeError $e) {
42+
echo $e->getMessage(), "\n";
43+
}
44+
try {
45+
var_dump(unserialize('O:1:"C":2:{s:1:"b";s:1:"x";s:1:"a";R:2;}'));
46+
} catch (TypeError $e) {
47+
echo $e->getMessage(), "\n";
48+
}
49+
try {
50+
var_dump(unserialize('O:1:"D":2:{s:1:"a";i:1;s:1:"b";R:2;}'));
51+
} catch (TypeError $e) {
52+
echo $e->getMessage(), "\n";
53+
}
2754

2855
?>
29-
--EXPECTF--
56+
--EXPECT--
3057
object(A)#1 (2) {
3158
["a"]=>
3259
&int(1)
3360
["b"]=>
3461
&int(1)
3562
}
36-
37-
Notice: unserialize(): Error at offset 35 of 36 bytes in %s on line %d
38-
bool(false)
39-
40-
Notice: unserialize(): Error at offset 21 of 34 bytes in %s on line %d
41-
bool(false)
42-
43-
Notice: unserialize(): Error at offset 33 of 34 bytes in %s on line %d
44-
bool(false)
45-
46-
Notice: unserialize(): Error at offset 35 of 36 bytes in %s on line %d
47-
bool(false)
48-
49-
Notice: unserialize(): Error at offset 39 of 40 bytes in %s on line %d
50-
bool(false)
63+
object(B)#1 (2) {
64+
["a"]=>
65+
&int(1)
66+
["b"]=>
67+
&int(1)
68+
}
69+
Typed property A::$a must be int, null used
70+
Typed property B::$b must be int, null used
71+
Typed property C::$b must be string, int used
72+
Typed property C::$a must be int, string used
73+
Reference with value of type int held by property D::$a of type int is not compatible with property D::$b of type float

ext/standard/var_unserializer.re

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key);
404404
static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_class_entry *ce)
405405
{
406406
while (elements-- > 0) {
407-
zval key, *data, d, *old_data, tmp;
407+
zval key, *data, d, *old_data;
408408
zend_ulong idx;
409409
zend_property_info *info = NULL;
410410

@@ -553,15 +553,15 @@ string_key:
553553
}
554554

555555
if (UNEXPECTED(info)) {
556-
// TODO Throw an error instead?
557-
// TODO Handle references correctly
558-
ZVAL_COPY_VALUE(&tmp, data);
559-
if (UNEXPECTED(!zend_verify_property_type(info, &tmp, 1))) {
556+
if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) {
557+
zval_ptr_dtor(data);
558+
ZVAL_UNDEF(data);
560559
zval_dtor(&key);
561560
return 0;
562561
}
563-
data = &tmp;
564-
ZVAL_COPY(old_data, data);
562+
if (Z_ISREF_P(data)) {
563+
ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info);
564+
}
565565
}
566566

567567
if (BG(unserialize).level > 1) {

0 commit comments

Comments
 (0)