Skip to content

Commit

Permalink
Adding and removing properties of classes (in PHP 5.4) whose objects …
Browse files Browse the repository at this point in the history
…have dynamic properties have been corrected. Issues related to memory management have been eliminated. New tests were added. (zenovich#40)
  • Loading branch information
zenovich committed Oct 3, 2012
1 parent a78fa7a commit cbcfa9b
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 40 deletions.
6 changes: 4 additions & 2 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Execute code in restricted environment (sandboxing).
+ Adding and redefining functions and methods, which return references, were fully implemented
New optional argument 'return_ref' of functions runkit_function_add and runkit_function_redefine was introduced
New constant RUNKIT_ACC_RETURN_REFERENCE was introduced for use with functions runkit_method_add and runkit_method_redefine
+ Properties adding, removing, and importing were reworked for proper inheritance (including objects in PHP5+)

Critical fixes:
* Highly probable crashes on using Reflection objects after modifying removing or renaming of functions, methods, and properties,
Expand All @@ -53,12 +54,11 @@ Execute code in restricted environment (sandboxing).
* Adding of non-lowercase default properties was fixed (removed lowercasing)
* Skip the leading slash in class names
* Copying of functions was reworked
* Properties adding, removing and importing were corrected for right inheritance
* Converting of input parameters (class names & function names) to lowecase was eliminated
* runkit_method_redefine function now sets the 'prototype' field for the method
and its descendants (Thanks to Anthony Dovgal)
* Freeing already freed memory on importing of a non-existent file was eliminated
* Test for correctness of runkit.superglobals feature was added
* A test for correctness of runkit.superglobals feature was added
* Tests for correctness of inheritance of properties were added
* Tests for correctness of adding static properties were added

Expand Down Expand Up @@ -186,8 +186,10 @@ Execute code in restricted environment (sandboxing).
<file name="namespaces.phpt" role="test" />
<file name="runkit_default_property_add.phpt" role="test" />
<file name="runkit_default_property_add4.phpt" role="test" />
<file name="runkit_default_property_add_and_remove_for_class_with_dynamic_properties.phpt" role="test" />
<file name="runkit_default_property_add_to_subclasses.phpt" role="test" />
<file name="runkit_default_property_remove.phpt" role="test" />
<file name="runkit_default_property_remove_and_add_for_class_with_dynamic_properties.phpt" role="test" />
<file name="runkit_default_property_remove_and_reflection.phpt" role="test" />
<file name="runkit_default_property_remove_from_subclasses.phpt" role="test" />
<file name="runkit_default_property_remove_from_subclasses4.phpt" role="test" />
Expand Down
3 changes: 2 additions & 1 deletion php_runkit.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@ int php_runkit_update_children_consts(RUNKIT_53_TSRMLS_ARG(void *pDest), int num
/* runkit_props.c */
int php_runkit_update_children_def_props(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce), int num_args, va_list args, zend_hash_key *hash_key);
int php_runkit_def_prop_add_int(zend_class_entry *ce, const char *propname, int propname_len, zval *copyval, long visibility, const char *doc_comment, int doc_comment_len, zend_class_entry *definer_class, int override TSRMLS_DC);
int php_runkit_def_prop_remove_int(zend_class_entry *ce, const char *propname, int propname_len, zend_class_entry *definer_class TSRMLS_DC);
int php_runkit_def_prop_remove_int(zend_class_entry *ce, const char *propname, int propname_len, zend_class_entry *definer_class,
int parent_offset, zend_bool was_static TSRMLS_DC);
#ifdef ZEND_ENGINE_2
void php_runkit_remove_property_from_reflection_objects(zend_class_entry *ce, const char *prop_name, int prop_name_len TSRMLS_DC);
#endif
Expand Down
4 changes: 2 additions & 2 deletions runkit_import.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ static int php_runkit_import_class_static_props(zend_class_entry *dce, zend_clas
if (zend_hash_find(&dce->properties_info, key, key_len, (void*) &ex_property_info_ptr) == SUCCESS && ex_property_info_ptr) {
if (override) {
if (!(ex_property_info_ptr->flags & ZEND_ACC_STATIC)) {
if (php_runkit_def_prop_remove_int(dce, key, key_len - 1, NULL TSRMLS_CC) != SUCCESS) {
if (php_runkit_def_prop_remove_int(dce, key, key_len - 1, NULL, -1, 0 TSRMLS_CC) != SUCCESS) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to import %s::$%s (cannot remove old member)", dce->name, key);
goto import_st_prop_skip;
}
Expand Down Expand Up @@ -377,7 +377,7 @@ static int php_runkit_import_class_props(zend_class_entry *dce, zend_class_entry
if (override) {
if (php_runkit_def_prop_remove_int(dce, property_info_ptr->name,
property_info_ptr->name_length,
NULL TSRMLS_CC) != SUCCESS) {
NULL, -1, 0 TSRMLS_CC) != SUCCESS) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Unable to remove old property %s%s%s, not importing",
dce->name, (property_info_ptr->flags & ZEND_ACC_STATIC) ? "::$" : "->",
Expand Down
128 changes: 93 additions & 35 deletions runkit_props.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ int php_runkit_remove_children_def_props(RUNKIT_53_TSRMLS_ARG(zend_class_entry *
char *pname = va_arg(args, char*);
int pname_len = va_arg(args, int);
zend_class_entry *definer_class = va_arg(args, zend_class_entry*);
int parent_offset = va_arg(args, int);
zend_bool was_static = va_arg(args, int);

RUNKIT_UNDER53_TSRMLS_FETCH();

Expand All @@ -71,21 +73,23 @@ int php_runkit_remove_children_def_props(RUNKIT_53_TSRMLS_ARG(zend_class_entry *
return ZEND_HASH_APPLY_KEEP;
}

php_runkit_def_prop_remove_int(ce, pname, pname_len, definer_class TSRMLS_CC);
php_runkit_def_prop_remove_int(ce, pname, pname_len, definer_class, parent_offset, was_static TSRMLS_CC);
return ZEND_HASH_APPLY_KEEP;
}
/* }}} */

#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
/* {{{ php_runkit_remove_private_property_from_childs
/* {{{ php_runkit_remove_shadowed_property_from_childs
Clean private properties by offset */
int php_runkit_remove_private_property_from_childs(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce), int num_args, va_list args, zend_hash_key *hash_key)
int php_runkit_remove_shadowed_property_from_childs(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce), int num_args, va_list args, zend_hash_key *hash_key)
{
zend_class_entry *parent_class = va_arg(args, zend_class_entry*);
char *pname = va_arg(args, char*);
int pname_len = va_arg(args, int);
int offset = va_arg(args, int);
zend_bool is_static = va_arg(args, int);
int i;
zval **table;

ce = *((zend_class_entry**)ce);

Expand All @@ -94,15 +98,17 @@ int php_runkit_remove_private_property_from_childs(RUNKIT_53_TSRMLS_ARG(zend_cla
return ZEND_HASH_APPLY_KEEP;
}

if (ce->default_properties_table[offset]) {
zval_ptr_dtor(&ce->default_properties_table[offset]);
ce->default_properties_table[offset] = NULL;
table = is_static ? ce->default_static_members_table : ce->default_properties_table;
if (table[offset]) {
zval_ptr_dtor(&table[offset]);
table[offset] = NULL;
}
zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_remove_private_property_from_childs,
4, ce, pname, pname_len, offset);

zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_remove_shadowed_property_from_childs,
5, ce, pname, pname_len, offset, is_static);
php_runkit_remove_property_from_reflection_objects(ce, pname, pname_len TSRMLS_CC);

if (!EG(objects_store).object_buckets) {
if (is_static || !EG(objects_store).object_buckets) {
return ZEND_HASH_APPLY_KEEP;
}
for (i = 1; i < EG(objects_store).top ; i++) {
Expand All @@ -111,11 +117,13 @@ int php_runkit_remove_private_property_from_childs(RUNKIT_53_TSRMLS_ARG(zend_cla
zend_object *object;
object = (zend_object *) EG(objects_store).object_buckets[i].bucket.obj.object;
if (object->ce == ce) {
if (object->properties_table) {
if (object->properties_table[offset]) {
if (object->properties_table[offset]) {
if (!object->properties) {
zval_ptr_dtor(&object->properties_table[offset]);
object->properties_table[offset] = NULL;
} else {
zend_hash_del(object->properties, pname, pname_len+1);
}
object->properties_table[offset] = NULL;
}
}
}
Expand All @@ -133,17 +141,20 @@ int php_runkit_def_prop_add_int(zend_class_entry *ce, const char *propname, int
#if PHP_MAJOR_VERSION >= 5
int i;
#endif
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
int offset;
#endif
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 3
HashTable *symt;
#endif
#if PHP_MAJOR_VERSION >= 5
zend_property_info *prop_info_ptr;
zend_property_info *prop_info_ptr = NULL;
long h = zend_get_hash_value((char *) propname, propname_len + 1);
#endif
zval *pcopyval = copyval;

#if PHP_MAJOR_VERSION >= 5
if ((visibility & ZEND_ACC_PRIVATE) && (visibility & ZEND_ACC_STATIC) && definer_class && definer_class != ce) {
if ((visibility & ZEND_ACC_PRIVATE) && (visibility & ZEND_ACC_STATIC) && definer_class && definer_class != ce) {
return SUCCESS;
}

Expand Down Expand Up @@ -171,9 +182,13 @@ int php_runkit_def_prop_add_int(zend_class_entry *ce, const char *propname, int
ce->name, (prop_info_ptr->flags & ZEND_ACC_STATIC) ? "::$" : "->", propname);
return FAILURE;
} else {
php_runkit_def_prop_remove_int(ce, propname, propname_len, NULL TSRMLS_CC);
php_runkit_def_prop_remove_int(ce, propname, propname_len, NULL, -1, 0 TSRMLS_CC);
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
#endif
}
}
prop_info_ptr = NULL;
#else
if (zend_hash_exists(&ce->default_properties, (char *) propname, propname_len + 1)) {
if (override) {
Expand Down Expand Up @@ -248,6 +263,15 @@ int php_runkit_def_prop_add_int(zend_class_entry *ce, const char *propname, int
propname, propname_len, visibility, definer_class, override);

#if PHP_MAJOR_VERSION >= 5
if (!prop_info_ptr && zend_hash_quick_find(&ce->properties_info, (char *) propname, propname_len + 1, h, (void*) &prop_info_ptr) != SUCCESS) {
zval_ptr_dtor(&pcopyval);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find just added property's info");
return FAILURE;
}
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
offset = prop_info_ptr->offset;
#endif

if ((visibility & ZEND_ACC_STATIC) || !EG(objects_store).object_buckets) {
return SUCCESS;
}
Expand All @@ -259,22 +283,27 @@ int php_runkit_def_prop_add_int(zend_class_entry *ce, const char *propname, int
if (object->ce == ce) {
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
if (!object->properties_table) {
object->properties_table = pemalloc(sizeof(void*) * ce->default_properties_count, 0);
object->properties_table = pemalloc(sizeof(void*) * (offset + 1), 0);
} else {
object->properties_table = perealloc(object->properties_table, sizeof(void*) * ce->default_properties_count, 0);
object->properties_table = perealloc(object->properties_table, sizeof(void*) * (offset + 1), 0);
}
object->properties_table[ce->default_properties_count-1] = ce->default_properties_table[ce->default_properties_count-1];
if (object->properties_table[ce->default_properties_count-1]) {
Z_ADDREF_P(object->properties_table[ce->default_properties_count-1]);

if (ce->default_properties_table[offset]) {
if (!object->properties) {
object->properties_table[offset] = ce->default_properties_table[offset];
} else {
zend_hash_quick_update(object->properties, propname, propname_len+1, h, &ce->default_properties_table[offset], sizeof(zval *), (void**)&object->properties_table[offset]);
}
Z_ADDREF_P(ce->default_properties_table[offset]);
}
#else
if (!object->properties) {
ALLOC_HASHTABLE(object->properties);
zend_hash_init(object->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
}
Z_ADDREF_P(pcopyval);
zend_hash_quick_del(object->properties, (char *) propname, propname_len + 1, h);
zend_hash_quick_add(object->properties, (char *) propname, propname_len + 1, h, &pcopyval, sizeof(zval *), NULL);
zend_hash_quick_del(object->properties, (char *) prop_info_ptr->name, prop_info_ptr->name_length + 1, prop_info_ptr->h);
zend_hash_quick_add(object->properties, (char *) prop_info_ptr->name, prop_info_ptr->name_length + 1, prop_info_ptr->h, &pcopyval, sizeof(zval *), NULL);
#endif // (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
}
}
Expand Down Expand Up @@ -341,7 +370,8 @@ static int php_runkit_def_prop_add(char *classname, int classname_len, char *pro
/* }}} */

/* {{{ php_runkit_def_prop_remove */
int php_runkit_def_prop_remove_int(zend_class_entry *ce, const char *propname, int propname_len, zend_class_entry *definer_class TSRMLS_DC)
int php_runkit_def_prop_remove_int(zend_class_entry *ce, const char *propname, int propname_len, zend_class_entry *definer_class,
int parent_offset, zend_bool was_static TSRMLS_DC)
{
#if PHP_MAJOR_VERSION == 4
/* Resolve the property's name */
Expand All @@ -353,13 +383,14 @@ int php_runkit_def_prop_remove_int(zend_class_entry *ce, const char *propname, i
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to remove the property from class: %s::%s", ce->name, propname);
return FAILURE;
}
zend_hash_apply_with_arguments(EG(class_table), (apply_func_args_t)php_runkit_remove_children_def_props, 4, ce, propname, propname_len, NULL);
zend_hash_apply_with_arguments(EG(class_table), (apply_func_args_t)php_runkit_remove_children_def_props, 6, ce, propname, propname_len, -1, 0, NULL);
#else
int i;
long h;
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
int offset;
#endif
int flags;
zend_property_info *property_info_ptr;

h = zend_get_hash_value((char *) propname, propname_len + 1);
Expand All @@ -378,13 +409,20 @@ int php_runkit_def_prop_remove_int(zend_class_entry *ce, const char *propname, i
}
efree(private);
zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_remove_children_def_props,
4, ce, propname, propname_len, definer_class);
6, ce, propname, propname_len, definer_class, -1, 0);
#elif (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
if (parent_offset >= 0 && parent_offset != property_info_ptr->offset) {
zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)),
(apply_func_args_t)php_runkit_remove_shadowed_property_from_childs,
5, ce, propname, propname_len, parent_offset, was_static);
}
#endif // PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4
return SUCCESS;
} else if (!definer_class) {
definer_class = property_info_ptr->ce;
}
if (property_info_ptr->flags & ZEND_ACC_STATIC) {
was_static = 1;
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
if (ce->default_static_members_table[property_info_ptr->offset]) {
zval_ptr_dtor(&ce->default_static_members_table[property_info_ptr->offset]);
Expand All @@ -398,6 +436,7 @@ int php_runkit_def_prop_remove_int(zend_class_entry *ce, const char *propname, i
}
#endif // (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
} else {
was_static = 0;
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
if (ce->default_properties_table[property_info_ptr->offset]) {
zval_ptr_dtor(&ce->default_properties_table[property_info_ptr->offset]);
Expand All @@ -412,27 +451,41 @@ int php_runkit_def_prop_remove_int(zend_class_entry *ce, const char *propname, i
#endif // (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
}

flags = property_info_ptr->flags;
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
offset = property_info_ptr->offset;
if ((property_info_ptr->flags & ZEND_ACC_PRIVATE) && !(property_info_ptr->flags & ZEND_ACC_STATIC) && offset >= 0) {
zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_remove_private_property_from_childs,
4, ce, propname, propname_len, offset);
} else

if (property_info_ptr->flags & ZEND_ACC_PRIVATE) {
if (offset >= 0) {
zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)),
(apply_func_args_t)php_runkit_remove_shadowed_property_from_childs,
5, ce, propname, propname_len, offset, property_info_ptr->flags & ZEND_ACC_STATIC);
}
}
#endif
zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_remove_children_def_props,
4, ce, propname, propname_len, definer_class);
6, ce, propname, propname_len, definer_class,
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
offset, was_static
#else
-1, 0
#endif
);

php_runkit_remove_property_from_reflection_objects(ce, propname, propname_len TSRMLS_CC);
if (zend_hash_quick_del(&ce->properties_info, (char *) propname, propname_len + 1, h) != SUCCESS) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to remove the property %s::%s", ce->name, propname);
return FAILURE;
}
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
#endif
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::%s does not exist", ce->name, propname);
return FAILURE;
}

if (!EG(objects_store).object_buckets) {
if ((flags & ZEND_ACC_STATIC) || !EG(objects_store).object_buckets) {
return SUCCESS;
}
for (i = 1; i < EG(objects_store).top ; i++) {
Expand All @@ -442,11 +495,13 @@ int php_runkit_def_prop_remove_int(zend_class_entry *ce, const char *propname, i
object = (zend_object *) EG(objects_store).object_buckets[i].bucket.obj.object;
if (object->ce == ce) {
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
if (object->properties_table) {
if (object->properties_table[offset]) {
if (object->properties_table[offset]) {
if (!object->properties) {
zval_ptr_dtor(&object->properties_table[offset]);
object->properties_table[offset] = NULL;
} else {
zend_hash_quick_del(object->properties, propname, propname_len+1, h);
}
object->properties_table[offset] = NULL;
}
#else
if (object->properties) {
Expand Down Expand Up @@ -476,7 +531,10 @@ static int php_runkit_def_prop_remove(char *classname, int classname_len, char *
return FAILURE;
}

return php_runkit_def_prop_remove_int(ce, propname, propname_len, NULL TSRMLS_CC);
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
#endif
return php_runkit_def_prop_remove_int(ce, propname, propname_len, NULL, -1, 0 TSRMLS_CC);
}
/* }}} */

Expand Down
Loading

0 comments on commit cbcfa9b

Please sign in to comment.