Skip to content

Instantly share code, notes, and snippets.

@chtg chtg/.patch Secret
Last active Oct 12, 2016

Embed
What would you like to do?
diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h
index 95af483..1636416 100644
--- a/ext/standard/php_var.h
+++ b/ext/standard/php_var.h
@@ -45,6 +45,8 @@ struct php_unserialize_data {
void *last;
void *first_dtor;
void *last_dtor;
+ void *first_wakeup;
+ void *last_wakeup;
};
typedef struct php_unserialize_data* php_unserialize_data_t;
@@ -102,10 +104,12 @@ do { \
do { \
/* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ \
if (BG(serialize_lock) || !BG(unserialize).level) { \
+ var_wakeup(&(var_hash_ptr)); \
var_destroy(&(var_hash_ptr)); \
efree(var_hash_ptr); \
} else { \
if (!--BG(unserialize).level) { \
+ var_wakeup(&(var_hash_ptr)); \
var_destroy(&(var_hash_ptr)); \
efree((var_hash_ptr)); \
BG(unserialize).var_hash = NULL; \
@@ -116,6 +120,7 @@ do { \
PHPAPI void var_replace(php_unserialize_data_t *var_hash, zval *ozval, zval **nzval);
PHPAPI void var_push_dtor(php_unserialize_data_t *var_hash, zval **val);
PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval);
+PHPAPI void var_wakeup(php_unserialize_data_t *var_hash);
PHPAPI void var_destroy(php_unserialize_data_t *var_hash);
#define PHP_VAR_UNSERIALIZE_ZVAL_CHANGED(var_hash, ozval, nzval) \
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c
index e7eac2d..b02ef4a 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -188,6 +188,61 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
}
}
+static inline void var_wakeup_push(php_unserialize_data_t *var_hashx, zval **rval)
+{
+ var_entries *var_hash = (*var_hashx)->last_wakeup;
+#if VAR_ENTRIES_DBG
+ fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
+#endif
+
+ if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
+ var_hash = emalloc(sizeof(var_entries));
+ var_hash->used_slots = 0;
+ var_hash->next = 0;
+
+ if (!(*var_hashx)->first_wakeup) {
+ (*var_hashx)->first_wakeup = var_hash;
+ } else {
+ ((var_entries *) (*var_hashx)->last_wakeup)->next = var_hash;
+ }
+
+ (*var_hashx)->last_wakeup = var_hash;
+ }
+
+ var_hash->data[var_hash->used_slots++] = *rval;
+}
+
+PHPAPI void var_wakeup(php_unserialize_data_t *var_hashx)
+{
+ long i;
+ void *next;
+ var_entries *var_hash = (*var_hashx)->first_wakeup;
+ zval *retval_ptr = NULL;
+ zval fname;
+
+ while (var_hash) {
+ for (i = 0; i < var_hash->used_slots; i++) {
+ if (Z_TYPE_P(var_hash->data[i]) == IS_OBJECT &&
+ Z_OBJCE_P(var_hash->data[i]) != PHP_IC_ENTRY &&
+ Z_OBJCE_P(var_hash->data[i])->serialize == NULL &&
+ zend_hash_exists(&Z_OBJCE_P(var_hash->data[i])->function_table, "__wakeup", sizeof("__wakeup"))) {
+ INIT_PZVAL(&fname);
+ ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1, 0);
+ BG(serialize_lock)++;
+ call_user_function_ex(CG(function_table), &var_hash->data[i], &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC);
+ BG(serialize_lock)--;
+
+ if (retval_ptr) {
+ zval_ptr_dtor(&retval_ptr);
+ }
+ }
+ }
+ next = var_hash->next;
+ efree(var_hash);
+ var_hash = next;
+ }
+}
+
/* }}} */
static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen)
@@ -424,9 +479,6 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
#endif
static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
{
- zval *retval_ptr = NULL;
- zval fname;
-
if (Z_TYPE_PP(rval) != IS_OBJECT) {
return 0;
}
@@ -440,26 +492,11 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
ZVAL_NULL(*rval);
return 0;
}
-
- if (Z_TYPE_PP(rval) != IS_OBJECT) {
- return 0;
- }
-
- if (Z_OBJCE_PP(rval) != PHP_IC_ENTRY &&
+
+ if (Z_TYPE_PP(rval) == IS_OBJECT &&
+ Z_OBJCE_PP(rval) != PHP_IC_ENTRY &&
zend_hash_exists(&Z_OBJCE_PP(rval)->function_table, "__wakeup", sizeof("__wakeup"))) {
- INIT_PZVAL(&fname);
- ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1, 0);
- BG(serialize_lock)++;
- call_user_function_ex(CG(function_table), rval, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC);
- BG(serialize_lock)--;
- }
-
- if (retval_ptr) {
- zval_ptr_dtor(&retval_ptr);
- }
-
- if (EG(exception)) {
- return 0;
+ var_wakeup_push(var_hash, rval);
}
return finish_nested_data(UNSERIALIZE_PASSTHRU);
@chtg

This comment has been minimized.

Copy link
Owner Author

chtg commented Oct 12, 2016

Another patch, deferring calls to __wakeup, and hiding unserialize data from ABI.

diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h
index 95af483..8d6a984 100644
--- a/ext/standard/php_var.h
+++ b/ext/standard/php_var.h
@@ -40,18 +40,14 @@ PHPAPI void php_debug_zval_dump(zval **struc, int level TSRMLS_DC);

 typedef HashTable* php_serialize_data_t;

-struct php_unserialize_data {
-   void *first;
-   void *last;
-   void *first_dtor;
-   void *last_dtor;
-};
-
 typedef struct php_unserialize_data* php_unserialize_data_t;

 PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t *var_hash TSRMLS_DC);
 PHPAPI int php_var_unserialize(zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC);

+PHPAPI php_unserialize_data_t php_var_unserialize_init(void);
+PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t var_hash_ptr);
+
 #define PHP_VAR_SERIALIZE_INIT(var_hash_ptr) \
 do  { \
    /* fprintf(stderr, "SERIALIZE_INIT      == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */ \
@@ -84,38 +80,15 @@ do { \
 } while (0)

 #define PHP_VAR_UNSERIALIZE_INIT(var_hash_ptr) \
-do { \
-   /* fprintf(stderr, "UNSERIALIZE_INIT    == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ \
-   if (BG(serialize_lock) || !BG(unserialize).level) { \
-       (var_hash_ptr) = (php_unserialize_data_t)ecalloc(1, sizeof(struct php_unserialize_data)); \
-       if (!BG(serialize_lock)) { \
-           BG(unserialize).var_hash = (void *)(var_hash_ptr); \
-           BG(unserialize).level = 1; \
-       } \
-   } else { \
-       (var_hash_ptr) = (php_unserialize_data_t)BG(unserialize).var_hash; \
-       ++BG(unserialize).level; \
-   } \
-} while (0)
+   (var_hash_ptr) = php_var_unserialize_init()

 #define PHP_VAR_UNSERIALIZE_DESTROY(var_hash_ptr) \
-do { \
-   /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ \
-   if (BG(serialize_lock) || !BG(unserialize).level) { \
-       var_destroy(&(var_hash_ptr)); \
-       efree(var_hash_ptr); \
-   } else { \
-       if (!--BG(unserialize).level) { \
-           var_destroy(&(var_hash_ptr)); \
-           efree((var_hash_ptr)); \
-           BG(unserialize).var_hash = NULL; \
-       } \
-   } \
-} while (0)
+   php_var_unserialize_destroy(var_hash_ptr)

 PHPAPI void var_replace(php_unserialize_data_t *var_hash, zval *ozval, zval **nzval);
 PHPAPI void var_push_dtor(php_unserialize_data_t *var_hash, zval **val);
 PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval);
+PHPAPI void var_wakeup(php_unserialize_data_t *var_hash);
 PHPAPI void var_destroy(php_unserialize_data_t *var_hash);

 #define PHP_VAR_UNSERIALIZE_ZVAL_CHANGED(var_hash, ozval, nzval) \
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c
index e7eac2d..4800c5f 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -23,6 +23,47 @@
 #include "ext/standard/php_var.h"
 #include "php_incomplete_class.h"

+struct php_unserialize_data {
+   void *first;
+   void *last;
+   void *first_dtor;
+   void *last_dtor;
+   void *first_wakeup;
+   void *last_wakeup;
+};
+
+PHPAPI php_unserialize_data_t php_var_unserialize_init() {
+   php_unserialize_data_t var_hash_ptr;
+   /* fprintf(stderr, "UNSERIALIZE_INIT    == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
+   if (BG(serialize_lock) || !BG(unserialize).level) {
+       var_hash_ptr = ecalloc(1, sizeof(struct php_unserialize_data));
+       if (!BG(serialize_lock)) {
+           BG(unserialize).var_hash = (void *)var_hash_ptr;
+           BG(unserialize).level = 1;
+       }
+   } else {
+       var_hash_ptr = BG(unserialize).var_hash;
+       ++BG(unserialize).level;
+   }
+   return var_hash_ptr;
+}
+
+PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t var_hash_ptr) {
+   /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
+   if (BG(serialize_lock) || !BG(unserialize).level) {
+       var_wakeup(&var_hash_ptr);
+       var_destroy(&var_hash_ptr);
+       efree(var_hash_ptr);
+   } else {
+       if (!--BG(unserialize).level) {
+           var_wakeup(&var_hash_ptr);
+           var_destroy(&var_hash_ptr);
+           efree((var_hash_ptr));
+           BG(unserialize).var_hash = NULL;
+       }
+   }
+}
+
 /* {{{ reference-handling for unserializer: var_* */
 #define VAR_ENTRIES_MAX 1024
 #define VAR_ENTRIES_DBG 0
@@ -188,6 +229,61 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
    }
 }

+static inline void var_wakeup_push(php_unserialize_data_t *var_hashx, zval **rval)
+{
+   var_entries *var_hash = (*var_hashx)->last_wakeup;
+#if VAR_ENTRIES_DBG
+   fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
+#endif
+
+   if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
+       var_hash = emalloc(sizeof(var_entries));
+       var_hash->used_slots = 0;
+       var_hash->next = 0;
+
+       if (!(*var_hashx)->first_wakeup) {
+           (*var_hashx)->first_wakeup = var_hash;
+       } else {
+           ((var_entries *) (*var_hashx)->last_wakeup)->next = var_hash;
+       }
+
+       (*var_hashx)->last_wakeup = var_hash;
+   }
+
+   var_hash->data[var_hash->used_slots++] = *rval;
+}
+
+PHPAPI void var_wakeup(php_unserialize_data_t *var_hashx)
+{
+   long i;
+   void *next;
+   var_entries *var_hash = (*var_hashx)->first_wakeup;
+   zval *retval_ptr = NULL;
+   zval fname;
+
+   while (var_hash) {
+       for (i = 0; i < var_hash->used_slots; i++) {
+           if (Z_TYPE_P(var_hash->data[i]) == IS_OBJECT &&
+               Z_OBJCE_P(var_hash->data[i]) != PHP_IC_ENTRY &&
+               Z_OBJCE_P(var_hash->data[i])->serialize == NULL &&
+               zend_hash_exists(&Z_OBJCE_P(var_hash->data[i])->function_table, "__wakeup", sizeof("__wakeup"))) {
+               INIT_PZVAL(&fname);
+               ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1, 0);
+               BG(serialize_lock)++;
+               call_user_function_ex(CG(function_table), &var_hash->data[i], &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC);
+               BG(serialize_lock)--;
+               
+               if (retval_ptr) {
+                   zval_ptr_dtor(&retval_ptr);
+               }
+           }
+       }
+       next = var_hash->next;
+       efree(var_hash);
+       var_hash = next;
+   }
+}
+
 /* }}} */

 static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen)
@@ -424,9 +520,6 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
 #endif
 static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
 {
-   zval *retval_ptr = NULL;
-   zval fname;
-
    if (Z_TYPE_PP(rval) != IS_OBJECT) {
        return 0;
    }
@@ -440,26 +533,11 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
        ZVAL_NULL(*rval);
        return 0;
    }
-
-    if (Z_TYPE_PP(rval) != IS_OBJECT) {
-        return 0;
-    }
-
-   if (Z_OBJCE_PP(rval) != PHP_IC_ENTRY &&
+   
+   if (Z_TYPE_PP(rval) == IS_OBJECT &&
+       Z_OBJCE_PP(rval) != PHP_IC_ENTRY &&
        zend_hash_exists(&Z_OBJCE_PP(rval)->function_table, "__wakeup", sizeof("__wakeup"))) {
-       INIT_PZVAL(&fname);
-       ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1, 0);
-       BG(serialize_lock)++;
-       call_user_function_ex(CG(function_table), rval, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC);
-       BG(serialize_lock)--;
-   }
-
-   if (retval_ptr) {
-       zval_ptr_dtor(&retval_ptr);
-   }
-
-   if (EG(exception)) {
-       return 0;
+       var_wakeup_push(var_hash, rval);
    }

    return finish_nested_data(UNSERIALIZE_PASSTHRU);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.