/bug72785.patch Secret
Created
August 15, 2016 21:45
Star
You must be signed in to star a gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h | |
index 80208d2..2d1cc5a 100644 | |
--- a/ext/standard/php_var.h | |
+++ b/ext/standard/php_var.h | |
@@ -45,12 +45,12 @@ PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t | |
PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash); | |
PHPAPI int php_var_unserialize_ref(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash); | |
PHPAPI int php_var_unserialize_intern(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash); | |
-PHPAPI int php_var_unserialize_ex(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes); | |
PHPAPI php_serialize_data_t php_var_serialize_init(void); | |
PHPAPI void php_var_serialize_destroy(php_serialize_data_t d); | |
PHPAPI php_unserialize_data_t php_var_unserialize_init(void); | |
PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d); | |
+PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes); | |
#define PHP_VAR_SERIALIZE_INIT(d) \ | |
(d) = php_var_serialize_init() | |
diff --git a/ext/standard/tests/serialize/bug72785.phpt b/ext/standard/tests/serialize/bug72785.phpt | |
new file mode 100644 | |
index 0000000..8bcdf63 | |
--- /dev/null | |
+++ b/ext/standard/tests/serialize/bug72785.phpt | |
@@ -0,0 +1,24 @@ | |
+--TEST-- | |
+Bug #72785: allowed_classes only applies to outermost unserialize() | |
+--FILE-- | |
+<?php | |
+ | |
+// Forbidden class | |
+class A {} | |
+ | |
+$p = 'x:i:0;a:1:{i:0;O:1:"A":0:{}};m:a:0:{}'; | |
+$s = 'C:11:"ArrayObject":' . strlen($p) . ':{' . $p . '}'; | |
+var_dump(unserialize($s, ['allowed_classes' => ['ArrayObject']])); | |
+ | |
+?> | |
+--EXPECT-- | |
+object(ArrayObject)#1 (1) { | |
+ ["storage":"ArrayObject":private]=> | |
+ array(1) { | |
+ [0]=> | |
+ object(__PHP_Incomplete_Class)#2 (1) { | |
+ ["__PHP_Incomplete_Class_Name"]=> | |
+ string(1) "A" | |
+ } | |
+ } | |
+} | |
diff --git a/ext/standard/tests/serialize/unserialize_error_001.phpt b/ext/standard/tests/serialize/unserialize_error_001.phpt | |
index 5589cbd..6773ec7 100644 | |
--- a/ext/standard/tests/serialize/unserialize_error_001.phpt | |
+++ b/ext/standard/tests/serialize/unserialize_error_001.phpt | |
@@ -13,40 +13,11 @@ var_dump(unserialize($s, ["allowed_classes" => 0])); | |
var_dump(unserialize($s, ["allowed_classes" => 1])); | |
--EXPECTF-- | |
-array(3) { | |
- [0]=> | |
- object(__PHP_Incomplete_Class)#%d (2) { | |
- ["__PHP_Incomplete_Class_Name"]=> | |
- string(3) "foo" | |
- ["x"]=> | |
- string(3) "bar" | |
- } | |
- [1]=> | |
- int(2) | |
- [2]=> | |
- string(1) "3" | |
-} | |
-array(3) { | |
- [0]=> | |
- object(__PHP_Incomplete_Class)#%d (2) { | |
- ["__PHP_Incomplete_Class_Name"]=> | |
- string(3) "foo" | |
- ["x"]=> | |
- string(3) "bar" | |
- } | |
- [1]=> | |
- int(2) | |
- [2]=> | |
- string(1) "3" | |
-} | |
-array(3) { | |
- [0]=> | |
- object(foo)#%d (1) { | |
- ["x"]=> | |
- string(3) "bar" | |
- } | |
- [1]=> | |
- int(2) | |
- [2]=> | |
- string(1) "3" | |
-} | |
+Warning: unserialize(): allowed_classes option should be array or boolean in %s on line %d | |
+bool(false) | |
+ | |
+Warning: unserialize(): allowed_classes option should be array or boolean in %s on line %d | |
+bool(false) | |
+ | |
+Warning: unserialize(): allowed_classes option should be array or boolean in %s on line %d | |
+bool(false) | |
diff --git a/ext/standard/var.c b/ext/standard/var.c | |
index cc033aa..18d027f 100644 | |
--- a/ext/standard/var.c | |
+++ b/ext/standard/var.c | |
@@ -1079,6 +1079,12 @@ PHP_FUNCTION(unserialize) | |
PHP_VAR_UNSERIALIZE_INIT(var_hash); | |
if(options != NULL) { | |
classes = zend_hash_str_find(Z_ARRVAL_P(options), "allowed_classes", sizeof("allowed_classes")-1); | |
+ if (classes && Z_TYPE_P(classes) != IS_ARRAY && Z_TYPE_P(classes) != IS_TRUE && Z_TYPE_P(classes) != IS_FALSE) { | |
+ php_error_docref(NULL, E_WARNING, "allowed_classes option should be array or boolean"); | |
+ PHP_VAR_UNSERIALIZE_DESTROY(var_hash); | |
+ RETURN_FALSE; | |
+ } | |
+ | |
if(classes && (Z_TYPE_P(classes) == IS_ARRAY || !zend_is_true(classes))) { | |
ALLOC_HASHTABLE(class_hash); | |
zend_hash_init(class_hash, (Z_TYPE_P(classes) == IS_ARRAY)?zend_hash_num_elements(Z_ARRVAL_P(classes)):0, NULL, NULL, 0); | |
@@ -1094,9 +1100,10 @@ PHP_FUNCTION(unserialize) | |
zend_string_release(lcname); | |
} ZEND_HASH_FOREACH_END(); | |
} | |
+ php_var_unserialize_set_allowed_classes(var_hash, class_hash); | |
} | |
- if (!php_var_unserialize_ex(return_value, &p, p + buf_len, &var_hash, class_hash)) { | |
+ if (!php_var_unserialize(return_value, &p, p + buf_len, &var_hash)) { | |
PHP_VAR_UNSERIALIZE_DESTROY(var_hash); | |
if (class_hash) { | |
zend_hash_destroy(class_hash); | |
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c | |
index f96fdb4..fa0f22c 100644 | |
--- a/ext/standard/var_unserializer.c | |
+++ b/ext/standard/var_unserializer.c | |
@@ -29,6 +29,7 @@ struct php_unserialize_data { | |
void *last; | |
void *first_dtor; | |
void *last_dtor; | |
+ HashTable *allowed_classes; | |
}; | |
PHPAPI php_unserialize_data_t php_var_unserialize_init() { | |
@@ -58,6 +59,10 @@ PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) { | |
} | |
} | |
+PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { | |
+ d->allowed_classes = classes; | |
+} | |
+ | |
/* {{{ reference-handling for unserializer: var_* */ | |
#define VAR_ENTRIES_MAX 1024 | |
@@ -246,8 +251,10 @@ static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t | |
return str; | |
} | |
-static inline int unserialize_allowed_class(zend_string *class_name, HashTable *classes) | |
+static inline int unserialize_allowed_class( | |
+ zend_string *class_name, php_unserialize_data_t *var_hashx) | |
{ | |
+ HashTable *classes = (*var_hashx)->allowed_classes; | |
zend_string *lcname; | |
int res; | |
ALLOCA_FLAG(use_heap) | |
@@ -273,7 +280,7 @@ static inline int unserialize_allowed_class(zend_string *class_name, HashTable * | |
#define YYMARKER marker | |
-#line 281 "ext/standard/var_unserializer.re" | |
+#line 288 "ext/standard/var_unserializer.re" | |
@@ -333,8 +340,8 @@ static inline size_t parse_uiv(const unsigned char *p) | |
return result; | |
} | |
-#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes | |
-#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes | |
+#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash | |
+#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash | |
static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) | |
{ | |
@@ -344,7 +351,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab | |
ZVAL_UNDEF(&key); | |
- if (!php_var_unserialize_ex(&key, p, max, NULL, classes)) { | |
+ if (!php_var_unserialize(&key, p, max, NULL)) { | |
zval_dtor(&key); | |
return 0; | |
} | |
@@ -400,7 +407,7 @@ string_key: | |
} | |
} | |
- if (!php_var_unserialize_ex(data, p, max, var_hash, classes)) { | |
+ if (!php_var_unserialize(data, p, max, var_hash)) { | |
zval_dtor(&key); | |
return 0; | |
} | |
@@ -523,14 +530,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) | |
# pragma optimize("", on) | |
#endif | |
-PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash) | |
-{ | |
- HashTable *classes = NULL; | |
- return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU); | |
-} | |
- | |
- | |
-PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) | |
+PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) | |
{ | |
const unsigned char *cursor, *limit, *marker, *start; | |
zval *rval_ref; | |
@@ -1139,7 +1139,7 @@ yy82: | |
class_name = zend_string_init(str, len, 0); | |
do { | |
- if(!unserialize_allowed_class(class_name, classes)) { | |
+ if(!unserialize_allowed_class(class_name, var_hash)) { | |
incomplete_class = 1; | |
ce = PHP_IC_ENTRY; | |
break; | |
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re | |
index 4ba8f75..3834433 100644 | |
--- a/ext/standard/var_unserializer.re | |
+++ b/ext/standard/var_unserializer.re | |
@@ -27,6 +27,7 @@ struct php_unserialize_data { | |
void *last; | |
void *first_dtor; | |
void *last_dtor; | |
+ HashTable *allowed_classes; | |
}; | |
PHPAPI php_unserialize_data_t php_var_unserialize_init() { | |
@@ -56,6 +57,10 @@ PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) { | |
} | |
} | |
+PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { | |
+ d->allowed_classes = classes; | |
+} | |
+ | |
/* {{{ reference-handling for unserializer: var_* */ | |
#define VAR_ENTRIES_MAX 1024 | |
@@ -244,8 +249,10 @@ static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t | |
return str; | |
} | |
-static inline int unserialize_allowed_class(zend_string *class_name, HashTable *classes) | |
+static inline int unserialize_allowed_class( | |
+ zend_string *class_name, php_unserialize_data_t *var_hashx) | |
{ | |
+ HashTable *classes = (*var_hashx)->allowed_classes; | |
zend_string *lcname; | |
int res; | |
ALLOCA_FLAG(use_heap) | |
@@ -337,8 +344,8 @@ static inline size_t parse_uiv(const unsigned char *p) | |
return result; | |
} | |
-#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes | |
-#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes | |
+#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash | |
+#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash | |
static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) | |
{ | |
@@ -348,7 +355,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab | |
ZVAL_UNDEF(&key); | |
- if (!php_var_unserialize_ex(&key, p, max, NULL, classes)) { | |
+ if (!php_var_unserialize(&key, p, max, NULL)) { | |
zval_dtor(&key); | |
return 0; | |
} | |
@@ -404,7 +411,7 @@ string_key: | |
} | |
} | |
- if (!php_var_unserialize_ex(data, p, max, var_hash, classes)) { | |
+ if (!php_var_unserialize(data, p, max, var_hash)) { | |
zval_dtor(&key); | |
return 0; | |
} | |
@@ -527,14 +534,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) | |
# pragma optimize("", on) | |
#endif | |
-PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash) | |
-{ | |
- HashTable *classes = NULL; | |
- return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU); | |
-} | |
- | |
- | |
-PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) | |
+PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) | |
{ | |
const unsigned char *cursor, *limit, *marker, *start; | |
zval *rval_ref; | |
@@ -814,7 +814,7 @@ object ":" uiv ":" ["] { | |
class_name = zend_string_init(str, len, 0); | |
do { | |
- if(!unserialize_allowed_class(class_name, classes)) { | |
+ if(!unserialize_allowed_class(class_name, var_hash)) { | |
incomplete_class = 1; | |
ce = PHP_IC_ENTRY; | |
break; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment