Skip to content

Instantly share code, notes, and snippets.

@nikic
Created August 15, 2016 21:45
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save nikic/c7941621d3a083f84d05d46c1cef35bd to your computer and use it in GitHub Desktop.
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