Skip to content

Commit 04a5516

Browse files
committed
Fix GH-20101: SplHeap/SplPriorityQueue serialization exposes INDIRECTs
Exposing INDIRECTs to userland is not allowed and can lead to all sorts of wrong behaviour. In this case it lead to UAF bugs. Solve it by duplicating the properties table, which de-indirects the elements and also decouples it for future modifications.
1 parent 52e1c9f commit 04a5516

File tree

3 files changed

+60
-28
lines changed

3 files changed

+60
-28
lines changed

ext/spl/spl_heap.c

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,7 @@ static zend_result spl_heap_unserialize_internal_state(HashTable *state_ht, spl_
11671167
return SUCCESS;
11681168
}
11691169

1170-
PHP_METHOD(SplPriorityQueue, __serialize)
1170+
static void spl_heap_serialize_internal(INTERNAL_FUNCTION_PARAMETERS, bool is_pqueue)
11711171
{
11721172
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
11731173
zval props, state;
@@ -1185,14 +1185,18 @@ PHP_METHOD(SplPriorityQueue, __serialize)
11851185

11861186
array_init(return_value);
11871187

1188-
ZVAL_ARR(&props, zend_std_get_properties(&intern->std));
1189-
Z_TRY_ADDREF(props);
1188+
ZVAL_ARR(&props, zend_array_dup(zend_std_get_properties(&intern->std)));
11901189
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &props);
11911190

1192-
spl_heap_serialize_internal_state(&state, intern, true);
1191+
spl_heap_serialize_internal_state(&state, intern, is_pqueue);
11931192
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &state);
11941193
}
11951194

1195+
PHP_METHOD(SplPriorityQueue, __serialize)
1196+
{
1197+
spl_heap_serialize_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1198+
}
1199+
11961200
PHP_METHOD(SplPriorityQueue, __unserialize)
11971201
{
11981202
HashTable *data;
@@ -1241,28 +1245,7 @@ PHP_METHOD(SplPriorityQueue, __unserialize)
12411245

12421246
PHP_METHOD(SplHeap, __serialize)
12431247
{
1244-
spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1245-
zval props, state;
1246-
1247-
ZEND_PARSE_PARAMETERS_NONE();
1248-
1249-
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
1250-
RETURN_THROWS();
1251-
}
1252-
1253-
if (intern->heap->flags & SPL_HEAP_WRITE_LOCKED) {
1254-
zend_throw_exception(spl_ce_RuntimeException, "Cannot serialize heap while it is being modified.", 0);
1255-
RETURN_THROWS();
1256-
}
1257-
1258-
array_init(return_value);
1259-
1260-
ZVAL_ARR(&props, zend_std_get_properties(&intern->std));
1261-
Z_TRY_ADDREF(props);
1262-
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &props);
1263-
1264-
spl_heap_serialize_internal_state(&state, intern, false);
1265-
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &state);
1248+
spl_heap_serialize_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
12661249
}
12671250

12681251
PHP_METHOD(SplHeap, __unserialize)

ext/spl/tests/SplHeap_serialize_indexed_format.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ array(2) {
7474
[0]=>
7575
array(2) {
7676
["flags"]=>
77-
UNKNOWN:0
77+
string(13) "user_property"
7878
["heap_elements"]=>
79-
UNKNOWN:0
79+
string(13) "user_property"
8080
}
8181
[1]=>
8282
array(2) {

ext/spl/tests/gh20101.phpt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
GH-20101 (SplHeap/SplPriorityQueue serialization exposes INDIRECTs)
3+
--FILE--
4+
<?php
5+
class CustomHeap extends SplMaxHeap {
6+
public $field = 0;
7+
}
8+
$heap = new CustomHeap();
9+
$data = $heap->__serialize();
10+
var_dump($data);
11+
12+
class CustomPriorityQueue extends SplPriorityQueue {
13+
public $field = 0;
14+
}
15+
$pqueue = new CustomPriorityQueue();
16+
$data = $pqueue->__serialize();
17+
var_dump($data);
18+
?>
19+
--EXPECT--
20+
array(2) {
21+
[0]=>
22+
array(1) {
23+
["field"]=>
24+
int(0)
25+
}
26+
[1]=>
27+
array(2) {
28+
["flags"]=>
29+
int(0)
30+
["heap_elements"]=>
31+
array(0) {
32+
}
33+
}
34+
}
35+
array(2) {
36+
[0]=>
37+
array(1) {
38+
["field"]=>
39+
int(0)
40+
}
41+
[1]=>
42+
array(2) {
43+
["flags"]=>
44+
int(1)
45+
["heap_elements"]=>
46+
array(0) {
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)