Skip to content

Commit 6902e19

Browse files
committed
Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2: Fix GH-16337: Use-after-free in SplHeap
2 parents a1f7ce5 + a56ff4f commit 6902e19

File tree

3 files changed

+89
-18
lines changed

3 files changed

+89
-18
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ PHP NEWS
3232
. Fixed bug GH-16385 (Unexpected null returned by session_set_cookie_params).
3333
(nielsdos)
3434

35+
- SPL:
36+
. Fixed bug GH-16337 (Use-after-free in SplHeap). (nielsdos)
37+
3538
- XMLReader:
3639
. Fixed bug GH-16292 (Segmentation fault in ext/xmlreader/php_xmlreader.c).
3740
(nielsdos)

ext/spl/spl_heap.c

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#define PTR_HEAP_BLOCK_SIZE 64
3333

3434
#define SPL_HEAP_CORRUPTED 0x00000001
35+
#define SPL_HEAP_WRITE_LOCKED 0x00000002
3536

3637
zend_object_handlers spl_handler_SplHeap;
3738
zend_object_handlers spl_handler_SplPriorityQueue;
@@ -278,12 +279,16 @@ static void spl_ptr_heap_insert(spl_ptr_heap *heap, void *elem, void *cmp_userda
278279
heap->max_size *= 2;
279280
}
280281

282+
heap->flags |= SPL_HEAP_WRITE_LOCKED;
283+
281284
/* sifting up */
282285
for (i = heap->count; i > 0 && heap->cmp(spl_heap_elem(heap, (i-1)/2), elem, cmp_userdata) < 0; i = (i-1)/2) {
283286
spl_heap_elem_copy(heap, spl_heap_elem(heap, i), spl_heap_elem(heap, (i-1)/2));
284287
}
285288
heap->count++;
286289

290+
heap->flags &= ~SPL_HEAP_WRITE_LOCKED;
291+
287292
if (EG(exception)) {
288293
/* exception thrown during comparison */
289294
heap->flags |= SPL_HEAP_CORRUPTED;
@@ -311,6 +316,8 @@ static zend_result spl_ptr_heap_delete_top(spl_ptr_heap *heap, void *elem, void
311316
return FAILURE;
312317
}
313318

319+
heap->flags |= SPL_HEAP_WRITE_LOCKED;
320+
314321
if (elem) {
315322
spl_heap_elem_copy(heap, elem, spl_heap_elem(heap, 0));
316323
} else {
@@ -334,6 +341,8 @@ static zend_result spl_ptr_heap_delete_top(spl_ptr_heap *heap, void *elem, void
334341
}
335342
}
336343

344+
heap->flags &= ~SPL_HEAP_WRITE_LOCKED;
345+
337346
if (EG(exception)) {
338347
/* exception thrown during comparison */
339348
heap->flags |= SPL_HEAP_CORRUPTED;
@@ -379,10 +388,14 @@ static void spl_ptr_heap_destroy(spl_ptr_heap *heap) { /* {{{ */
379388

380389
int i;
381390

391+
heap->flags |= SPL_HEAP_WRITE_LOCKED;
392+
382393
for (i = 0; i < heap->count; ++i) {
383394
heap->dtor(spl_heap_elem(heap, i));
384395
}
385396

397+
heap->flags &= ~SPL_HEAP_WRITE_LOCKED;
398+
386399
efree(heap->elements);
387400
efree(heap);
388401
}
@@ -601,6 +614,21 @@ PHP_METHOD(SplHeap, isEmpty)
601614
}
602615
/* }}} */
603616

617+
static zend_result spl_heap_consistency_validations(const spl_heap_object *intern, bool write)
618+
{
619+
if (intern->heap->flags & SPL_HEAP_CORRUPTED) {
620+
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
621+
return FAILURE;
622+
}
623+
624+
if (write && (intern->heap->flags & SPL_HEAP_WRITE_LOCKED)) {
625+
zend_throw_exception(spl_ce_RuntimeException, "Heap cannot be changed when it is already being modified.", 0);
626+
return FAILURE;
627+
}
628+
629+
return SUCCESS;
630+
}
631+
604632
/* {{{ Push $value on the heap */
605633
PHP_METHOD(SplHeap, insert)
606634
{
@@ -613,8 +641,7 @@ PHP_METHOD(SplHeap, insert)
613641

614642
intern = Z_SPLHEAP_P(ZEND_THIS);
615643

616-
if (intern->heap->flags & SPL_HEAP_CORRUPTED) {
617-
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
644+
if (UNEXPECTED(spl_heap_consistency_validations(intern, true) != SUCCESS)) {
618645
RETURN_THROWS();
619646
}
620647

@@ -636,8 +663,7 @@ PHP_METHOD(SplHeap, extract)
636663

637664
intern = Z_SPLHEAP_P(ZEND_THIS);
638665

639-
if (intern->heap->flags & SPL_HEAP_CORRUPTED) {
640-
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
666+
if (UNEXPECTED(spl_heap_consistency_validations(intern, true) != SUCCESS)) {
641667
RETURN_THROWS();
642668
}
643669

@@ -662,8 +688,7 @@ PHP_METHOD(SplPriorityQueue, insert)
662688

663689
intern = Z_SPLHEAP_P(ZEND_THIS);
664690

665-
if (intern->heap->flags & SPL_HEAP_CORRUPTED) {
666-
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
691+
if (UNEXPECTED(spl_heap_consistency_validations(intern, true) != SUCCESS)) {
667692
RETURN_THROWS();
668693
}
669694

@@ -703,8 +728,7 @@ PHP_METHOD(SplPriorityQueue, extract)
703728

704729
intern = Z_SPLHEAP_P(ZEND_THIS);
705730

706-
if (intern->heap->flags & SPL_HEAP_CORRUPTED) {
707-
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
731+
if (UNEXPECTED(spl_heap_consistency_validations(intern, true) != SUCCESS)) {
708732
RETURN_THROWS();
709733
}
710734

@@ -730,8 +754,7 @@ PHP_METHOD(SplPriorityQueue, top)
730754

731755
intern = Z_SPLHEAP_P(ZEND_THIS);
732756

733-
if (intern->heap->flags & SPL_HEAP_CORRUPTED) {
734-
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
757+
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
735758
RETURN_THROWS();
736759
}
737760

@@ -841,8 +864,7 @@ PHP_METHOD(SplHeap, top)
841864

842865
intern = Z_SPLHEAP_P(ZEND_THIS);
843866

844-
if (intern->heap->flags & SPL_HEAP_CORRUPTED) {
845-
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
867+
if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
846868
RETURN_THROWS();
847869
}
848870

@@ -906,8 +928,7 @@ static zval *spl_heap_it_get_current_data(zend_object_iterator *iter) /* {{{ */
906928
{
907929
spl_heap_object *object = Z_SPLHEAP_P(&iter->data);
908930

909-
if (object->heap->flags & SPL_HEAP_CORRUPTED) {
910-
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
931+
if (UNEXPECTED(spl_heap_consistency_validations(object, false) != SUCCESS)) {
911932
return NULL;
912933
}
913934

@@ -924,8 +945,7 @@ static zval *spl_pqueue_it_get_current_data(zend_object_iterator *iter) /* {{{ *
924945
zend_user_iterator *user_it = (zend_user_iterator *) iter;
925946
spl_heap_object *object = Z_SPLHEAP_P(&iter->data);
926947

927-
if (object->heap->flags & SPL_HEAP_CORRUPTED) {
928-
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
948+
if (UNEXPECTED(spl_heap_consistency_validations(object, false) != SUCCESS)) {
929949
return NULL;
930950
}
931951

@@ -953,8 +973,7 @@ static void spl_heap_it_move_forward(zend_object_iterator *iter) /* {{{ */
953973
{
954974
spl_heap_object *object = Z_SPLHEAP_P(&iter->data);
955975

956-
if (object->heap->flags & SPL_HEAP_CORRUPTED) {
957-
zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
976+
if (UNEXPECTED(spl_heap_consistency_validations(object, false) != SUCCESS)) {
958977
return;
959978
}
960979

ext/spl/tests/gh16337.phpt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
GH-16337 (Use-after-free in SplHeap)
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
function __toString() {
8+
global $heap;
9+
try {
10+
$heap->extract();
11+
} catch (Throwable $e) {
12+
echo $e->getMessage(), "\n";
13+
}
14+
try {
15+
$heap->insert(1);
16+
} catch (Throwable $e) {
17+
echo $e->getMessage(), "\n";
18+
}
19+
echo $heap->top(), "\n";
20+
return "0";
21+
}
22+
}
23+
24+
$heap = new SplMinHeap;
25+
for ($i = 0; $i < 100; $i++) {
26+
$heap->insert((string) $i);
27+
}
28+
$heap->insert(new C);
29+
30+
?>
31+
--EXPECT--
32+
Heap cannot be changed when it is already being modified.
33+
Heap cannot be changed when it is already being modified.
34+
0
35+
Heap cannot be changed when it is already being modified.
36+
Heap cannot be changed when it is already being modified.
37+
0
38+
Heap cannot be changed when it is already being modified.
39+
Heap cannot be changed when it is already being modified.
40+
0
41+
Heap cannot be changed when it is already being modified.
42+
Heap cannot be changed when it is already being modified.
43+
0
44+
Heap cannot be changed when it is already being modified.
45+
Heap cannot be changed when it is already being modified.
46+
0
47+
Heap cannot be changed when it is already being modified.
48+
Heap cannot be changed when it is already being modified.
49+
0

0 commit comments

Comments
 (0)