Skip to content

Commit 3f041a3

Browse files
Fix GH-14402: Add support for serialization in SplPriorityQueue, SplMinHeap and SplMaxHeap
1 parent bec66ee commit 3f041a3

File tree

3 files changed

+130
-32
lines changed

3 files changed

+130
-32
lines changed

ext/spl/spl_heap.c

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,13 +1091,18 @@ static void spl_heap_serialize_properties(zval *return_value, spl_heap_object *i
10911091
{
10921092
HashTable *props = zend_std_get_properties(&intern->std);
10931093

1094-
ZVAL_ARR(return_value, props ? zend_array_dup(props) : zend_new_array(0));
1094+
ZVAL_ARR(return_value, props);
1095+
if (props) {
1096+
GC_ADDREF(props);
1097+
} else {
1098+
ZVAL_ARR(return_value, zend_new_array(0));
1099+
}
10951100
}
10961101

10971102
static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_object *intern, bool is_pqueue)
10981103
{
1099-
zval heap_elements;
1100-
int heap_count = intern->heap->count;
1104+
zval heap_elements;
1105+
int heap_count = intern->heap->count;
11011106

11021107
array_init(return_value);
11031108
add_assoc_long(return_value, "flags", intern->flags);
@@ -1113,9 +1118,9 @@ static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_objec
11131118
spl_pqueue_elem *elem = spl_heap_elem(intern->heap, heap_idx);
11141119
zval entry;
11151120
array_init(&entry);
1116-
add_assoc_zval_ex(&entry, "data", sizeof("data") - 1, &elem->data);
1121+
add_assoc_zval_ex(&entry, "data", strlen("data"), &elem->data);
11171122
Z_TRY_ADDREF(elem->data);
1118-
add_assoc_zval_ex(&entry, "priority", sizeof("priority") - 1, &elem->priority);
1123+
add_assoc_zval_ex(&entry, "priority", strlen("priority"), &elem->priority);
11191124
Z_TRY_ADDREF(elem->priority);
11201125
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), &entry);
11211126
} else {
@@ -1135,42 +1140,51 @@ static void spl_heap_unserialize_properties(HashTable *props_ht, spl_heap_object
11351140
return;
11361141
}
11371142

1138-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1143+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11391144
}
11401145

11411146
static void spl_heap_unserialize_internal_state(HashTable *state_ht, spl_heap_object *intern, zval *this_ptr, bool is_pqueue)
11421147
{
1143-
zval *flags_val = zend_hash_str_find(state_ht, "flags", sizeof("flags") - 1);
1148+
zval *flags_val = zend_hash_str_find(state_ht, "flags", strlen("flags"));
11441149
if (!flags_val || Z_TYPE_P(flags_val) != IS_LONG) {
1145-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: missing or invalid flags", ZSTR_VAL(intern->std.ce->name));
1150+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11461151
return;
11471152
}
11481153

1149-
intern->flags = (int) Z_LVAL_P(flags_val);
1154+
zend_long flags_value = Z_LVAL_P(flags_val);
11501155

1151-
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", sizeof("heap_elements") - 1);
1156+
if (is_pqueue) {
1157+
flags_value &= SPL_PQUEUE_EXTR_MASK;
1158+
if (!flags_value) {
1159+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1160+
return;
1161+
}
1162+
}
1163+
1164+
intern->flags = (int) flags_value;
1165+
1166+
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", strlen("heap_elements"));
11521167
if (!heap_elements) {
11531168
return;
11541169
}
11551170

11561171
if (Z_TYPE_P(heap_elements) != IS_ARRAY) {
1157-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: heap_elements must be an array", ZSTR_VAL(intern->std.ce->name));
1172+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11581173
return;
11591174
}
11601175

1161-
zval *val;
1162-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), val) {
1176+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), zval *val) {
11631177
if (is_pqueue) {
11641178
if (Z_TYPE_P(val) != IS_ARRAY) {
1165-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: priority queue elements must be arrays", ZSTR_VAL(intern->std.ce->name));
1179+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11661180
return;
11671181
}
11681182

1169-
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", sizeof("data") - 1);
1170-
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", sizeof("priority") - 1);
1183+
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", strlen("data") );
1184+
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", strlen("priority"));
11711185

11721186
if (!data_val || !priority_val) {
1173-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: priority queue elements must have data and priority", ZSTR_VAL(intern->std.ce->name));
1187+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11741188
return;
11751189
}
11761190

@@ -1205,7 +1219,6 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12051219
{
12061220
HashTable *data;
12071221
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1208-
zval *props, *state;
12091222

12101223
ZEND_PARSE_PARAMETERS_START(1, 1)
12111224
Z_PARAM_ARRAY_HT(data)
@@ -1216,7 +1229,7 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12161229
RETURN_THROWS();
12171230
}
12181231

1219-
props = zend_hash_index_find(data, 0);
1232+
zval *props = zend_hash_index_find(data, 0);
12201233
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
12211234
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12221235
RETURN_THROWS();
@@ -1227,13 +1240,16 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12271240
RETURN_THROWS();
12281241
}
12291242

1230-
state = zend_hash_index_find(data, 1);
1243+
zval *state = zend_hash_index_find(data, 1);
12311244
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
12321245
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12331246
RETURN_THROWS();
12341247
}
12351248

12361249
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, true);
1250+
if (EG(exception)) {
1251+
RETURN_THROWS();
1252+
}
12371253
}
12381254

12391255
PHP_METHOD(SplHeap, __serialize)
@@ -1256,7 +1272,6 @@ PHP_METHOD(SplHeap, __unserialize)
12561272
{
12571273
HashTable *data;
12581274
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1259-
zval *props, *state;
12601275

12611276
ZEND_PARSE_PARAMETERS_START(1, 1)
12621277
Z_PARAM_ARRAY_HT(data)
@@ -1267,7 +1282,7 @@ PHP_METHOD(SplHeap, __unserialize)
12671282
RETURN_THROWS();
12681283
}
12691284

1270-
props = zend_hash_index_find(data, 0);
1285+
zval *props = zend_hash_index_find(data, 0);
12711286
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
12721287
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12731288
RETURN_THROWS();
@@ -1278,13 +1293,16 @@ PHP_METHOD(SplHeap, __unserialize)
12781293
RETURN_THROWS();
12791294
}
12801295

1281-
state = zend_hash_index_find(data, 1);
1296+
zval *state = zend_hash_index_find(data, 1);
12821297
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
12831298
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12841299
RETURN_THROWS();
12851300
}
12861301

12871302
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, false);
1303+
if (EG(exception)) {
1304+
RETURN_THROWS();
1305+
}
12881306
}
12891307

12901308
/* iterator handler table */

ext/spl/tests/SplHeap_serialize_error_handling.phpt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,23 @@ try {
6969

7070
try {
7171
$pq = new SplPriorityQueue();
72-
$pq->__unserialize([[], ['flags' => 0, 'heap_elements' => ['not_array']]]);
72+
$pq->__unserialize([[], ['flags' => 1, 'heap_elements' => ['not_array']]]);
7373
echo "FAIL: Should have thrown exception\n";
7474
} catch (Exception $e) {
7575
echo "PQ invalid element structure - " . $e->getMessage() . "\n";
7676
}
7777

7878
try {
7979
$pq = new SplPriorityQueue();
80-
$pq->__unserialize([[], ['flags' => 0, 'heap_elements' => [['data' => 'test']]]]);
80+
$pq->__unserialize([[], ['flags' => 1, 'heap_elements' => [['data' => 'test']]]]);
8181
echo "FAIL: Should have thrown exception\n";
8282
} catch (Exception $e) {
8383
echo "PQ missing priority - " . $e->getMessage() . "\n";
8484
}
8585

8686
try {
8787
$pq = new SplPriorityQueue();
88-
$pq->__unserialize([[], ['flags' => 0, 'heap_elements' => [['priority' => 1]]]]);
88+
$pq->__unserialize([[], ['flags' => 1, 'heap_elements' => [['priority' => 1]]]]);
8989
echo "FAIL: Should have thrown exception\n";
9090
} catch (Exception $e) {
9191
echo "PQ missing data - " . $e->getMessage() . "\n";
@@ -96,11 +96,11 @@ try {
9696
Wrong element count - Invalid serialization data for SplMaxHeap object
9797
Invalid properties type - Invalid serialization data for SplMinHeap object
9898
Invalid state type - Invalid serialization data for SplMaxHeap object
99-
Missing flags - Invalid serialization data for SplMaxHeap object: missing or invalid flags
100-
Invalid heap elements - Invalid serialization data for SplMaxHeap object: heap_elements must be an array
99+
Missing flags - Invalid serialization data for SplMaxHeap object
100+
Invalid heap elements - Invalid serialization data for SplMaxHeap object
101101
PQ wrong element count - Invalid serialization data for SplPriorityQueue object
102102
PQ invalid properties - Invalid serialization data for SplPriorityQueue object
103-
PQ missing flags - Invalid serialization data for SplPriorityQueue object: missing or invalid flags
104-
PQ invalid element structure - Invalid serialization data for SplPriorityQueue object: priority queue elements must be arrays
105-
PQ missing priority - Invalid serialization data for SplPriorityQueue object: priority queue elements must have data and priority
106-
PQ missing data - Invalid serialization data for SplPriorityQueue object: priority queue elements must have data and priority
103+
PQ missing flags - Invalid serialization data for SplPriorityQueue object
104+
PQ invalid element structure - Invalid serialization data for SplPriorityQueue object
105+
PQ missing priority - Invalid serialization data for SplPriorityQueue object
106+
PQ missing data - Invalid serialization data for SplPriorityQueue object
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
--TEST--
2+
SplPriorityQueue unserialization with invalid flags should throw exception
3+
--FILE--
4+
<?php
5+
6+
try {
7+
$data = [
8+
[],
9+
[
10+
'flags' => 4, // invalid flag value (4 & 3 = 0)
11+
'heap_elements' => []
12+
]
13+
];
14+
15+
$queue = new SplPriorityQueue();
16+
$queue->__unserialize($data);
17+
echo "Should have thrown exception for invalid flags\n";
18+
} catch (Exception $e) {
19+
echo "Exception thrown for invalid flags: " . $e->getMessage() . "\n";
20+
}
21+
22+
try {
23+
$data = [
24+
[],
25+
[
26+
'flags' => 0,
27+
'heap_elements' => []
28+
]
29+
];
30+
31+
$queue = new SplPriorityQueue();
32+
$queue->__unserialize($data);
33+
echo "Should have thrown exception for zero flags\n";
34+
} catch (Exception $e) {
35+
echo "Exception thrown for zero flags: " . $e->getMessage() . "\n";
36+
}
37+
38+
try {
39+
$data = [
40+
[],
41+
[
42+
'flags' => SplPriorityQueue::EXTR_DATA,
43+
'heap_elements' => []
44+
]
45+
];
46+
47+
$queue = new SplPriorityQueue();
48+
$queue->__unserialize($data);
49+
echo "Valid flags accepted\n";
50+
} catch (Exception $e) {
51+
echo "Valid flags rejected: " . $e->getMessage() . "\n";
52+
}
53+
54+
try {
55+
$data = [
56+
[],
57+
[
58+
'flags' => 999, // extra bits that should be masked to 3 (EXTR_BOTH)
59+
'heap_elements' => []
60+
]
61+
];
62+
63+
$queue = new SplPriorityQueue();
64+
$queue->__unserialize($data);
65+
66+
if ($queue->getExtractFlags() === SplPriorityQueue::EXTR_BOTH) {
67+
echo "Flags properly masked\n";
68+
} else {
69+
echo "Flags not properly masked, got: " . $queue->getExtractFlags() . "\n";
70+
}
71+
} catch (Exception $e) {
72+
echo "Flags with extra bits should be masked: " . $e->getMessage() . "\n";
73+
}
74+
75+
?>
76+
--EXPECT--
77+
Exception thrown for invalid flags: Invalid serialization data for SplPriorityQueue object
78+
Exception thrown for zero flags: Invalid serialization data for SplPriorityQueue object
79+
Valid flags accepted
80+
Flags properly masked

0 commit comments

Comments
 (0)