Skip to content

Commit

Permalink
secured unserialize
Browse files Browse the repository at this point in the history
- update for BC-compatible unserialize
- add tests
  • Loading branch information
smalyshev committed Nov 22, 2014
1 parent 9de44a2 commit baa4889
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 49 deletions.
1 change: 1 addition & 0 deletions ext/standard/php_var.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ 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 TSRMLS_DC);
PHPAPI int php_var_unserialize_ref(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC);
PHPAPI int php_var_unserialize_intern(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC);
PHPAPI int php_var_unserialize_ex(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes TSRMLS_DC);

#define PHP_VAR_SERIALIZE_INIT(d) \
do { \
Expand Down
6 changes: 3 additions & 3 deletions ext/standard/tests/serialize/serialization_error_001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var_dump( unserialize() );

//Test serialize with one more than the expected number of arguments
var_dump( serialize(1,2) );
var_dump( unserialize(1,2) );
var_dump( unserialize(1,2,3) );

echo "Done";
?>
Expand All @@ -31,12 +31,12 @@ echo "Done";
Warning: serialize() expects exactly 1 parameter, 0 given in %s on line 16
NULL

Warning: unserialize() expects exactly 1 parameter, 0 given in %s on line 17
Warning: unserialize() expects at least 1 parameter, 0 given in %s on line 17
bool(false)

Warning: serialize() expects exactly 1 parameter, 2 given in %s on line 20
NULL

Warning: unserialize() expects exactly 1 parameter, 2 given in %s on line 21
Warning: unserialize() expects at most 2 parameters, 3 given in %s on line 21
bool(false)
Done
88 changes: 88 additions & 0 deletions ext/standard/tests/serialize/unserialize_classes.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
--TEST--
Test unserialize() with second parameter
--FILE--
<?php
class foo {
public $x = "bar";
}
$z = array(new foo(), 2, "3");
$s = serialize($z);

var_dump(unserialize($s));
var_dump(unserialize($s, false));
var_dump(unserialize($s, true));
var_dump(unserialize($s, array("bar")));
var_dump(unserialize($s, array("FOO")));
var_dump(unserialize($s, array("bar", "fOo")));

--EXPECTF--
array(3) {
[0]=>
object(foo)#%d (1) {
["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"
}
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"
}
array(3) {
[0]=>
object(foo)#%d (1) {
["x"]=>
string(3) "bar"
}
[1]=>
int(2)
[2]=>
string(1) "3"
}
34 changes: 31 additions & 3 deletions ext/standard/var.c
Original file line number Diff line number Diff line change
Expand Up @@ -990,16 +990,18 @@ PHP_FUNCTION(serialize)
}
/* }}} */

/* {{{ proto mixed unserialize(string variable_representation)
/* {{{ proto mixed unserialize(string variable_representation[, bool|array allowed_classes])
Takes a string representation of variable and recreates it */
PHP_FUNCTION(unserialize)
{
char *buf = NULL;
size_t buf_len;
const unsigned char *p;
php_unserialize_data_t var_hash;
zval *classes = NULL;
HashTable *class_hash = NULL;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &buf, &buf_len, &classes) == FAILURE) {
RETURN_FALSE;
}

Expand All @@ -1009,15 +1011,41 @@ PHP_FUNCTION(unserialize)

p = (const unsigned char*) buf;
PHP_VAR_UNSERIALIZE_INIT(var_hash);
if (!php_var_unserialize(return_value, &p, p + buf_len, &var_hash TSRMLS_CC)) {
if(classes != NULL) {
if(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);
}
if(class_hash && Z_TYPE_P(classes) == IS_ARRAY) {
zval *entry;
zend_string *lcname;
ALLOCA_FLAG(use_heap)

ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(classes), entry) {
convert_to_string_ex(entry);
STR_ALLOCA_ALLOC(lcname, Z_STRLEN_P(entry), use_heap);
zend_str_tolower_copy(lcname->val, Z_STRVAL_P(entry), Z_STRLEN_P(entry));
zend_hash_add_empty_element(class_hash, lcname);
STR_ALLOCA_FREE(lcname, use_heap);
} ZEND_HASH_FOREACH_END();
}
}

if (!php_var_unserialize_ex(return_value, &p, p + buf_len, &var_hash, class_hash TSRMLS_CC)) {
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
if(class_hash) {
zend_hash_destroy(class_hash);
}
zval_dtor(return_value);
if (!EG(exception)) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset " ZEND_LONG_FMT " of %d bytes", (zend_long)((char*)p - buf), buf_len);
}
RETURN_FALSE;
}
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
if(class_hash) {
zend_hash_destroy(class_hash);
}
}
/* }}} */

Expand Down
Loading

0 comments on commit baa4889

Please sign in to comment.