Fix class constant fetching

If a class is extended after the constant fetch has been cached
the cached value will be turned into a reference. On the next
fetch the polymorphic cache will return this reference, which
will be directly returned. The object assignment code then
dereferences this result and performs a shallow copy, which is
invalid for references. This subsequently leads to the constant
value being prematurely freed.

This is fixed by dereferencing the value returned from the
polymorphic cache. Furthermore the incorrect dereference from
in the object assignment code is replaced with an assertion that
we're dealing with a non-reference, so ensure that this kind of
problem cannot go unnoticed in the future.
This commit is contained in:
Nikita Popov 2014-05-29 00:02:13 +02:00
parent afd8a02160
commit d9a35c7e97
4 changed files with 40 additions and 13 deletions

View File

@ -0,0 +1,31 @@
--TEST--
Conversion of a class constant to a reference after it has been cached
--FILE--
<?php
class Test {
const TEST = 'TEST';
private $prop;
public function readConst() {
$this->prop = self::TEST;
}
}
$obj = new Test;
$obj->readConst();
unset($obj);
var_dump(Test::TEST);
eval('class Test2 extends Test {}');
$obj = new Test;
$obj->readConst();
unset($obj);
var_dump(Test::TEST);
?>
--EXPECT--
string(4) "TEST"
string(4) "TEST"

View File

@ -724,7 +724,7 @@ static inline void zend_assign_to_object(zval *retval, zval *object_ptr, zval *p
/* separate our value if necessary */
if (value_type == IS_TMP_VAR) {
ZVAL_DEREF(value);
ZEND_ASSERT(Z_TYPE_P(value) != IS_REFERENCE);
ZVAL_COPY_VALUE(&tmp, value);
value = &tmp;
} else if (value_type == IS_CONST) {

View File

@ -3631,15 +3631,14 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST)
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(opline->op2.zv), ce)) != NULL) {
ZVAL_DEREF(value);
ZVAL_DUP(EX_VAR(opline->result.var), value);
goto constant_fetch_end;
}
}
if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(opline->op2.zv))) != NULL)) {
if (Z_ISREF_P(value)) {
value = Z_REFVAL_P(value);
}
ZVAL_DEREF(value);
if (Z_CONSTANT_P(value)) {
zend_class_entry *old_scope = EG(scope);

View File

@ -3903,15 +3903,14 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(opline->op2.zv), ce)) != NULL) {
ZVAL_DEREF(value);
ZVAL_DUP(EX_VAR(opline->result.var), value);
goto constant_fetch_end;
}
}
if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(opline->op2.zv))) != NULL)) {
if (Z_ISREF_P(value)) {
value = Z_REFVAL_P(value);
}
ZVAL_DEREF(value);
if (Z_CONSTANT_P(value)) {
zend_class_entry *old_scope = EG(scope);
@ -15346,15 +15345,14 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(opline->op2.zv), ce)) != NULL) {
ZVAL_DEREF(value);
ZVAL_DUP(EX_VAR(opline->result.var), value);
goto constant_fetch_end;
}
}
if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(opline->op2.zv))) != NULL)) {
if (Z_ISREF_P(value)) {
value = Z_REFVAL_P(value);
}
ZVAL_DEREF(value);
if (Z_CONSTANT_P(value)) {
zend_class_entry *old_scope = EG(scope);
@ -24557,15 +24555,14 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(opline->op2.zv), ce)) != NULL) {
ZVAL_DEREF(value);
ZVAL_DUP(EX_VAR(opline->result.var), value);
goto constant_fetch_end;
}
}
if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(opline->op2.zv))) != NULL)) {
if (Z_ISREF_P(value)) {
value = Z_REFVAL_P(value);
}
ZVAL_DEREF(value);
if (Z_CONSTANT_P(value)) {
zend_class_entry *old_scope = EG(scope);