Skip to content

Commit 1a478b1

Browse files
committed
Fix weakref notify reentrancy during object destruction (GH-20404)
1 parent 114260b commit 1a478b1

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
GH-20404: Crash when adding object to WeakMap during destruction
3+
--FILE--
4+
<?php
5+
6+
$w = new WeakMap;
7+
8+
$o = new stdClass;
9+
$w[$o] = new class($r) {
10+
function __construct(public &$r) {}
11+
function __destruct() {
12+
global $refs;
13+
$o = $this->r->get();
14+
for ($i = 0; $i < 8; ++$i) {
15+
$r = new WeakMap;
16+
$r[$o] = 1;
17+
$refs[] = $r;
18+
}
19+
}
20+
};
21+
$r = WeakReference::create($o);
22+
23+
echo "END OF SCRIPT\n";
24+
?>
25+
--EXPECT--
26+
END OF SCRIPT

Zend/zend_weakrefs.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,13 @@ static void zend_weakref_register(zend_object *object, void *payload) {
125125
static void zend_weakref_unregister(zend_object *object, void *payload, bool weakref_free) {
126126
zend_ulong obj_key = zend_object_to_weakref_key(object);
127127
void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key);
128+
129+
if (!tagged_ptr) {
130+
#if ZEND_DEBUG
128131
ZEND_ASSERT(tagged_ptr && "Weakref not registered?");
132+
#endif
133+
return;
134+
}
129135

130136
void *ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr);
131137
uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr);
@@ -213,13 +219,13 @@ void zend_weakrefs_notify(zend_object *object) {
213219
/* Annoyingly we can't use the HT destructor here, because we need access to the key (which
214220
* is the object address), which is not provided to the dtor. */
215221
const zend_ulong obj_key = zend_object_to_weakref_key(object);
216-
void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key);
222+
void *tagged_ptr;
217223
#if ZEND_DEBUG
218224
ZEND_ASSERT(tagged_ptr && "Tracking of the IS_OBJ_WEAKLY_REFERENCE flag should be precise");
219225
#endif
220-
if (tagged_ptr) {
221-
zend_weakref_unref(object, tagged_ptr);
226+
while ((tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key))) {
222227
zend_hash_index_del(&EG(weakrefs), obj_key);
228+
zend_weakref_unref(object, tagged_ptr);
223229
}
224230
}
225231

0 commit comments

Comments
 (0)