Skip to content

Commit 0d3e00d

Browse files
Fix GH-14402: Add support for serialization in SplPriorityQueue, SplMinHeap and SplMaxHeap
1 parent cc9fb7b commit 0d3e00d

File tree

4 files changed

+146
-33
lines changed

4 files changed

+146
-33
lines changed

ext/spl/spl_heap.c

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,13 +1074,14 @@ static void spl_heap_serialize_properties(zval *return_value, spl_heap_object *i
10741074
{
10751075
HashTable *props = zend_std_get_properties(&intern->std);
10761076

1077-
ZVAL_ARR(return_value, props ? zend_array_dup(props) : zend_new_array(0));
1077+
ZVAL_ARR(return_value, props);
1078+
GC_ADDREF(props);
10781079
}
10791080

10801081
static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_object *intern, bool is_pqueue)
10811082
{
1082-
zval heap_elements;
1083-
int heap_count = intern->heap->count;
1083+
zval heap_elements;
1084+
int heap_count = intern->heap->count;
10841085

10851086
array_init(return_value);
10861087
add_assoc_long(return_value, "flags", intern->flags);
@@ -1096,9 +1097,9 @@ static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_objec
10961097
spl_pqueue_elem *elem = spl_heap_elem(intern->heap, heap_idx);
10971098
zval entry;
10981099
array_init(&entry);
1099-
add_assoc_zval_ex(&entry, "data", sizeof("data") - 1, &elem->data);
1100+
add_assoc_zval_ex(&entry, "data", strlen("data"), &elem->data);
11001101
Z_TRY_ADDREF(elem->data);
1101-
add_assoc_zval_ex(&entry, "priority", sizeof("priority") - 1, &elem->priority);
1102+
add_assoc_zval_ex(&entry, "priority", strlen("priority"), &elem->priority);
11021103
Z_TRY_ADDREF(elem->priority);
11031104
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), &entry);
11041105
} else {
@@ -1118,42 +1119,52 @@ static void spl_heap_unserialize_properties(HashTable *props_ht, spl_heap_object
11181119
return;
11191120
}
11201121

1121-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1122+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11221123
}
11231124

11241125
static void spl_heap_unserialize_internal_state(HashTable *state_ht, spl_heap_object *intern, zval *this_ptr, bool is_pqueue)
11251126
{
1126-
zval *flags_val = zend_hash_str_find(state_ht, "flags", sizeof("flags") - 1);
1127+
zval *flags_val = zend_hash_str_find(state_ht, "flags", strlen("flags"));
11271128
if (!flags_val || Z_TYPE_P(flags_val) != IS_LONG) {
1128-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: missing or invalid flags", ZSTR_VAL(intern->std.ce->name));
1129+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11291130
return;
11301131
}
11311132

1132-
intern->flags = (int) Z_LVAL_P(flags_val);
1133+
zend_long flags_value = Z_LVAL_P(flags_val);
11331134

1134-
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", sizeof("heap_elements") - 1);
1135+
if (is_pqueue) {
1136+
flags_value &= SPL_PQUEUE_EXTR_MASK;
1137+
if (!flags_value) {
1138+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1139+
return;
1140+
}
1141+
}
1142+
1143+
intern->flags = (int) flags_value;
1144+
1145+
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", strlen("heap_elements"));
11351146
if (!heap_elements) {
1147+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11361148
return;
11371149
}
11381150

11391151
if (Z_TYPE_P(heap_elements) != IS_ARRAY) {
1140-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: heap_elements must be an array", ZSTR_VAL(intern->std.ce->name));
1152+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11411153
return;
11421154
}
11431155

1144-
zval *val;
1145-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), val) {
1156+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), zval *val) {
11461157
if (is_pqueue) {
11471158
if (Z_TYPE_P(val) != IS_ARRAY) {
1148-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: priority queue elements must be arrays", ZSTR_VAL(intern->std.ce->name));
1159+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11491160
return;
11501161
}
11511162

1152-
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", sizeof("data") - 1);
1153-
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", sizeof("priority") - 1);
1163+
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", strlen("data") );
1164+
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", strlen("priority"));
11541165

11551166
if (!data_val || !priority_val) {
1156-
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));
1167+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11571168
return;
11581169
}
11591170

@@ -1188,7 +1199,6 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
11881199
{
11891200
HashTable *data;
11901201
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1191-
zval *props, *state;
11921202

11931203
ZEND_PARSE_PARAMETERS_START(1, 1)
11941204
Z_PARAM_ARRAY_HT(data)
@@ -1199,7 +1209,7 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
11991209
RETURN_THROWS();
12001210
}
12011211

1202-
props = zend_hash_index_find(data, 0);
1212+
zval *props = zend_hash_index_find(data, 0);
12031213
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
12041214
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12051215
RETURN_THROWS();
@@ -1210,13 +1220,16 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12101220
RETURN_THROWS();
12111221
}
12121222

1213-
state = zend_hash_index_find(data, 1);
1223+
zval *state = zend_hash_index_find(data, 1);
12141224
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
12151225
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12161226
RETURN_THROWS();
12171227
}
12181228

12191229
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, true);
1230+
if (EG(exception)) {
1231+
RETURN_THROWS();
1232+
}
12201233
}
12211234

12221235
PHP_METHOD(SplHeap, __serialize)
@@ -1239,7 +1252,6 @@ PHP_METHOD(SplHeap, __unserialize)
12391252
{
12401253
HashTable *data;
12411254
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1242-
zval *props, *state;
12431255

12441256
ZEND_PARSE_PARAMETERS_START(1, 1)
12451257
Z_PARAM_ARRAY_HT(data)
@@ -1250,7 +1262,7 @@ PHP_METHOD(SplHeap, __unserialize)
12501262
RETURN_THROWS();
12511263
}
12521264

1253-
props = zend_hash_index_find(data, 0);
1265+
zval *props = zend_hash_index_find(data, 0);
12541266
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
12551267
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12561268
RETURN_THROWS();
@@ -1261,13 +1273,16 @@ PHP_METHOD(SplHeap, __unserialize)
12611273
RETURN_THROWS();
12621274
}
12631275

1264-
state = zend_hash_index_find(data, 1);
1276+
zval *state = zend_hash_index_find(data, 1);
12651277
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
12661278
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12671279
RETURN_THROWS();
12681280
}
12691281

12701282
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, false);
1283+
if (EG(exception)) {
1284+
RETURN_THROWS();
1285+
}
12711286
}
12721287

12731288
/* iterator handler table */

ext/spl/spl_heap_arginfo.h

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

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)