From 19357aba79c88d82497ac73ffa1c4628113adab7 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Sat, 23 Dec 2023 19:06:44 +0100 Subject: [PATCH 1/2] adds failing test case for #12986 --- ext/zend_test/test.c | 18 ++++++++++++++++++ ext/zend_test/test.stub.php | 2 ++ ext/zend_test/test_arginfo.h | 6 +++++- ext/zend_test/tests/gh12986.phpt | 12 ++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 ext/zend_test/tests/gh12986.phpt diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index a85fa89add744..7c6afbf8c95b0 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -73,6 +73,24 @@ static zend_class_entry *zend_test_int_enum; static zend_class_entry *zend_test_magic_call; static zend_object_handlers zend_test_class_handlers; +static ZEND_FUNCTION(zend_test_gh12986) +{ + ZEND_PARSE_PARAMETERS_NONE(); + HashTable *ht = _zend_new_array_0(); + // The HT_ALLOW_COW_VIOLATION() macro is a noop in non-debug builds, + // so we have to set the flag directly. + HT_FLAGS(ht) |= HASH_FLAG_ALLOW_COW_VIOLATION; + zval value; + ZVAL_LONG(&value, 1); + zend_hash_next_index_insert(ht, &value); + if (HT_FLAGS(ht) & HASH_FLAG_ALLOW_COW_VIOLATION) { + php_printf("OK: HASH_FLAG_ALLOW_COW_VIOLATION is preserved by zend_hash_real_init_ex\n"); + } else { + php_printf("KO: HASH_FLAG_ALLOW_COW_VIOLATION was cleared by zend_hash_real_init_ex\n"); + } + zend_array_release(ht); +} + static int le_throwing_resource; static ZEND_FUNCTION(zend_test_func) diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 0f816950ca765..31ae95cc93e79 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -246,6 +246,8 @@ function zend_test_override_libxml_global_state(): void {} #endif function zend_test_is_pcre_bundled(): bool {} + + function zend_test_gh12986(): void {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index b6d27d7a506f2..af638a46c264f 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 39a14fb061199171b0a0a08b821dabcba516ccf5 */ + * Stub hash: 27be2a99460c562b48b8a6527a4201973a95abe1 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -144,6 +144,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_is_pcre_bundled, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() +#define arginfo_zend_test_gh12986 arginfo_zend_test_void_return + #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled #define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return @@ -266,6 +268,7 @@ static ZEND_FUNCTION(get_open_basedir); static ZEND_FUNCTION(zend_test_override_libxml_global_state); #endif static ZEND_FUNCTION(zend_test_is_pcre_bundled); +static ZEND_FUNCTION(zend_test_gh12986); static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -339,6 +342,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_override_libxml_global_state, arginfo_zend_test_override_libxml_global_state) #endif ZEND_FE(zend_test_is_pcre_bundled, arginfo_zend_test_is_pcre_bundled) + ZEND_FE(zend_test_gh12986, arginfo_zend_test_gh12986) ZEND_NS_FALIAS("ZendTestNS2", namespaced_func, ZendTestNS2_namespaced_func, arginfo_ZendTestNS2_namespaced_func) ZEND_NS_DEP_FALIAS("ZendTestNS2", namespaced_deprecated_func, ZendTestNS2_namespaced_deprecated_func, arginfo_ZendTestNS2_namespaced_deprecated_func) ZEND_NS_FALIAS("ZendTestNS2", namespaced_aliased_func, zend_test_void_return, arginfo_ZendTestNS2_namespaced_aliased_func) diff --git a/ext/zend_test/tests/gh12986.phpt b/ext/zend_test/tests/gh12986.phpt new file mode 100644 index 0000000000000..1a6c80837b2c3 --- /dev/null +++ b/ext/zend_test/tests/gh12986.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-12986 (HASH_FLAG_ALLOW_COW_VIOLATION is not preserved by zend_hash_real_init_ex) +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECT-- +OK: HASH_FLAG_ALLOW_COW_VIOLATION is preserved by zend_hash_real_init_ex From a6ce4c542b524dc8fdfee1d3abc9ff8f6cefb4b3 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Sat, 23 Dec 2023 19:07:41 +0100 Subject: [PATCH 2/2] preserves HASH_FLAG_ALLOW_COW_VIOLATION in zend_hash_real_init_ex Closes #12986 --- Zend/zend_hash.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 6668c4c17c669..c32db86264243 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -149,6 +149,7 @@ static zend_always_inline uint32_t zend_hash_check_size(uint32_t nSize) static zend_always_inline void zend_hash_real_init_packed_ex(HashTable *ht) { void *data; + uint8_t prev_flags = HT_FLAGS(ht) & HASH_FLAG_ALLOW_COW_VIOLATION; if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) { data = pemalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK), 1); @@ -160,7 +161,7 @@ static zend_always_inline void zend_hash_real_init_packed_ex(HashTable *ht) } HT_SET_DATA_ADDR(ht, data); /* Don't overwrite iterator count. */ - ht->u.v.flags = HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS; + ht->u.v.flags = prev_flags | HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS; HT_HASH_RESET_PACKED(ht); } @@ -168,6 +169,7 @@ static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht) { void *data; uint32_t nSize = ht->nTableSize; + uint8_t prev_flags = HT_FLAGS(ht) & HASH_FLAG_ALLOW_COW_VIOLATION; ZEND_ASSERT(HT_SIZE_TO_MASK(nSize)); @@ -178,7 +180,7 @@ static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht) ht->nTableMask = HT_SIZE_TO_MASK(HT_MIN_SIZE); HT_SET_DATA_ADDR(ht, data); /* Don't overwrite iterator count. */ - ht->u.v.flags = HASH_FLAG_STATIC_KEYS; + ht->u.v.flags = prev_flags | HASH_FLAG_STATIC_KEYS; #if defined(__AVX2__) do { __m256i ymm0 = _mm256_setzero_si256(); @@ -227,7 +229,7 @@ static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht) } ht->nTableMask = HT_SIZE_TO_MASK(nSize); HT_SET_DATA_ADDR(ht, data); - HT_FLAGS(ht) = HASH_FLAG_STATIC_KEYS; + HT_FLAGS(ht) = prev_flags | HASH_FLAG_STATIC_KEYS; HT_HASH_RESET(ht); }