components/php/php56/patches/CVE-2015-6834_70172.patch
changeset 5116 867d838118ad
equal deleted inserted replaced
5115:9c865404b7f5 5116:867d838118ad
       
     1 # Source: upstream
       
     2 # https://bugs.php.net/bug.php?id=70172
       
     3 # http://git.php.net/?p=php-src.git;a=commit;h=e8429400d40e3c3aa4b22ba701991d698a2f3b2f
       
     4 # Patch to var_unserializer.c regenerated to remove inapplicable #line comments
       
     5 
       
     6 From e8429400d40e3c3aa4b22ba701991d698a2f3b2f Mon Sep 17 00:00:00 2001
       
     7 From: Stanislav Malyshev <[email protected]>
       
     8 Date: Mon, 31 Aug 2015 21:28:11 -0700
       
     9 Subject: [PATCH] Fix bug #70172 - Use After Free Vulnerability in
       
    10  unserialize()
       
    11 
       
    12 ---
       
    13  ext/standard/tests/serialize/bug70172.phpt | 52 ++++++++++++++++++++
       
    14  ext/standard/var.c                         | 23 +++++++--
       
    15  ext/standard/var_unserializer.c            | 76 ++++++++++++++++--------------
       
    16  ext/standard/var_unserializer.re           | 12 +++--
       
    17  4 files changed, 121 insertions(+), 42 deletions(-)
       
    18  create mode 100644 ext/standard/tests/serialize/bug70172.phpt
       
    19 
       
    20 diff --git a/ext/standard/tests/serialize/bug70172.phpt b/ext/standard/tests/serialize/bug70172.phpt
       
    21 new file mode 100644
       
    22 index 0000000..0e9d7ed
       
    23 --- /dev/null
       
    24 +++ b/ext/standard/tests/serialize/bug70172.phpt
       
    25 @@ -0,0 +1,52 @@
       
    26 +--TEST--
       
    27 +Bug #70172 - Use After Free Vulnerability in unserialize()
       
    28 +--FILE--
       
    29 +<?php
       
    30 +class obj implements Serializable {
       
    31 +	var $data;
       
    32 +	function serialize() {
       
    33 +		return serialize($this->data);
       
    34 +	}
       
    35 +	function unserialize($data) {
       
    36 +		$this->data = unserialize($data);
       
    37 +	}
       
    38 +}
       
    39 +
       
    40 +$fakezval = ptr2str(1122334455);
       
    41 +$fakezval .= ptr2str(0);
       
    42 +$fakezval .= "\x00\x00\x00\x00";
       
    43 +$fakezval .= "\x01";
       
    44 +$fakezval .= "\x00";
       
    45 +$fakezval .= "\x00\x00";
       
    46 +
       
    47 +$inner = 'r:2;';
       
    48 +$exploit = 'a:2:{i:0;i:1;i:1;C:3:"obj":'.strlen($inner).':{'.$inner.'}}';
       
    49 +
       
    50 +$data = unserialize($exploit);
       
    51 +
       
    52 +for ($i = 0; $i < 5; $i++) {
       
    53 +	$v[$i] = $fakezval.$i;
       
    54 +}
       
    55 +
       
    56 +var_dump($data);
       
    57 +
       
    58 +function ptr2str($ptr)
       
    59 +{
       
    60 +	$out = '';
       
    61 +	for ($i = 0; $i < 8; $i++) {
       
    62 +		$out .= chr($ptr & 0xff);
       
    63 +		$ptr >>= 8;
       
    64 +	}
       
    65 +	return $out;
       
    66 +}
       
    67 +?>
       
    68 +--EXPECTF--
       
    69 +array(2) {
       
    70 +  [0]=>
       
    71 +  int(1)
       
    72 +  [1]=>
       
    73 +  object(obj)#%d (1) {
       
    74 +    ["data"]=>
       
    75 +    int(1)
       
    76 +  }
       
    77 +}
       
    78 \ No newline at end of file
       
    79 diff --git a/ext/standard/var.c b/ext/standard/var.c
       
    80 index 7603ff2..33b976f 100644
       
    81 --- a/ext/standard/var.c
       
    82 +++ b/ext/standard/var.c
       
    83 @@ -373,7 +373,7 @@ static int php_array_element_export(zval **zv TSRMLS_DC, int num_args, va_list a
       
    84  
       
    85  	smart_str_appendc(buf, ',');
       
    86  	smart_str_appendc(buf, '\n');
       
    87 -	
       
    88 +
       
    89  	return 0;
       
    90  }
       
    91  /* }}} */
       
    92 @@ -392,7 +392,7 @@ static int php_object_element_export(zval **zv TSRMLS_DC, int num_args, va_list
       
    93  		const char *pname;
       
    94  		char *pname_esc;
       
    95  		int  pname_esc_len;
       
    96 -		
       
    97 +
       
    98  		zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1,
       
    99  				&class_name, &pname);
       
   100  		pname_esc = php_addcslashes(pname, strlen(pname), &pname_esc_len, 0,
       
   101 @@ -469,7 +469,7 @@ PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC)
       
   102  			buffer_append_spaces(buf, level - 1);
       
   103  		}
       
   104  		smart_str_appendc(buf, ')');
       
   105 -    
       
   106 +
       
   107  		break;
       
   108  
       
   109  	case IS_OBJECT:
       
   110 @@ -802,7 +802,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var
       
   111  					BG(serialize_lock)++;
       
   112  					res = call_user_function_ex(CG(function_table), &struc, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC);
       
   113  					BG(serialize_lock)--;
       
   114 -                    
       
   115 +
       
   116  					if (EG(exception)) {
       
   117  						if (retval_ptr) {
       
   118  							zval_ptr_dtor(&retval_ptr);
       
   119 @@ -951,6 +951,8 @@ PHP_FUNCTION(unserialize)
       
   120  	int buf_len;
       
   121  	const unsigned char *p;
       
   122  	php_unserialize_data_t var_hash;
       
   123 +	int oldlevel;
       
   124 +	zval *old_rval = return_value;
       
   125  
       
   126  	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
       
   127  		RETURN_FALSE;
       
   128 @@ -970,6 +972,19 @@ PHP_FUNCTION(unserialize)
       
   129  		}
       
   130  		RETURN_FALSE;
       
   131  	}
       
   132 +	if (return_value != old_rval) {
       
   133 +		/*
       
   134 +		 * Terrible hack due to the fact that executor passes us zval *,
       
   135 +		 * but unserialize with r/R wants to replace it with another zval *
       
   136 +		 */
       
   137 +		zval_dtor(old_rval);
       
   138 +		*old_rval = *return_value;
       
   139 +		zval_copy_ctor(old_rval);
       
   140 +		var_push_dtor_no_addref(&var_hash, &return_value);
       
   141 +		var_push_dtor_no_addref(&var_hash, &old_rval);
       
   142 +	} else {
       
   143 +		var_push_dtor(&var_hash, &return_value);
       
   144 +	}
       
   145  	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
       
   146  }
       
   147  /* }}} */
       
   148 --- var_unserializer.c.orig	Wed Sep  9 13:59:27 2015
       
   149 +++ php-5.6.8/ext/standard/var_unserializer.c	Wed Sep  9 14:02:03 2015
       
   150 @@ -67,7 +67,7 @@
       
   151  
       
   152  	var_hash = (*var_hashx)->last_dtor;
       
   153  #if VAR_ENTRIES_DBG
       
   154 -	fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
       
   155 +	fprintf(stderr, "var_push_dtor(%p, %ld): %d\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
       
   156  #endif
       
   157  
       
   158  	if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
       
   159 @@ -92,7 +92,7 @@
       
   160  {
       
   161  	var_entries *var_hash = (*var_hashx)->last_dtor;
       
   162  #if VAR_ENTRIES_DBG
       
   163 -	fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval));
       
   164 +	fprintf(stderr, "var_push_dtor_no_addref(%p, %ld): %d (%d)\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval));
       
   165  #endif
       
   166  
       
   167  	if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
       
   168 @@ -171,6 +171,9 @@
       
   169  	
       
   170  	while (var_hash) {
       
   171  		for (i = 0; i < var_hash->used_slots; i++) {
       
   172 +#if VAR_ENTRIES_DBG
       
   173 +    fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_hash->data[i], Z_REFCOUNT_P(var_hash->data[i]));
       
   174 +#endif
       
   175  			zval_ptr_dtor(&var_hash->data[i]);
       
   176  		}
       
   177  		next = var_hash->next;
       
   178 @@ -628,6 +631,7 @@
       
   179  	zval **args[1];
       
   180  	zval *arg_func_name;
       
   181  
       
   182 +    if (!var_hash) return 0;
       
   183  	if (*start == 'C') {
       
   184  		custom_object = 1;
       
   185  	}
       
   186 @@ -783,6 +787,7 @@
       
   187  	if (yych != '"') goto yy18;
       
   188  	++YYCURSOR;
       
   189  	{
       
   190 +    if (!var_hash) return 0;
       
   191  
       
   192  	INIT_PZVAL(*rval);
       
   193  	
       
   194 @@ -813,6 +818,7 @@
       
   195  	long elements = parse_iv(start + 2);
       
   196  	/* use iv() not uiv() in order to check data range */
       
   197  	*p = YYCURSOR;
       
   198 +    if (!var_hash) return 0;
       
   199  
       
   200  	if (elements < 0) {
       
   201  		return 0;
       
   202 @@ -1242,7 +1248,7 @@
       
   203  	}
       
   204  
       
   205  	if (*rval != NULL) {
       
   206 -		zval_ptr_dtor(rval);
       
   207 +		var_push_dtor_no_addref(var_hash, rval);
       
   208  	}
       
   209  	*rval = *rval_ref;
       
   210  	Z_ADDREF_PP(rval);
       
   211 diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
       
   212 index f02602c..ed82152 100644
       
   213 --- a/ext/standard/var_unserializer.re
       
   214 +++ b/ext/standard/var_unserializer.re
       
   215 @@ -67,7 +67,7 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
       
   216  
       
   217  	var_hash = (*var_hashx)->last_dtor;
       
   218  #if VAR_ENTRIES_DBG
       
   219 -	fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
       
   220 +	fprintf(stderr, "var_push_dtor(%p, %ld): %d\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
       
   221  #endif
       
   222  
       
   223  	if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
       
   224 @@ -98,7 +98,7 @@ PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rv
       
   225  
       
   226      var_hash = (*var_hashx)->last_dtor;
       
   227  #if VAR_ENTRIES_DBG
       
   228 -	fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval));
       
   229 +	fprintf(stderr, "var_push_dtor_no_addref(%p, %ld): %d (%d)\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval));
       
   230  #endif
       
   231  
       
   232  	if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
       
   233 @@ -177,6 +177,9 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
       
   234  
       
   235  	while (var_hash) {
       
   236  		for (i = 0; i < var_hash->used_slots; i++) {
       
   237 +#if VAR_ENTRIES_DBG
       
   238 +    fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_hash->data[i], Z_REFCOUNT_P(var_hash->data[i]));
       
   239 +#endif
       
   240  			zval_ptr_dtor(&var_hash->data[i]);
       
   241  		}
       
   242  		next = var_hash->next;
       
   243 @@ -501,7 +504,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
       
   244  	}
       
   245  
       
   246  	if (*rval != NULL) {
       
   247 -		zval_ptr_dtor(rval);
       
   248 +		var_push_dtor_no_addref(var_hash, rval);
       
   249  	}
       
   250  	*rval = *rval_ref;
       
   251  	Z_ADDREF_PP(rval);
       
   252 @@ -660,6 +663,7 @@ use_double:
       
   253  	long elements = parse_iv(start + 2);
       
   254  	/* use iv() not uiv() in order to check data range */
       
   255  	*p = YYCURSOR;
       
   256 +    if (!var_hash) return 0;
       
   257  
       
   258  	if (elements < 0) {
       
   259  		return 0;
       
   260 @@ -677,6 +681,7 @@ use_double:
       
   261  }
       
   262  
       
   263  "o:" iv ":" ["] {
       
   264 +    if (!var_hash) return 0;
       
   265  
       
   266  	INIT_PZVAL(*rval);
       
   267  
       
   268 @@ -699,6 +704,7 @@ object ":" uiv ":" ["]	{
       
   269  	zval **args[1];
       
   270  	zval *arg_func_name;
       
   271  
       
   272 +    if (!var_hash) return 0;
       
   273  	if (*start == 'C') {
       
   274  		custom_object = 1;
       
   275  	}
       
   276 -- 
       
   277 2.1.4
       
   278 
       
   279