@@ -199,6 +199,55 @@ static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, i
199
199
return ht ;
200
200
}
201
201
202
+ static zend_bool spl_fixedarray_shallow_identical (zval * v1 , zval * v2 )
203
+ {
204
+ if (Z_TYPE_P (v1 ) != Z_TYPE_P (v2 )) {
205
+ return false;
206
+ }
207
+ switch (Z_TYPE_P (v1 )) {
208
+ case IS_NULL :
209
+ case IS_FALSE :
210
+ case IS_TRUE :
211
+ return true;
212
+ case IS_LONG :
213
+ return Z_LVAL_P (v1 ) == Z_LVAL_P (v2 );
214
+ case IS_DOUBLE : {
215
+ double d1 = Z_DVAL_P (v1 );
216
+ double d2 = Z_DVAL_P (v2 );
217
+ /* Also check for NAN */
218
+ return d1 == d2 || (d1 != d1 && d2 != d2 );
219
+ }
220
+ case IS_STRING :
221
+ return Z_STR_P (v1 ) == Z_STR_P (v2 ); /* Same pointer */
222
+ case IS_ARRAY :
223
+ return Z_ARR_P (v1 ) == Z_ARR_P (v2 ); /* Same pointer */
224
+ case IS_OBJECT :
225
+ return Z_OBJ_P (v1 ) == Z_OBJ_P (v2 );
226
+ case IS_RESOURCE :
227
+ return Z_RES_P (v1 ) == Z_RES_P (v2 );
228
+ default :
229
+ return false;
230
+ }
231
+ }
232
+
233
+ static zend_bool spl_fixedarray_object_needs_rebuild (spl_fixedarray_object * intern , HashTable * ht , zend_long j )
234
+ {
235
+ for (zend_long i = 0 ; i < intern -> array .size ; i ++ ) {
236
+ zval * old = zend_hash_index_find (ht , i );
237
+ if (old == NULL || !spl_fixedarray_shallow_identical (& intern -> array .elements [i ], old )) {
238
+ return true;
239
+ }
240
+ }
241
+ if (j > intern -> array .size ) {
242
+ for (zend_long i = intern -> array .size ; i < j ; ++ i ) {
243
+ if (zend_hash_index_exists (ht , i )) {
244
+ return true;
245
+ }
246
+ }
247
+ }
248
+ return false;
249
+ }
250
+
202
251
static HashTable * spl_fixedarray_object_get_properties (zend_object * obj )
203
252
{
204
253
spl_fixedarray_object * intern = spl_fixed_array_from_obj (obj );
@@ -208,6 +257,19 @@ static HashTable* spl_fixedarray_object_get_properties(zend_object *obj)
208
257
zend_long j = zend_hash_num_elements (ht );
209
258
210
259
if (GC_REFCOUNT (ht ) > 1 ) {
260
+ /*
261
+ * Usually, the reference count of the hash table is 1,
262
+ * except during cyclic reference cycles.
263
+ *
264
+ * Attempt to maintain the DEBUG invariant that a hash table isn't modified during iteration.
265
+ *
266
+ * See https://github.com/php/php-src/issues/8079 and ext/spl/tests/fixedarray_022.phpt
267
+ * Also see https://github.com/php/php-src/issues/8044 for alternate considered approaches.
268
+ */
269
+ if (!spl_fixedarray_object_needs_rebuild (intern , ht , j )) {
270
+ /* Return the same hash table so that recursion cycle detection works in internal functions. */
271
+ return ht ;
272
+ }
211
273
intern -> std .properties = zend_array_dup (ht );
212
274
if (!(GC_FLAGS (ht ) & GC_IMMUTABLE )) {
213
275
GC_DELREF (ht );
0 commit comments