Commit 50e38ac74c8 for php

commit 50e38ac74c8e8a60faac2fe1318ca80c94f24969
Merge: 593ed68ac95 10b26c49ec5
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date:   Tue Oct 27 13:28:34 2026 +0100

    Merge branch 'PHP-8.6'

    * PHP-8.6:
      Fix GH-24577: CE-cache flag on interned class name visible before map_ptr offset (#24585)

diff --cc Zend/zend.c
index 21605651121,55ad84f189f..a0a1a2af022
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@@ -2119,6 -2143,7 +2119,7 @@@ ZEND_API void zend_alloc_ce_cache(zend_
  	do {
  		ret = ZEND_MAP_PTR_NEW_OFFSET();
  	} while (ret <= 2);
- 	GC_ADD_FLAGS(type_name, IS_STR_CLASS_NAME_MAP_PTR);
  	GC_SET_REFCOUNT(type_name, ret);
 -	ZEND_ATOMIC_FENCE_RELEASE();
++	atomic_thread_fence(memory_order_release);
+ 	GC_ADD_FLAGS(type_name, IS_STR_CLASS_NAME_MAP_PTR);
  }
diff --cc Zend/zend_types.h
index 932d131820d,1c409ec45a0..fb1c4a93249
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@@ -960,8 -961,14 +961,29 @@@ static zend_always_inline uint32_t zend

  #define OBJ_EXTRA_FLAGS(obj)		((obj)->extra_flags)

++static zend_always_inline uint32_t zend_atomic_load_acquire_32(const uint32_t *ptr)
++{
++#if defined(__GNUC__) || defined(__clang__) || defined(HAVE_GNUC_ATOMICS)
++	return __atomic_load_n(ptr, __ATOMIC_ACQUIRE);
++#else
++	uint32_t val = *(const volatile uint32_t *)ptr;
++	ZEND_ATOMIC_FENCE_ACQUIRE();
++	return val;
++#endif
++}
++
  /* Fast class cache */
- #define ZSTR_HAS_CE_CACHE(s)		(GC_FLAGS(s) & IS_STR_CLASS_NAME_MAP_PTR)
+ static zend_always_inline bool zstr_has_ce_cache(const zend_string *s)
+ {
 -	uint32_t type_info = ZEND_ATOMIC_LOAD_ACQUIRE_32(&GC_TYPE_INFO(s));
++	static_assert(sizeof(_Atomic uint32_t) == sizeof(uint32_t), "atomic size");
++	static_assert(alignof(_Atomic uint32_t) == alignof(uint32_t), "atomic align");
++	static_assert(ATOMIC_INT_LOCK_FREE == 2, "not always lock-free");
++
++	uint32_t type_info = atomic_load_explicit((_Atomic uint32_t *)&GC_TYPE_INFO(s), memory_order_acquire);
+ 	return (type_info & (IS_STR_CLASS_NAME_MAP_PTR << GC_FLAGS_SHIFT)) != 0;
+ }
+
+ #define ZSTR_HAS_CE_CACHE(s)		zstr_has_ce_cache(s)
  #define ZSTR_GET_CE_CACHE(s)		ZSTR_GET_CE_CACHE_EX(s, 1)
  #define ZSTR_SET_CE_CACHE(s, ce)	ZSTR_SET_CE_CACHE_EX(s, ce, 1)

diff --cc ext/opcache/ZendAccelerator.c
index 2318f5b28c8,28032182e29..3834190ffe9
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@@ -457,8 -457,9 +457,9 @@@ static void accel_interned_strings_rest
  				/* Discard non-global CE_CACHE slots on reset. */
  				uintptr_t idx = (GC_REFCOUNT(s) - 1) / sizeof(void *);
  				if (idx >= ZCSG(map_ptr_last)) {
- 					GC_SET_REFCOUNT(s, 2);
  					GC_DEL_FLAGS(s, IS_STR_CLASS_NAME_MAP_PTR);
 -					ZEND_ATOMIC_FENCE_RELEASE();
++					atomic_thread_fence(memory_order_release);
+ 					GC_SET_REFCOUNT(s, 2);
  				}
  			}

@@@ -567,6 -568,7 +568,7 @@@ finish
  	if (ZSTR_HAS_CE_CACHE(str) && !ZSTR_HAS_CE_CACHE(s)) {
  		ZEND_ASSERT(GC_FLAGS(str) & IS_STR_PERMANENT);
  		GC_SET_REFCOUNT(s, GC_REFCOUNT(str));
 -		ZEND_ATOMIC_FENCE_RELEASE();
++		atomic_thread_fence(memory_order_release);
  		GC_ADD_FLAGS(s, IS_STR_CLASS_NAME_MAP_PTR);
  	}

diff --cc ext/opcache/zend_persist.c
index 3d70dfcaccf,052411a0eb0..84043ed0450
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@@ -371,6 -371,7 +371,7 @@@ void zend_accel_pending_ce_cache_commit

  		ZEND_ASSERT(!ZSTR_HAS_CE_CACHE(type_name));
  		GC_SET_REFCOUNT(type_name, (uint32_t)Z_LVAL_P(zv));
 -		ZEND_ATOMIC_FENCE_RELEASE();
++		atomic_thread_fence(memory_order_release);
  		GC_ADD_FLAGS(type_name, IS_STR_CLASS_NAME_MAP_PTR);
  	} ZEND_HASH_FOREACH_END();