Skip to content

Commit 5f8ed77

Browse files
committed
Fix GC of object properties HT
We partially fixed this in bug #78379, but still don't handle the case where the properties array is marked as grey first, which causes a delref to not be performed later. Fix this by treating the object properties HT the same way as other refcounted values, including addrefs/delrefs. The object dtor code already handles properties HT with NULL GC type, so out of order destruction should not be a problem. Fixes oss-fuzz #36023.
1 parent 0192fd2 commit 5f8ed77

File tree

3 files changed

+59
-34
lines changed

3 files changed

+59
-34
lines changed

Zend/tests/gc_044.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
GC of object property table (order variation)
3+
--FILE--
4+
<?php
5+
function test() {
6+
$o1 = new stdClass;
7+
$o2 = new stdClass;
8+
$a = ['prop' => $o2];
9+
$o = $o1;
10+
$o2->a = (object) $a;
11+
}
12+
test();
13+
?>
14+
===DONE===
15+
--EXPECT--
16+
===DONE===

Zend/zend_gc.c

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -705,15 +705,21 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
705705
zval *zv, *end;
706706

707707
ht = obj->handlers->get_gc(obj, &zv, &n);
708-
if (EXPECTED(!ht) || UNEXPECTED(GC_REF_CHECK_COLOR(ht, GC_BLACK))) {
709-
ht = NULL;
708+
if (UNEXPECTED(ht)) {
709+
GC_ADDREF(ht);
710+
if (!GC_REF_CHECK_COLOR(ht, GC_BLACK)) {
711+
GC_REF_SET_BLACK(ht);
712+
} else {
713+
ht = NULL;
714+
}
715+
}
716+
if (EXPECTED(!ht)) {
710717
if (!n) goto next;
711718
end = zv + n;
712719
while (!Z_REFCOUNTED_P(--end)) {
713720
if (zv == end) goto next;
714721
}
715722
} else {
716-
GC_REF_SET_BLACK(ht);
717723
if (!n) goto handle_ht;
718724
end = zv + n;
719725
}
@@ -823,15 +829,21 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
823829
zval *zv, *end;
824830

825831
ht = obj->handlers->get_gc(obj, &zv, &n);
826-
if (EXPECTED(!ht) || UNEXPECTED(GC_REF_CHECK_COLOR(ht, GC_GREY))) {
827-
ht = NULL;
832+
if (UNEXPECTED(ht)) {
833+
GC_DELREF(ht);
834+
if (!GC_REF_CHECK_COLOR(ht, GC_GREY)) {
835+
GC_REF_SET_COLOR(ht, GC_GREY);
836+
} else {
837+
ht = NULL;
838+
}
839+
}
840+
if (EXPECTED(!ht)) {
828841
if (!n) goto next;
829842
end = zv + n;
830843
while (!Z_REFCOUNTED_P(--end)) {
831844
if (zv == end) goto next;
832845
}
833846
} else {
834-
GC_REF_SET_COLOR(ht, GC_GREY);
835847
if (!n) goto handle_ht;
836848
end = zv + n;
837849
}
@@ -1006,17 +1018,18 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
10061018
zval *zv, *end;
10071019

10081020
ht = obj->handlers->get_gc(obj, &zv, &n);
1009-
if (EXPECTED(!ht) || UNEXPECTED(!GC_REF_CHECK_COLOR(ht, GC_GREY))) {
1010-
ht = NULL;
1011-
if (!n) goto next;
1012-
end = zv + n;
1013-
while (!Z_REFCOUNTED_P(--end)) {
1014-
if (zv == end) goto next;
1021+
if (UNEXPECTED(ht)) {
1022+
if (GC_REF_CHECK_COLOR(ht, GC_GREY)) {
1023+
GC_REF_SET_COLOR(ht, GC_WHITE);
1024+
GC_STACK_PUSH((zend_refcounted *) ht);
10151025
}
1016-
} else {
1017-
GC_REF_SET_COLOR(ht, GC_WHITE);
1018-
if (!n) goto handle_ht;
1019-
end = zv + n;
1026+
ht = NULL;
1027+
}
1028+
1029+
if (!n) goto next;
1030+
end = zv + n;
1031+
while (!Z_REFCOUNTED_P(--end)) {
1032+
if (zv == end) goto next;
10201033
}
10211034
while (zv != end) {
10221035
if (Z_REFCOUNTED_P(zv)) {
@@ -1028,17 +1041,13 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
10281041
}
10291042
zv++;
10301043
}
1031-
if (EXPECTED(!ht)) {
1032-
ref = Z_COUNTED_P(zv);
1033-
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
1034-
GC_REF_SET_COLOR(ref, GC_WHITE);
1035-
goto tail_call;
1036-
}
1037-
goto next;
1044+
ref = Z_COUNTED_P(zv);
1045+
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
1046+
GC_REF_SET_COLOR(ref, GC_WHITE);
1047+
goto tail_call;
10381048
}
1039-
} else {
1040-
goto next;
10411049
}
1050+
goto next;
10421051
} else if (GC_TYPE(ref) == IS_ARRAY) {
10431052
ZEND_ASSERT((zend_array*)ref != &EG(symbol_table));
10441053
ht = (zend_array*)ref;
@@ -1055,7 +1064,6 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
10551064
goto next;
10561065
}
10571066

1058-
handle_ht:
10591067
if (!ht->nNumUsed) goto next;
10601068
p = ht->arData;
10611069
end = p + ht->nNumUsed;
@@ -1175,15 +1183,21 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta
11751183
*flags |= GC_HAS_DESTRUCTORS;
11761184
}
11771185
ht = obj->handlers->get_gc(obj, &zv, &n);
1178-
if (EXPECTED(!ht) || UNEXPECTED(GC_REF_CHECK_COLOR(ht, GC_BLACK))) {
1179-
ht = NULL;
1186+
if (UNEXPECTED(ht)) {
1187+
GC_ADDREF(ht);
1188+
if (GC_REF_CHECK_COLOR(ht, GC_WHITE)) {
1189+
GC_REF_SET_BLACK(ht);
1190+
} else {
1191+
ht = NULL;
1192+
}
1193+
}
1194+
if (EXPECTED(!ht)) {
11801195
if (!n) goto next;
11811196
end = zv + n;
11821197
while (!Z_REFCOUNTED_P(--end)) {
11831198
if (zv == end) goto next;
11841199
}
11851200
} else {
1186-
GC_REF_SET_BLACK(ht);
11871201
if (!n) goto handle_ht;
11881202
end = zv + n;
11891203
}

Zend/zend_object_handlers.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,6 @@ ZEND_API HashTable *zend_std_get_gc(zend_object *zobj, zval **table, int *n) /*
138138
if (zobj->properties) {
139139
*table = NULL;
140140
*n = 0;
141-
if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)
142-
&& EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
143-
GC_DELREF(zobj->properties);
144-
zobj->properties = zend_array_dup(zobj->properties);
145-
}
146141
return zobj->properties;
147142
} else {
148143
*table = zobj->properties_table;

0 commit comments

Comments
 (0)