Skip to content

Commit 5209c63

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

File tree

3 files changed

+127
-32
lines changed

3 files changed

+127
-32
lines changed

ext/spl/spl_heap.c

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,13 +1091,14 @@ 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+
GC_ADDREF(props);
10951096
}
10961097

10971098
static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_object *intern, bool is_pqueue)
10981099
{
1099-
zval heap_elements;
1100-
int heap_count = intern->heap->count;
1100+
zval heap_elements;
1101+
int heap_count = intern->heap->count;
11011102

11021103
array_init(return_value);
11031104
add_assoc_long(return_value, "flags", intern->flags);
@@ -1113,9 +1114,9 @@ static void spl_heap_serialize_internal_state(zval *return_value, spl_heap_objec
11131114
spl_pqueue_elem *elem = spl_heap_elem(intern->heap, heap_idx);
11141115
zval entry;
11151116
array_init(&entry);
1116-
add_assoc_zval_ex(&entry, "data", sizeof("data") - 1, &elem->data);
1117+
add_assoc_zval_ex(&entry, "data", strlen("data"), &elem->data);
11171118
Z_TRY_ADDREF(elem->data);
1118-
add_assoc_zval_ex(&entry, "priority", sizeof("priority") - 1, &elem->priority);
1119+
add_assoc_zval_ex(&entry, "priority", strlen("priority"), &elem->priority);
11191120
Z_TRY_ADDREF(elem->priority);
11201121
zend_hash_next_index_insert(Z_ARRVAL(heap_elements), &entry);
11211122
} else {
@@ -1135,42 +1136,52 @@ static void spl_heap_unserialize_properties(HashTable *props_ht, spl_heap_object
11351136
return;
11361137
}
11371138

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

11411142
static void spl_heap_unserialize_internal_state(HashTable *state_ht, spl_heap_object *intern, zval *this_ptr, bool is_pqueue)
11421143
{
1143-
zval *flags_val = zend_hash_str_find(state_ht, "flags", sizeof("flags") - 1);
1144+
zval *flags_val = zend_hash_str_find(state_ht, "flags", strlen("flags"));
11441145
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));
1146+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11461147
return;
11471148
}
11481149

1149-
intern->flags = (int) Z_LVAL_P(flags_val);
1150+
zend_long flags_value = Z_LVAL_P(flags_val);
11501151

1151-
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", sizeof("heap_elements") - 1);
1152+
if (is_pqueue) {
1153+
flags_value &= SPL_PQUEUE_EXTR_MASK;
1154+
if (!flags_value) {
1155+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
1156+
return;
1157+
}
1158+
}
1159+
1160+
intern->flags = (int) flags_value;
1161+
1162+
zval *heap_elements = zend_hash_str_find(state_ht, "heap_elements", strlen("heap_elements"));
11521163
if (!heap_elements) {
1164+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11531165
return;
11541166
}
11551167

11561168
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));
1169+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11581170
return;
11591171
}
11601172

1161-
zval *val;
1162-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), val) {
1173+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(heap_elements), zval *val) {
11631174
if (is_pqueue) {
11641175
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));
1176+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11661177
return;
11671178
}
11681179

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);
1180+
zval *data_val = zend_hash_str_find(Z_ARRVAL_P(val), "data", strlen("data") );
1181+
zval *priority_val = zend_hash_str_find(Z_ARRVAL_P(val), "priority", strlen("priority"));
11711182

11721183
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));
1184+
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
11741185
return;
11751186
}
11761187

@@ -1205,7 +1216,6 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12051216
{
12061217
HashTable *data;
12071218
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1208-
zval *props, *state;
12091219

12101220
ZEND_PARSE_PARAMETERS_START(1, 1)
12111221
Z_PARAM_ARRAY_HT(data)
@@ -1216,7 +1226,7 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12161226
RETURN_THROWS();
12171227
}
12181228

1219-
props = zend_hash_index_find(data, 0);
1229+
zval *props = zend_hash_index_find(data, 0);
12201230
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
12211231
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12221232
RETURN_THROWS();
@@ -1227,13 +1237,16 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12271237
RETURN_THROWS();
12281238
}
12291239

1230-
state = zend_hash_index_find(data, 1);
1240+
zval *state = zend_hash_index_find(data, 1);
12311241
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
12321242
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12331243
RETURN_THROWS();
12341244
}
12351245

12361246
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, true);
1247+
if (EG(exception)) {
1248+
RETURN_THROWS();
1249+
}
12371250
}
12381251

12391252
PHP_METHOD(SplHeap, __serialize)
@@ -1256,7 +1269,6 @@ PHP_METHOD(SplHeap, __unserialize)
12561269
{
12571270
HashTable *data;
12581271
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1259-
zval *props, *state;
12601272

12611273
ZEND_PARSE_PARAMETERS_START(1, 1)
12621274
Z_PARAM_ARRAY_HT(data)
@@ -1267,7 +1279,7 @@ PHP_METHOD(SplHeap, __unserialize)
12671279
RETURN_THROWS();
12681280
}
12691281

1270-
props = zend_hash_index_find(data, 0);
1282+
zval *props = zend_hash_index_find(data, 0);
12711283
if (!props || Z_TYPE_P(props) != IS_ARRAY) {
12721284
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12731285
RETURN_THROWS();
@@ -1278,13 +1290,16 @@ PHP_METHOD(SplHeap, __unserialize)
12781290
RETURN_THROWS();
12791291
}
12801292

1281-
state = zend_hash_index_find(data, 1);
1293+
zval *state = zend_hash_index_find(data, 1);
12821294
if (!state || Z_TYPE_P(state) != IS_ARRAY) {
12831295
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(intern->std.ce->name));
12841296
RETURN_THROWS();
12851297
}
12861298

12871299
spl_heap_unserialize_internal_state(Z_ARRVAL_P(state), intern, ZEND_THIS, false);
1300+
if (EG(exception)) {
1301+
RETURN_THROWS();
1302+
}
12881303
}
12891304

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