mirror of
https://github.com/php/php-src.git
synced 2025-08-19 08:49:28 +02:00
Fixed bug #60536 (Traits Segfault)
# this is a tough one, I think I should explain # Zend use zend_object->properties_table both as zval ** and zval *** # if a zend_object->properties is not initialized, the properties_table is zval ** # while in rebuild_object_properties, zend will store the zval ** to zend_object->properties # then stash the zval ***(ie, zobj->properties_table[0] is zval ** now) to zobj->properties_table[0] # so when a zend_object inherit form multi parent and these parent have a same property_info->offset # properties, will result in a repeat zval **->zval ** transform, which will lead to a segmentfault # *may be* this fix is not the best fix, we should not use this tricky way, and rewrite this mechanism.
This commit is contained in:
parent
a099e0d2f6
commit
707f658c33
6 changed files with 201 additions and 2 deletions
26
Zend/tests/bug60536_001.phpt
Normal file
26
Zend/tests/bug60536_001.phpt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--TEST--
|
||||||
|
Bug #60536 (Traits Segfault)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
trait T { private $x = 0; }
|
||||||
|
class X {
|
||||||
|
use T;
|
||||||
|
}
|
||||||
|
class Y extends X {
|
||||||
|
use T;
|
||||||
|
function x() {
|
||||||
|
return ++$this->x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Z extends Y {
|
||||||
|
function z() {
|
||||||
|
return ++$this->x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$a = new Z();
|
||||||
|
$a->x();
|
||||||
|
echo "DONE";
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Strict Standards: X and T define the same property ($x) in the composition of Y. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed in %sbug60536_001.php on line %d
|
||||||
|
DONE
|
40
Zend/tests/bug60536_002.phpt
Normal file
40
Zend/tests/bug60536_002.phpt
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
--TEST--
|
||||||
|
The same rules are applied for properties that are defined in the class hierarchy. Thus, if the properties are compatible, a notice is issued, if not a fatal error occures. (relevant with #60536)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
private $hello;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait THello1 {
|
||||||
|
private $hello;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "PRE-CLASS-GUARD\n";
|
||||||
|
class Notice extends Base {
|
||||||
|
use THello1;
|
||||||
|
private $hello;
|
||||||
|
}
|
||||||
|
echo "POST-CLASS-GUARD\n";
|
||||||
|
|
||||||
|
// now we do the test for a fatal error
|
||||||
|
|
||||||
|
class TraitsTest {
|
||||||
|
use THello1;
|
||||||
|
public $hello;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "POST-CLASS-GUARD2\n";
|
||||||
|
|
||||||
|
$t = new TraitsTest;
|
||||||
|
$t->hello = "foo";
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
PRE-CLASS-GUARD
|
||||||
|
|
||||||
|
Strict Standards: Notice and THello1 define the same property ($hello) in the composition of Notice. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed in %s on line %d
|
||||||
|
POST-CLASS-GUARD
|
||||||
|
|
||||||
|
Fatal error: TraitsTest and THello1 define the same property ($hello) in the composition of TraitsTest. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
|
49
Zend/tests/bug60536_003.phpt
Normal file
49
Zend/tests/bug60536_003.phpt
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
--TEST--
|
||||||
|
Private (relevant to #60536)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
|
||||||
|
class BaseWithPropA {
|
||||||
|
private $hello = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait AHelloProperty {
|
||||||
|
private $hello = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseWithTPropB {
|
||||||
|
use AHelloProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubclassA extends BaseWithPropA {
|
||||||
|
use AHelloProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubclassB extends BaseWithTPropB {
|
||||||
|
use AHelloProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
$a = new SubclassA;
|
||||||
|
var_dump($a);
|
||||||
|
|
||||||
|
$b = new SubclassB;
|
||||||
|
var_dump($b);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Strict Standards: BaseWithPropA and AHelloProperty define the same property ($hello) in the composition of SubclassA. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed in %sbug60536_003.php on line %d
|
||||||
|
|
||||||
|
Strict Standards: BaseWithTPropB and AHelloProperty define the same property ($hello) in the composition of SubclassB. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed in %sbug60536_003.php on line %d
|
||||||
|
object(SubclassA)#%d (2) {
|
||||||
|
["hello":"SubclassA":private]=>
|
||||||
|
int(0)
|
||||||
|
["hello":"BaseWithPropA":private]=>
|
||||||
|
int(0)
|
||||||
|
}
|
||||||
|
object(SubclassB)#%d (2) {
|
||||||
|
["hello":"SubclassB":private]=>
|
||||||
|
int(0)
|
||||||
|
["hello":"BaseWithTPropB":private]=>
|
||||||
|
int(0)
|
||||||
|
}
|
39
Zend/tests/bug60536_004.phpt
Normal file
39
Zend/tests/bug60536_004.phpt
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
--TEST--
|
||||||
|
Introducing new private variables of the same name in a subclass is ok, and does not lead to any output. That is consitent with normal inheritance handling. (relevant to #60536)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
private $hello;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait THello1 {
|
||||||
|
private $hello;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we use the trait, which happens to introduce another private variable
|
||||||
|
// but they are distinct, and not related to each other, so no warning.
|
||||||
|
echo "PRE-CLASS-GUARD\n";
|
||||||
|
class SameNameInSubClassNoNotice extends Base {
|
||||||
|
use THello1;
|
||||||
|
}
|
||||||
|
echo "POST-CLASS-GUARD\n";
|
||||||
|
|
||||||
|
// now the same with a class that defines the property itself,
|
||||||
|
// that should give the expected strict warning.
|
||||||
|
|
||||||
|
class Notice extends Base {
|
||||||
|
use THello1;
|
||||||
|
private $hello;
|
||||||
|
}
|
||||||
|
echo "POST-CLASS-GUARD2\n";
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
PRE-CLASS-GUARD
|
||||||
|
|
||||||
|
Strict Standards: Base and THello1 define the same property ($hello) in the composition of SameNameInSubClassNoNotice. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed in %sbug60536_004.php on line %d
|
||||||
|
POST-CLASS-GUARD
|
||||||
|
|
||||||
|
Strict Standards: Notice and THello1 define the same property ($hello) in the composition of Notice. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed in %sbug60536_004.php on line %d
|
||||||
|
POST-CLASS-GUARD2
|
38
Zend/tests/bug60536_005.phpt
Normal file
38
Zend/tests/bug60536_005.phpt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
--TEST--
|
||||||
|
Introducing new private variables of the same name in a subclass is ok, and does not lead to any output. That is consitent with normal inheritance handling. (relevant to #60536)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
protected $hello;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait THello1 {
|
||||||
|
protected $hello;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protected and public are handle more strict with a warning then what is
|
||||||
|
// expected from normal inheritance since they can have easier coliding semantics
|
||||||
|
echo "PRE-CLASS-GUARD\n";
|
||||||
|
class SameNameInSubClassProducesNotice extends Base {
|
||||||
|
use THello1;
|
||||||
|
}
|
||||||
|
echo "POST-CLASS-GUARD\n";
|
||||||
|
|
||||||
|
// now the same with a class that defines the property itself, too.
|
||||||
|
|
||||||
|
class Notice extends Base {
|
||||||
|
use THello1;
|
||||||
|
protected $hello;
|
||||||
|
}
|
||||||
|
echo "POST-CLASS-GUARD2\n";
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
PRE-CLASS-GUARD
|
||||||
|
|
||||||
|
Strict Standards: Base and THello1 define the same property ($hello) in the composition of SameNameInSubClassProducesNotice. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed in %s on line %d
|
||||||
|
POST-CLASS-GUARD
|
||||||
|
|
||||||
|
Strict Standards: Notice and THello1 define the same property ($hello) in the composition of Notice. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed in %s on line %d
|
||||||
|
POST-CLASS-GUARD2
|
|
@ -62,6 +62,7 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */
|
||||||
ALLOC_HASHTABLE(zobj->properties);
|
ALLOC_HASHTABLE(zobj->properties);
|
||||||
zend_hash_init(zobj->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
|
zend_hash_init(zobj->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
|
||||||
if (ce->default_properties_count) {
|
if (ce->default_properties_count) {
|
||||||
|
int *flags = ecalloc(ce->default_properties_count, sizeof(int));
|
||||||
for (zend_hash_internal_pointer_reset_ex(&ce->properties_info, &pos);
|
for (zend_hash_internal_pointer_reset_ex(&ce->properties_info, &pos);
|
||||||
zend_hash_get_current_data_ex(&ce->properties_info, (void**)&prop_info, &pos) == SUCCESS;
|
zend_hash_get_current_data_ex(&ce->properties_info, (void**)&prop_info, &pos) == SUCCESS;
|
||||||
zend_hash_move_forward_ex(&ce->properties_info, &pos)) {
|
zend_hash_move_forward_ex(&ce->properties_info, &pos)) {
|
||||||
|
@ -70,6 +71,7 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */
|
||||||
prop_info->offset >= 0 &&
|
prop_info->offset >= 0 &&
|
||||||
zobj->properties_table[prop_info->offset]) {
|
zobj->properties_table[prop_info->offset]) {
|
||||||
zend_hash_quick_add(zobj->properties, prop_info->name, prop_info->name_length+1, prop_info->h, (void**)&zobj->properties_table[prop_info->offset], sizeof(zval*), (void**)&zobj->properties_table[prop_info->offset]);
|
zend_hash_quick_add(zobj->properties, prop_info->name, prop_info->name_length+1, prop_info->h, (void**)&zobj->properties_table[prop_info->offset], sizeof(zval*), (void**)&zobj->properties_table[prop_info->offset]);
|
||||||
|
flags[prop_info->offset] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (ce->parent && ce->parent->default_properties_count) {
|
while (ce->parent && ce->parent->default_properties_count) {
|
||||||
|
@ -82,11 +84,16 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */
|
||||||
(prop_info->flags & ZEND_ACC_PRIVATE) != 0 &&
|
(prop_info->flags & ZEND_ACC_PRIVATE) != 0 &&
|
||||||
prop_info->offset >= 0 &&
|
prop_info->offset >= 0 &&
|
||||||
zobj->properties_table[prop_info->offset]) {
|
zobj->properties_table[prop_info->offset]) {
|
||||||
|
if (UNEXPECTED(flags[prop_info->offset])) {
|
||||||
|
zend_hash_quick_add(zobj->properties, prop_info->name, prop_info->name_length+1, prop_info->h, (void**)zobj->properties_table[prop_info->offset], sizeof(zval*), (void**)&zobj->properties_table[prop_info->offset]);
|
||||||
|
} else {
|
||||||
zend_hash_quick_add(zobj->properties, prop_info->name, prop_info->name_length+1, prop_info->h, (void**)&zobj->properties_table[prop_info->offset], sizeof(zval*), (void**)&zobj->properties_table[prop_info->offset]);
|
zend_hash_quick_add(zobj->properties, prop_info->name, prop_info->name_length+1, prop_info->h, (void**)&zobj->properties_table[prop_info->offset], sizeof(zval*), (void**)&zobj->properties_table[prop_info->offset]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
efree(flags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue