Commit 8796d753650 for php.net
commit 8796d75365087d4281ae13eb0dcb61459d0aeb5a
Author: ndossche <7771979+ndossche@users.noreply.github.com>
Date: Sun Mar 15 00:05:23 2026 +0100
Fix GH-21454: Missing write lock validation in SplHeap
Closes GH-21448.
diff --git a/NEWS b/NEWS
index d582ab9318a..9cff078b34c 100644
--- a/NEWS
+++ b/NEWS
@@ -33,6 +33,10 @@ PHP NEWS
. Fixed Set-Cookie parsing bug wrong offset while scanning attributes.
(David Carlier)
+- SPL:
+ . Fixed bug GH-21454 (missing write lock validation in SplHeap).
+ (ndossche)
+
- Standard:
. Fixed bug GH-20906 (Assertion failure when messing up output buffers).
(ndossche)
diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c
index d4450da4200..e2c090945b6 100644
--- a/ext/spl/spl_heap.c
+++ b/ext/spl/spl_heap.c
@@ -961,7 +961,7 @@ static void spl_heap_it_move_forward(zend_object_iterator *iter) /* {{{ */
{
spl_heap_object *object = Z_SPLHEAP_P(&iter->data);
- if (UNEXPECTED(spl_heap_consistency_validations(object, false) != SUCCESS)) {
+ if (UNEXPECTED(spl_heap_consistency_validations(object, true) != SUCCESS)) {
return;
}
@@ -992,6 +992,10 @@ PHP_METHOD(SplHeap, next)
RETURN_THROWS();
}
+ if (UNEXPECTED(spl_heap_consistency_validations(intern, true) != SUCCESS)) {
+ RETURN_THROWS();
+ }
+
spl_ptr_heap_delete_top(intern->heap, NULL, ZEND_THIS);
}
/* }}} */
diff --git a/ext/spl/tests/heap_next_write_lock.phpt b/ext/spl/tests/heap_next_write_lock.phpt
new file mode 100644
index 00000000000..fcad94f3ccd
--- /dev/null
+++ b/ext/spl/tests/heap_next_write_lock.phpt
@@ -0,0 +1,34 @@
+--TEST--
+SplHeap::next() write lock
+--CREDITS--
+cnitlrt
+--FILE--
+<?php
+
+class EvilPQ extends SplPriorityQueue {
+ private bool $did = false;
+
+ public function compare(mixed $p1, mixed $p2): int {
+ if (!$this->did) {
+ $this->did = true;
+ // Re-entrant write during internal heap insertion comparison.
+ if (!$this->isEmpty()) {
+ $this->next(); // no write-lock validation
+ }
+ }
+ return parent::compare($p1, $p2);
+ }
+}
+
+$q = new EvilPQ();
+try {
+ for ($i = 0; $i < 200; $i++) {
+ $q->insert("d$i", 100 - $i);
+ }
+} catch (RuntimeException $e) {
+ echo $e::class, ": ", $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+RuntimeException: Heap cannot be changed when it is already being modified.