Skip to content

Commit 3e0bf4f

Browse files
deepclone_to_array: fix UAF on ref pointers across packed→hash conversion
dc_copy_array stashes pointers into dst's HT in ref_entry->tree_pos for later dtor/replacement. But dst was initialized as an empty packed HT, and the first add_new() on a string key triggers zend_hash_packed_to_hash() — which frees the packed storage. Any tree_pos saved by an earlier (integer-keyed) iteration from this array now dangles, and the post-processing pass hits a heap-use-after-free when dtoring through it. Fix: force mixed/hash storage on dst and mask_dst before the loop, so the first and all subsequent add_new() calls write into a stable arData that won't be realloc'd out from under saved tree_pos pointers. Found by libFuzzer (to_array round-trip harness + ASAN) — reproduced with a 9-byte input that builds an array containing a ref and then adds a string key via convert_to_string on a SplFixedArray key.
1 parent 2603a8d commit 3e0bf4f

1 file changed

Lines changed: 7 additions & 0 deletions

File tree

deepclone.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,13 @@ static void dc_copy_array(dc_ctx *ctx, HashTable *src_ht, zval *dst, zval *mask_
909909
return;
910910
}
911911

912+
/* Force hash (mixed) storage up front. dc_copy_value on a reference
913+
* stashes new_dst_slot in ref_entry->tree_pos; if the first insert here
914+
* transitioned dst from packed to hash mode, the later zend_hash_add_new
915+
* would free the packed storage and leave that tree_pos dangling. */
916+
zend_hash_real_init_mixed(Z_ARRVAL_P(dst));
917+
zend_hash_real_init_mixed(Z_ARRVAL_P(mask_dst));
918+
912919
ZEND_HASH_FOREACH_KEY_VAL(src_ht, idx, key, src_val) {
913920
zval undef, null_marker;
914921
ZVAL_UNDEF(&undef);

0 commit comments

Comments
 (0)