Skip to content

Commit

Permalink
Fix use-after-free is WeakMap key and value are the same
Browse files Browse the repository at this point in the history
Drop the object from the WeakMap as the last step, as this might
end up destroying the object.
  • Loading branch information
nikic committed Aug 27, 2020
1 parent 9d409f2 commit 0026d8a
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 3 deletions.
21 changes: 21 additions & 0 deletions Zend/tests/weakrefs/weakmap_weakness.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ var_dump($map);
gc_collect_cycles();
var_dump($map);

echo "\nStoring object as own value:\n";
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = $obj;
unset($obj);
var_dump($map);
unset($map);

echo "\nStoring map in itself:\n";
$map = new WeakMap;
$map[$map] = $map;
Expand Down Expand Up @@ -95,6 +103,19 @@ object(WeakMap)#1 (1) {
object(WeakMap)#1 (0) {
}

Storing object as own value:
object(WeakMap)#3 (1) {
[0]=>
array(2) {
["key"]=>
object(stdClass)#1 (0) {
}
["value"]=>
object(stdClass)#1 (0) {
}
}
}

Storing map in itself:
object(WeakMap)#3 (1) {
[0]=>
Expand Down
10 changes: 7 additions & 3 deletions Zend/zend_weakrefs.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,25 +119,29 @@ static void zend_weakref_unregister(zend_object *object, void *payload) {
uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr);
if (tag != ZEND_WEAKREF_TAG_HT) {
ZEND_ASSERT(tagged_ptr == payload);
zend_weakref_unref_single(ptr, tag, obj_addr);
zend_hash_index_del(&EG(weakrefs), obj_addr);
GC_DEL_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED);

/* Do this last, as it may destroy the object. */
zend_weakref_unref_single(ptr, tag, obj_addr);
return;
}

HashTable *ht = ptr;
tagged_ptr = zend_hash_index_find_ptr(ht, (zend_ulong) payload);
ZEND_ASSERT(tagged_ptr && "Weakref not registered?");
ZEND_ASSERT(tagged_ptr == payload);
zend_weakref_unref_single(
ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), obj_addr);
zend_hash_index_del(ht, (zend_ulong) payload);
if (zend_hash_num_elements(ht) == 0) {
GC_DEL_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED);
zend_hash_destroy(ht);
FREE_HASHTABLE(ht);
zend_hash_index_del(&EG(weakrefs), obj_addr);
}

/* Do this last, as it may destroy the object. */
zend_weakref_unref_single(
ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), obj_addr);
}

void zend_weakrefs_init(void) {
Expand Down

0 comments on commit 0026d8a

Please sign in to comment.