Skip to content

Commit 2236ca2

Browse files
Fix GH-14402: Add support for serialization in SplPriorityQueue, SplMinHeap and SplMaxHeap
1 parent 2c9bfbb commit 2236ca2

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
@@ -1079,13 +1079,14 @@ static void spl_heap_serialize_properties(zval *return_value, spl_heap_object *i
10791079
{
10801080
HashTable *props = zend_std_get_properties(&intern->std);
10811081

1082-
ZVAL_ARR(return_value, props ? zend_array_dup(props) : zend_new_array(0));
1082+
ZVAL_ARR(return_value, props);
1083+
GC_ADDREF(props);
10831084
}
10841085

10851086
static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_object *intern, bool is_pqueue)
10861087
{
1087-
zval heap_elements;
1088-
int heap_count = intern->heap->count;
1088+
zval heap_elements;
1089+
int heap_count = intern->heap->count;
10891090

10901091
array_init(return_value);
10911092
add_assoc_long(return_value, "flags", intern->flags);
@@ -1101,9 +1102,9 @@ static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_objec
11011102
spl_pqueue_elem *elem = spl_heap_elem(intern->heap, heap_idx);
11021103
zval entry;
11031104
array_init(&entry);
1104-
add_assoc_zval_ex(&entry, "data", sizeof("data") - 1, &elem->data);
1105+
add_assoc_zval_ex(&entry, "data", strlen("data"), &elem->data);
11051106
Z_TRY_ADDREF(elem->data);
1106-
add_assoc_zval_ex(&entry, "priority", sizeof("priority") - 1, &elem->priority);
1107+
add_assoc_zval_ex(&entry, "priority", strlen("priority"), &elem->priority);
11071108
Z_TRY_ADDREF(elem->priority);
11081109
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), &entry);
11091110
} else {
@@ -1123,42 +1124,52 @@ static void spl_heap_unserialize_properties(HashTable *props_ht, spl_heap_object
11231124
return;
11241125
}
11251126

1126-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1127+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11271128
}
11281129

11291130
static void spl_heap_unserialize_internal_state(HashTable *state_ht, spl_heap_object *intern, zval *this_ptr, bool is_pqueue)
11301131
{
1131-
zval *flags_val = zend_hash_str_find(state_ht, "flags", sizeof("flags") - 1);
1132+
zval *flags_val = zend_hash_str_find(state_ht, "flags", strlen("flags"));
11321133
if (!flags_val || Z_TYPE_P(flags_val) != IS_LONG) {
1133-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: missing or invalid flags", ZSTR_VAL(intern->std.ce->name));
1134+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11341135
return;
11351136
}
11361137

1137-
intern->flags = (int) Z_LVAL_P(flags_val);
1138+
zend_long flags_value = Z_LVAL_P(flags_val);
11381139

1139-
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", sizeof("heap_elements") - 1);
1140+
if (is_pqueue) {
1141+
flags_value &= SPL_PQUEUE_EXTR_MASK;
1142+
if (!flags_value) {
1143+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1144+
return;
1145+
}
1146+
}
1147+
1148+
intern->flags = (int) flags_value;
1149+
1150+
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", strlen("heap_elements"));
11401151
if (!heap_elements) {
1152+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11411153
return;
11421154
}
11431155

11441156
if (Z_TYPE_P(heap_elements) != IS_ARRAY) {
1145-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: heap_elements must be an array", ZSTR_VAL(intern->std.ce->name));
1157+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11461158
return;
11471159
}
11481160

1149-
zval *val;
1150-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), val) {
1161+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), zval *val) {
11511162
if (is_pqueue) {
11521163
if (Z_TYPE_P(val) != IS_ARRAY) {
1153-
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: priority queue elements must be arrays", ZSTR_VAL(intern->std.ce->name));
1164+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11541165
return;
11551166
}
11561167

1157-
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", sizeof("data") - 1);
1158-
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", sizeof("priority") - 1);
1168+
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", strlen("data") );
1169+
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", strlen("priority"));
11591170

11601171
if (!data_val || !priority_val) {
1161-
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));
1172+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11621173
return;
11631174
}
11641175

@@ -1193,7 +1204,6 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
11931204
{
11941205
HashTable *data;
11951206
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1196-
zval *props, *state;
11971207

11981208
ZEND_PARSE_PARAMETERS_START(1, 1)
11991209
Z_PARAM_ARRAY_HT(data)
@@ -1204,7 +1214,7 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12041214
RETURN_THROWS();
12051215
}
12061216

1207-
props = zend_hash_index_find(data, 0);
1217+
zval *props = zend_hash_index_find(data, 0);
12081218
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
12091219
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12101220
RETURN_THROWS();
@@ -1215,13 +1225,16 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12151225
RETURN_THROWS();
12161226
}
12171227

1218-
state = zend_hash_index_find(data, 1);
1228+
zval *state = zend_hash_index_find(data, 1);
12191229
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
12201230
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12211231
RETURN_THROWS();
12221232
}
12231233

12241234
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, true);
1235+
if (EG(exception)) {
1236+
RETURN_THROWS();
1237+
}
12251238
}
12261239

12271240
PHP_METHOD(SplHeap, __serialize)
@@ -1244,7 +1257,6 @@ PHP_METHOD(SplHeap, __unserialize)
12441257
{
12451258
HashTable *data;
12461259
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1247-
zval *props, *state;
12481260

12491261
ZEND_PARSE_PARAMETERS_START(1, 1)
12501262
Z_PARAM_ARRAY_HT(data)
@@ -1255,7 +1267,7 @@ PHP_METHOD(SplHeap, __unserialize)
12551267
RETURN_THROWS();
12561268
}
12571269

1258-
props = zend_hash_index_find(data, 0);
1270+
zval *props = zend_hash_index_find(data, 0);
12591271
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
12601272
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12611273
RETURN_THROWS();
@@ -1266,13 +1278,16 @@ PHP_METHOD(SplHeap, __unserialize)
12661278
RETURN_THROWS();
12671279
}
12681280

1269-
state = zend_hash_index_find(data, 1);
1281+
zval *state = zend_hash_index_find(data, 1);
12701282
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
12711283
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12721284
RETURN_THROWS();
12731285
}
12741286

12751287
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, false);
1288+
if (EG(exception)) {
1289+
RETURN_THROWS();
1290+
}
12761291
}
12771292

12781293
/* 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)