New macro ZVAL_COPY_OR_DUP() is used perform duplication, if necessary.
This should eliminate related race-coditions in ZTS build and prevent reference-counting bugs after unclean shutdown.
#endif
for (i = 0; i < class_type->default_static_members_count; i++) {
p = &class_type->default_static_members_table[i];
- if (Z_ISREF_P(p) &&
- class_type->parent &&
- i < class_type->parent->default_static_members_count &&
- p == &class_type->parent->default_static_members_table[i] &&
- Z_TYPE(CE_STATIC_MEMBERS(class_type->parent)[i]) != IS_UNDEF
- ) {
- zval *q = &CE_STATIC_MEMBERS(class_type->parent)[i];
-
- ZVAL_NEW_REF(q, q);
- ZVAL_COPY_VALUE(&CE_STATIC_MEMBERS(class_type)[i], q);
- Z_ADDREF_P(q);
+ if (Z_ISREF_P(p)) {
+ if (class_type->parent &&
+ i < class_type->parent->default_static_members_count &&
+ p == &class_type->parent->default_static_members_table[i] &&
+ Z_TYPE(CE_STATIC_MEMBERS(class_type->parent)[i]) != IS_UNDEF
+ ) {
+ zval *q = &CE_STATIC_MEMBERS(class_type->parent)[i];
+
+ ZVAL_NEW_REF(q, q);
+ ZVAL_COPY_VALUE(&CE_STATIC_MEMBERS(class_type)[i], q);
+ Z_ADDREF_P(q);
+ } else {
+ ZVAL_COPY_OR_DUP(&CE_STATIC_MEMBERS(class_type)[i], Z_REFVAL_P(p));
+ }
} else {
- ZVAL_DUP(&CE_STATIC_MEMBERS(class_type)[i], p);
+ ZVAL_COPY_OR_DUP(&CE_STATIC_MEMBERS(class_type)[i], p);
}
}
} else {
zval *dst = object->properties_table;
zval *end = src + class_type->default_properties_count;
- do {
-#if ZTS
- ZVAL_DUP(dst, src);
-#else
- ZVAL_COPY(dst, src);
-#endif
- src++;
- dst++;
- } while (src != end);
+ if (UNEXPECTED(class_type->type == ZEND_INTERNAL_CLASS)) {
+ do {
+ ZVAL_COPY_OR_DUP(dst, src);
+ src++;
+ dst++;
+ } while (src != end);
+ } else {
+ do {
+ ZVAL_COPY(dst, src);
+ src++;
+ dst++;
+ } while (src != end);
+ }
object->properties = NULL;
}
}
ret = zend_use_undefined_constant(name, ast->attr, result);
break;
}
- ZVAL_DUP(result, zv);
+ ZVAL_COPY_OR_DUP(result, zv);
break;
}
case ZEND_AST_CONSTANT_CLASS:
zend_fetch_dimension_const(&tmp, &op1, &op2, (ast->attr == ZEND_DIM_IS) ? BP_VAR_IS : BP_VAR_R);
if (UNEXPECTED(Z_ISREF(tmp))) {
- ZVAL_DUP(result, Z_REFVAL(tmp));
+ ZVAL_COPY_OR_DUP(result, Z_REFVAL(tmp));
} else {
- ZVAL_DUP(result, &tmp);
+ ZVAL_COPY_OR_DUP(result, &tmp);
}
zval_ptr_dtor(&tmp);
zval_dtor(&op1);
/* copy: enforce read only access */
ZVAL_DEREF(prop);
- if (UNEXPECTED(Z_COPYABLE_P(prop))) {
- ZVAL_DUP(&prop_copy, prop);
- prop = &prop_copy;
- } else {
- Z_TRY_ADDREF_P(prop);
- }
+ ZVAL_COPY_OR_DUP(&prop_copy, prop);
+ prop = &prop_copy;
/* this is necessary to make it able to work with default array
* properties, returned to user */
return 0;
}
- ZVAL_DUP(&const_val, &constant->value);
+ ZVAL_COPY_OR_DUP(&const_val, &constant->value);
zend_hash_add_new(Z_ARRVAL_P(name_array), constant->name, &const_val);
return 0;
}
add_assoc_zval(return_value, module_names[module_number], &modules[module_number]);
}
- ZVAL_DUP(&const_val, &val->value);
+ ZVAL_COPY_OR_DUP(&const_val, &val->value);
zend_hash_add_new(Z_ARRVAL(modules[module_number]), val->name, &const_val);
} ZEND_HASH_FOREACH_END();
}
/* }}} */
-static void zend_destroy_class_constant_internal(zval *zv) /* {{{ */
-{
- free(Z_PTR_P(zv));
-}
-/* }}} */
-
static zend_string *zend_new_interned_string_safe(zend_string *str) /* {{{ */ {
zend_string *interned_str;
((c->flags & CONST_PERSISTENT) && !(CG(compiler_options) & ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION))
|| (Z_TYPE(c->value) < IS_OBJECT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION))
)) {
- ZVAL_DUP(zv, &c->value);
+ ZVAL_COPY_OR_DUP(zv, &c->value);
return 1;
}
c = zend_lookup_reserved_const(lookup_name, lookup_len);
if (c) {
- ZVAL_DUP(zv, &c->value);
+ ZVAL_COPY_OR_DUP(zv, &c->value);
return 1;
}
}
/* Substitute case-sensitive (or lowercase) persistent class constants */
if (Z_TYPE_P(c) < IS_OBJECT) {
- ZVAL_DUP(zv, c);
+ ZVAL_COPY_OR_DUP(zv, c);
return 1;
}
ce->default_properties_table = NULL;
ce->default_static_members_table = NULL;
zend_hash_init_ex(&ce->properties_info, 8, NULL, (persistent_hashes ? zend_destroy_property_info_internal : NULL), persistent_hashes, 0);
- zend_hash_init_ex(&ce->constants_table, 8, NULL, (persistent_hashes ? zend_destroy_class_constant_internal : NULL), persistent_hashes, 0);
+ zend_hash_init_ex(&ce->constants_table, 8, NULL, NULL, persistent_hashes, 0);
zend_hash_init_ex(&ce->function_table, 8, NULL, ZEND_FUNCTION_DTOR, persistent_hashes, 0);
if (ce->type == ZEND_INTERNAL_CLASS) {
return zend_use_undefined_constant(name, ast->attr, p);
}
zval_ptr_dtor_nogc(p);
- ZVAL_DUP(p, zv);
+ ZVAL_COPY_OR_DUP(p, zv);
} else {
zval tmp;
if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
- if (ce->type & ZEND_INTERNAL_CLASS) {
- if (Z_REFCOUNTED(parent_const->value)) {
- Z_ADDREF(parent_const->value);
- }
- c = pemalloc(sizeof(zend_class_constant), 1);
- memcpy(c, parent_const, sizeof(zend_class_constant));
- } else {
- c = parent_const;
- }
- _zend_hash_append_ptr(&ce->constants_table, name, c);
+ _zend_hash_append_ptr(&ce->constants_table, name, parent_const);
}
}
/* }}} */
ce->default_properties_table = end;
}
src = parent_ce->default_properties_table + parent_ce->default_properties_count;
- do {
- dst--;
- src--;
-#ifdef ZTS
- if (parent_ce->type != ce->type) {
- ZVAL_DUP(dst, src);
+ if (UNEXPECTED(parent_ce->type != ce->type)) {
+ /* User class extends internal */
+ do {
+ dst--;
+ src--;
+ ZVAL_COPY_OR_DUP(dst, src);
if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
continue;
- }
-#endif
-
- ZVAL_COPY(dst, src);
- if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
- } while (dst != end);
+ } while (dst != end);
+ } else {
+ do {
+ dst--;
+ src--;
+ ZVAL_COPY(dst, src);
+ if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) {
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
+ continue;
+ } while (dst != end);
+ }
ce->default_properties_count += parent_ce->default_properties_count;
}
dst = end + parent_ce->default_static_members_count;
ce->default_static_members_table = end;
}
- src = parent_ce->default_static_members_table + parent_ce->default_static_members_count;
- do {
- dst--;
- src--;
- if (parent_ce->type == ZEND_INTERNAL_CLASS) {
+ if (UNEXPECTED(parent_ce->type != ce->type)) {
+ /* User class extends internal */
+ if (UNEXPECTED(zend_update_class_constants(parent_ce) != SUCCESS)) {
+ ZEND_ASSERT(0);
+ }
+ src = CE_STATIC_MEMBERS(parent_ce) + parent_ce->default_static_members_count;
+ do {
+ dst--;
+ src--;
+ ZVAL_MAKE_REF(src);
+ ZVAL_COPY_VALUE(dst, src);
+ Z_ADDREF_P(dst);
+ } while (dst != end);
+ } else if (ce->type == ZEND_USER_CLASS) {
+ src = parent_ce->default_static_members_table + parent_ce->default_static_members_count;
+ do {
+ dst--;
+ src--;
+ ZVAL_MAKE_REF(src);
+ ZVAL_COPY_VALUE(dst, src);
+ Z_ADDREF_P(dst);
+ if (Z_TYPE_P(Z_REFVAL_P(dst)) == IS_CONSTANT_AST) {
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
+ } while (dst != end);
+ } else {
+ src = parent_ce->default_static_members_table + parent_ce->default_static_members_count;
+ do {
+ dst--;
+ src--;
if (!Z_ISREF_P(src)) {
ZVAL_NEW_PERSISTENT_REF(src, src);
}
- } else {
- ZVAL_MAKE_REF(src);
- }
- ZVAL_COPY_VALUE(dst, src);
- Z_ADDREF_P(dst);
- if (Z_TYPE_P(Z_REFVAL_P(dst)) == IS_CONSTANT_AST) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
- } while (dst != end);
+ ZVAL_COPY_VALUE(dst, src);
+ Z_ADDREF_P(dst);
+ } while (dst != end);
+ }
ce->default_static_members_count += parent_ce->default_static_members_count;
if (ce->type == ZEND_USER_CLASS) {
ce->static_members_table = ce->default_static_members_table;
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
- if (ce->type & ZEND_INTERNAL_CLASS) {
- if (Z_REFCOUNTED(c->value)) {
- Z_ADDREF(c->value);
- }
- ct = pemalloc(sizeof(zend_class_constant), 1);
- memcpy(ct, c, sizeof(zend_class_constant));
- } else {
- ct = c;
- }
- zend_hash_update_ptr(&ce->constants_table, name, ct);
+ zend_hash_update_ptr(&ce->constants_table, name, c);
}
}
/* }}} */
zend_class_constant *c;
ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
- zval_internal_ptr_dtor(&c->value);
- if (c->doc_comment && c->ce == ce) {
- zend_string_release(c->doc_comment);
+ if (c->ce == ce) {
+ zval_internal_ptr_dtor(&c->value);
+ if (c->doc_comment) {
+ zend_string_release(c->doc_comment);
+ }
+ free(c);
}
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(&ce->constants_table);
} \
} while (0)
+
+/* ZVAL_COPY_OR_DUP() should be used instead of ZVAL_COPY() and ZVAL_DUP()
+ * in all places where the source may be a persistent zval.
+ */
+#define ZVAL_COPY_OR_DUP(z, v) \
+ do { \
+ zval *_z1 = (z); \
+ const zval *_z2 = (v); \
+ zend_refcounted *_gc = Z_COUNTED_P(_z2); \
+ uint32_t _t = Z_TYPE_INFO_P(_z2); \
+ ZVAL_COPY_VALUE_EX(_z1, _z2, _gc, _t); \
+ if ((_t & (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) != 0) { \
+ if (EXPECTED(!(GC_FLAGS(_gc) & GC_PERSISTENT))) { \
+ GC_ADDREF(_gc); \
+ } else { \
+ _zval_copy_ctor_func(_z1 ZEND_FILE_LINE_CC); \
+ } \
+ } \
+ } while (0)
+
#define ZVAL_DEREF(z) do { \
if (UNEXPECTED(Z_ISREF_P(z))) { \
(z) = Z_REFVAL_P(z); \
CACHE_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2)), c);
}
-#ifdef ZTS
- if (c->flags & CONST_PERSISTENT) {
- ZVAL_DUP(EX_VAR(opline->result.var), &c->value);
- } else {
- ZVAL_COPY(EX_VAR(opline->result.var), &c->value);
- }
-#else
- ZVAL_COPY(EX_VAR(opline->result.var), &c->value);
-#endif
+ ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value);
ZEND_VM_NEXT_OPCODE();
}
if (OP1_TYPE == IS_CONST) {
if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2))))) {
value = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2)));
-#ifdef ZTS
- ce = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1)));
-#endif
break;
} else if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1))))) {
ce = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1)));
}
} while (0);
-#ifdef ZTS
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_DUP(EX_VAR(opline->result.var), value);
- } else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
- }
-#else
- ZVAL_COPY(EX_VAR(opline->result.var), value);
-#endif
+ ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), value);
ZEND_VM_NEXT_OPCODE();
}
if (IS_CONST == IS_CONST) {
if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2))))) {
value = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2)));
-#ifdef ZTS
- ce = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1)));
-#endif
break;
} else if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1))))) {
ce = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1)));
}
} while (0);
-#ifdef ZTS
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_DUP(EX_VAR(opline->result.var), value);
- } else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
- }
-#else
- ZVAL_COPY(EX_VAR(opline->result.var), value);
-#endif
+ ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), value);
ZEND_VM_NEXT_OPCODE();
}
if (IS_VAR == IS_CONST) {
if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2))))) {
value = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2)));
-#ifdef ZTS
- ce = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1)));
-#endif
break;
} else if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1))))) {
ce = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1)));
}
} while (0);
-#ifdef ZTS
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_DUP(EX_VAR(opline->result.var), value);
- } else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
- }
-#else
- ZVAL_COPY(EX_VAR(opline->result.var), value);
-#endif
+ ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), value);
ZEND_VM_NEXT_OPCODE();
}
CACHE_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2)), c);
}
-#ifdef ZTS
- if (c->flags & CONST_PERSISTENT) {
- ZVAL_DUP(EX_VAR(opline->result.var), &c->value);
- } else {
- ZVAL_COPY(EX_VAR(opline->result.var), &c->value);
- }
-#else
- ZVAL_COPY(EX_VAR(opline->result.var), &c->value);
-#endif
+ ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value);
ZEND_VM_NEXT_OPCODE();
}
if (IS_UNUSED == IS_CONST) {
if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2))))) {
value = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op2)));
-#ifdef ZTS
- ce = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1)));
-#endif
break;
} else if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1))))) {
ce = CACHED_PTR(Z_CACHE_SLOT_P(RT_CONSTANT(opline, opline->op1)));
}
} while (0);
-#ifdef ZTS
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_DUP(EX_VAR(opline->result.var), value);
- } else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
- }
-#else
- ZVAL_COPY(EX_VAR(opline->result.var), value);
-#endif
+ ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), value);
ZEND_VM_NEXT_OPCODE();
}
break;
}
} else {
- ZVAL_COPY_VALUE(&t, c);
- zval_copy_ctor(&t);
+ ZVAL_COPY_OR_DUP(&t, c);
}
if (opline->op1_type == IS_CONST) {
}
GET_REFLECTION_OBJECT_PTR(ref);
- ZVAL_DUP(return_value, &ref->value);
+ ZVAL_COPY_OR_DUP(return_value, &ref->value);
if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) {
zval_update_constant_ex(return_value, ref->ce);
}
/* copy: enforce read only access */
ZVAL_DEREF(prop);
- ZVAL_DUP(&prop_copy, prop);
+ ZVAL_COPY_OR_DUP(&prop_copy, prop);
/* this is necessary to make it able to work with default array
* properties, returned to user */
zend_class_entry *ce;
zend_string *key;
zend_class_constant *c;
- zval *val;
+ zval val;
if (zend_parse_parameters_none() == FAILURE) {
return;
zend_array_destroy(Z_ARRVAL_P(return_value));
return;
}
- val = zend_hash_add_new(Z_ARRVAL_P(return_value), key, &c->value);
- Z_TRY_ADDREF_P(val);
+ ZVAL_COPY_OR_DUP(&val, &c->value);
+ zend_hash_add_new(Z_ARRVAL_P(return_value), key, &val);
} ZEND_HASH_FOREACH_END();
}
/* }}} */
if ((c = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) {
RETURN_FALSE;
}
- ZVAL_DUP(return_value, &c->value);
+ ZVAL_COPY_OR_DUP(return_value, &c->value);
}
/* }}} */
int number = va_arg(args, int);
if (number == constant->module_number) {
- ZVAL_DUP(&const_val, &constant->value);
+ ZVAL_COPY_OR_DUP(&const_val, &constant->value);
zend_hash_update(Z_ARRVAL_P(retval), constant->name, &const_val);
}
return 0;
scope = zend_get_executed_scope();
c = zend_get_constant_ex(const_name, scope, ZEND_FETCH_CLASS_SILENT);
if (c) {
- ZVAL_DUP(return_value, c);
+ ZVAL_COPY_OR_DUP(return_value, c);
if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) {
if (UNEXPECTED(zval_update_constant_ex(return_value, scope) != SUCCESS)) {
return;