]> granicus.if.org Git - php/commitdiff
Fix class constant fetching
authorNikita Popov <nikic@php.net>
Wed, 28 May 2014 22:02:13 +0000 (00:02 +0200)
committerNikita Popov <nikic@php.net>
Wed, 28 May 2014 22:15:50 +0000 (00:15 +0200)
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.

Zend/tests/class_constant_to_reference_cached.phpt [new file with mode: 0644]
Zend/zend_execute.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/Zend/tests/class_constant_to_reference_cached.phpt b/Zend/tests/class_constant_to_reference_cached.phpt
new file mode 100644 (file)
index 0000000..b752226
--- /dev/null
@@ -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"
index e0653f9b631be6bafaaa9ba103967d328d586b00..9cc0f3a4cf914fd3c17ab81c5653149a20526a2f 100644 (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) {
index cba6e77d2ba3dde80a2ef059fa101e1396493704..5042c065ba41d6e1dc067d0ad4125deba0b32544 100644 (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);
 
index 2e7184e30d5fb69d6329636872f7839d7b8452ea..5b5c69923a00b1d791583e0d668c4abeed3811de 100644 (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);