From 93948f76c7e795907512e9351165384ea83cb76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 15 Sep 2025 14:30:40 +0200 Subject: [PATCH 1/2] zend_hash: Assert that the `interned` parameter is not a lie While investigating php/php-src#19842 I was wondering why non-interned string didn't cause troubles, until I realized it was the value instead of the key. Nevertheless it appears useful to check that the key is actually interned as claimed by the caller to prevent hard-to-find bugs. --- Zend/zend_hash.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 71206e61550b..b1c466b932e2 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -1628,6 +1628,7 @@ static zend_always_inline zval *_zend_hash_append_ex(HashTable *ht, zend_string Bucket *p = ht->arData + idx; ZVAL_COPY_VALUE(&p->val, zv); + ZEND_ASSERT(!interned || ZSTR_IS_INTERNED(key)); if (!interned && !ZSTR_IS_INTERNED(key)) { HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS; zend_string_addref(key); @@ -1654,6 +1655,7 @@ static zend_always_inline zval *_zend_hash_append_ptr_ex(HashTable *ht, zend_str Bucket *p = ht->arData + idx; ZVAL_PTR(&p->val, ptr); + ZEND_ASSERT(!interned || ZSTR_IS_INTERNED(key)); if (!interned && !ZSTR_IS_INTERNED(key)) { HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS; zend_string_addref(key); From a1c9f5040ef0dec6642c7f4c41adffc264cfa107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 16 Sep 2025 09:22:44 +0200 Subject: [PATCH 2/2] zend_hash: Rename `interned` parameter name to `key_guaranteed_interned` --- Zend/zend_hash.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index b1c466b932e2..57020bbcad0b 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -1621,15 +1621,15 @@ static zend_always_inline bool zend_array_is_list(const zend_array *array) } -static zend_always_inline zval *_zend_hash_append_ex(HashTable *ht, zend_string *key, zval *zv, bool interned) +static zend_always_inline zval *_zend_hash_append_ex(HashTable *ht, zend_string *key, zval *zv, bool key_guaranteed_interned) { uint32_t idx = ht->nNumUsed++; uint32_t nIndex; Bucket *p = ht->arData + idx; ZVAL_COPY_VALUE(&p->val, zv); - ZEND_ASSERT(!interned || ZSTR_IS_INTERNED(key)); - if (!interned && !ZSTR_IS_INTERNED(key)) { + ZEND_ASSERT(!key_guaranteed_interned || ZSTR_IS_INTERNED(key)); + if (!key_guaranteed_interned && !ZSTR_IS_INTERNED(key)) { HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS; zend_string_addref(key); zend_string_hash_val(key); @@ -1648,15 +1648,15 @@ static zend_always_inline zval *_zend_hash_append(HashTable *ht, zend_string *ke return _zend_hash_append_ex(ht, key, zv, 0); } -static zend_always_inline zval *_zend_hash_append_ptr_ex(HashTable *ht, zend_string *key, void *ptr, bool interned) +static zend_always_inline zval *_zend_hash_append_ptr_ex(HashTable *ht, zend_string *key, void *ptr, bool key_guaranteed_interned) { uint32_t idx = ht->nNumUsed++; uint32_t nIndex; Bucket *p = ht->arData + idx; ZVAL_PTR(&p->val, ptr); - ZEND_ASSERT(!interned || ZSTR_IS_INTERNED(key)); - if (!interned && !ZSTR_IS_INTERNED(key)) { + ZEND_ASSERT(!key_guaranteed_interned || ZSTR_IS_INTERNED(key)); + if (!key_guaranteed_interned && !ZSTR_IS_INTERNED(key)) { HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS; zend_string_addref(key); zend_string_hash_val(key);