From: Dmitry Stogov Date: Wed, 17 Feb 2021 10:30:52 +0000 (+0300) Subject: unserialize() optimization. Omit class name validation before hash lookup, and perfor... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=76b3635607eb74ebe77e0c03df417cc2ffa6ee99;p=php unserialize() optimization. Omit class name validation before hash lookup, and perform it only before autoloading. --- diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index dec3d9c081..5f3b81a77d 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -342,12 +342,9 @@ static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t } static inline int unserialize_allowed_class( - zend_string *class_name, php_unserialize_data_t *var_hashx) + zend_string *lcname, php_unserialize_data_t *var_hashx) { HashTable *classes = (*var_hashx)->allowed_classes; - zend_string *lcname; - int res; - ALLOCA_FLAG(use_heap) if(classes == NULL) { return 1; @@ -356,11 +353,7 @@ static inline int unserialize_allowed_class( return 0; } - ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(class_name), use_heap); - zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(class_name), ZSTR_LEN(class_name)); - res = zend_hash_exists(classes, lcname); - ZSTR_ALLOCA_FREE(lcname, use_heap); - return res; + return zend_hash_exists(classes, lcname); } #define YYFILL(n) do { } while (0) @@ -1145,22 +1138,55 @@ object ":" uiv ":" ["] { return 0; } - class_name = zend_string_init(str, len, 0); - if (!zend_is_valid_class_name(class_name)) { - zend_string_release_ex(class_name, 0); + if (len == 0) { + /* empty class names are not allowed */ + return 0; + } + + if (str[0] == '\000') { + /* runtime definition keys are not allowed */ return 0; } + if (str[0] == '\\') { + /* class name can't start from namespace separator */ + return 0; + } + + class_name = zend_string_init(str, len, 0); + do { - if(!unserialize_allowed_class(class_name, var_hash)) { + zend_string *lc_name = zend_string_tolower(class_name); + + if(!unserialize_allowed_class(lc_name, var_hash)) { + zend_string_release_ex(lc_name, 0); + if (!zend_is_valid_class_name(class_name)) { + zend_string_release_ex(class_name, 0); + return 0; + } incomplete_class = 1; ce = PHP_IC_ENTRY; break; } + ce = zend_hash_find_ptr(EG(class_table), lc_name); + if (ce + && (ce->ce_flags & ZEND_ACC_LINKED) + && !(ce->ce_flags & ZEND_ACC_ANON_CLASS)) { + zend_string_release_ex(lc_name, 0); + break; + } + + if (!zend_is_valid_class_name(class_name)) { + zend_string_release_ex(lc_name, 0); + zend_string_release_ex(class_name, 0); + return 0; + } + /* Try to find class directly */ BG(serialize_lock)++; - ce = zend_lookup_class(class_name); + ce = zend_lookup_class_ex(class_name, lc_name, 0); + zend_string_release_ex(lc_name, 0); if (ce) { BG(serialize_lock)--; if (EG(exception)) {