mirror of
https://github.com/php/php-src.git
synced 2024-12-13 03:44:17 +08:00
Fix bug #70172 - Use After Free Vulnerability in unserialize()
This commit is contained in:
parent
e201f01ac1
commit
e8429400d4
52
ext/standard/tests/serialize/bug70172.phpt
Normal file
52
ext/standard/tests/serialize/bug70172.phpt
Normal file
@ -0,0 +1,52 @@
|
||||
--TEST--
|
||||
Bug #70172 - Use After Free Vulnerability in unserialize()
|
||||
--FILE--
|
||||
<?php
|
||||
class obj implements Serializable {
|
||||
var $data;
|
||||
function serialize() {
|
||||
return serialize($this->data);
|
||||
}
|
||||
function unserialize($data) {
|
||||
$this->data = unserialize($data);
|
||||
}
|
||||
}
|
||||
|
||||
$fakezval = ptr2str(1122334455);
|
||||
$fakezval .= ptr2str(0);
|
||||
$fakezval .= "\x00\x00\x00\x00";
|
||||
$fakezval .= "\x01";
|
||||
$fakezval .= "\x00";
|
||||
$fakezval .= "\x00\x00";
|
||||
|
||||
$inner = 'r:2;';
|
||||
$exploit = 'a:2:{i:0;i:1;i:1;C:3:"obj":'.strlen($inner).':{'.$inner.'}}';
|
||||
|
||||
$data = unserialize($exploit);
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$v[$i] = $fakezval.$i;
|
||||
}
|
||||
|
||||
var_dump($data);
|
||||
|
||||
function ptr2str($ptr)
|
||||
{
|
||||
$out = '';
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
$out .= chr($ptr & 0xff);
|
||||
$ptr >>= 8;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
?>
|
||||
--EXPECTF--
|
||||
array(2) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
object(obj)#%d (1) {
|
||||
["data"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
@ -373,7 +373,7 @@ static int php_array_element_export(zval **zv TSRMLS_DC, int num_args, va_list a
|
||||
|
||||
smart_str_appendc(buf, ',');
|
||||
smart_str_appendc(buf, '\n');
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
@ -392,7 +392,7 @@ static int php_object_element_export(zval **zv TSRMLS_DC, int num_args, va_list
|
||||
const char *pname;
|
||||
char *pname_esc;
|
||||
int pname_esc_len;
|
||||
|
||||
|
||||
zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1,
|
||||
&class_name, &pname);
|
||||
pname_esc = php_addcslashes(pname, strlen(pname), &pname_esc_len, 0,
|
||||
@ -469,7 +469,7 @@ PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC)
|
||||
buffer_append_spaces(buf, level - 1);
|
||||
}
|
||||
smart_str_appendc(buf, ')');
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case IS_OBJECT:
|
||||
@ -802,7 +802,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var
|
||||
BG(serialize_lock)++;
|
||||
res = call_user_function_ex(CG(function_table), &struc, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC);
|
||||
BG(serialize_lock)--;
|
||||
|
||||
|
||||
if (EG(exception)) {
|
||||
if (retval_ptr) {
|
||||
zval_ptr_dtor(&retval_ptr);
|
||||
@ -951,6 +951,8 @@ PHP_FUNCTION(unserialize)
|
||||
int buf_len;
|
||||
const unsigned char *p;
|
||||
php_unserialize_data_t var_hash;
|
||||
int oldlevel;
|
||||
zval *old_rval = return_value;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
|
||||
RETURN_FALSE;
|
||||
@ -970,6 +972,19 @@ PHP_FUNCTION(unserialize)
|
||||
}
|
||||
RETURN_FALSE;
|
||||
}
|
||||
if (return_value != old_rval) {
|
||||
/*
|
||||
* Terrible hack due to the fact that executor passes us zval *,
|
||||
* but unserialize with r/R wants to replace it with another zval *
|
||||
*/
|
||||
zval_dtor(old_rval);
|
||||
*old_rval = *return_value;
|
||||
zval_copy_ctor(old_rval);
|
||||
var_push_dtor_no_addref(&var_hash, &return_value);
|
||||
var_push_dtor_no_addref(&var_hash, &old_rval);
|
||||
} else {
|
||||
var_push_dtor(&var_hash, &return_value);
|
||||
}
|
||||
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
|
||||
}
|
||||
/* }}} */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Generated by re2c 0.13.7.5 on Sun Aug 23 19:50:03 2015 */
|
||||
/* Generated by re2c 0.13.7.5 on Mon Aug 31 23:15:46 2015 */
|
||||
#line 1 "ext/standard/var_unserializer.re"
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
@ -69,7 +69,7 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
|
||||
|
||||
var_hash = (*var_hashx)->last_dtor;
|
||||
#if VAR_ENTRIES_DBG
|
||||
fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
|
||||
fprintf(stderr, "var_push_dtor(%p, %ld): %d\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
|
||||
#endif
|
||||
|
||||
if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
|
||||
@ -100,7 +100,7 @@ PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rv
|
||||
|
||||
var_hash = (*var_hashx)->last_dtor;
|
||||
#if VAR_ENTRIES_DBG
|
||||
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));
|
||||
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));
|
||||
#endif
|
||||
|
||||
if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
|
||||
@ -179,6 +179,9 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
|
||||
|
||||
while (var_hash) {
|
||||
for (i = 0; i < var_hash->used_slots; i++) {
|
||||
#if VAR_ENTRIES_DBG
|
||||
fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_hash->data[i], Z_REFCOUNT_P(var_hash->data[i]));
|
||||
#endif
|
||||
zval_ptr_dtor(&var_hash->data[i]);
|
||||
}
|
||||
next = var_hash->next;
|
||||
@ -239,7 +242,7 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen
|
||||
#define YYMARKER marker
|
||||
|
||||
|
||||
#line 247 "ext/standard/var_unserializer.re"
|
||||
#line 250 "ext/standard/var_unserializer.re"
|
||||
|
||||
|
||||
|
||||
@ -484,7 +487,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
|
||||
|
||||
|
||||
|
||||
#line 488 "ext/standard/var_unserializer.c"
|
||||
#line 491 "ext/standard/var_unserializer.c"
|
||||
{
|
||||
YYCTYPE yych;
|
||||
static const unsigned char yybm[] = {
|
||||
@ -544,9 +547,9 @@ yy2:
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == ':') goto yy95;
|
||||
yy3:
|
||||
#line 839 "ext/standard/var_unserializer.re"
|
||||
#line 845 "ext/standard/var_unserializer.re"
|
||||
{ return 0; }
|
||||
#line 550 "ext/standard/var_unserializer.c"
|
||||
#line 553 "ext/standard/var_unserializer.c"
|
||||
yy4:
|
||||
yych = *(YYMARKER = ++YYCURSOR);
|
||||
if (yych == ':') goto yy89;
|
||||
@ -589,13 +592,13 @@ yy13:
|
||||
goto yy3;
|
||||
yy14:
|
||||
++YYCURSOR;
|
||||
#line 833 "ext/standard/var_unserializer.re"
|
||||
#line 839 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
/* this is the case where we have less data than planned */
|
||||
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data");
|
||||
return 0; /* not sure if it should be 0 or 1 here? */
|
||||
}
|
||||
#line 599 "ext/standard/var_unserializer.c"
|
||||
#line 602 "ext/standard/var_unserializer.c"
|
||||
yy16:
|
||||
yych = *++YYCURSOR;
|
||||
goto yy3;
|
||||
@ -626,7 +629,7 @@ yy20:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych != '"') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 687 "ext/standard/var_unserializer.re"
|
||||
#line 692 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
size_t len, len2, len3, maxlen;
|
||||
long elements;
|
||||
@ -642,6 +645,7 @@ yy20:
|
||||
zval **args[1];
|
||||
zval *arg_func_name;
|
||||
|
||||
if (!var_hash) return 0;
|
||||
if (*start == 'C') {
|
||||
custom_object = 1;
|
||||
}
|
||||
@ -772,7 +776,7 @@ yy20:
|
||||
|
||||
return object_common2(UNSERIALIZE_PASSTHRU, elements);
|
||||
}
|
||||
#line 776 "ext/standard/var_unserializer.c"
|
||||
#line 780 "ext/standard/var_unserializer.c"
|
||||
yy25:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych <= ',') {
|
||||
@ -797,15 +801,16 @@ yy27:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych != '"') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 679 "ext/standard/var_unserializer.re"
|
||||
#line 683 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
if (!var_hash) return 0;
|
||||
|
||||
INIT_PZVAL(*rval);
|
||||
|
||||
return object_common2(UNSERIALIZE_PASSTHRU,
|
||||
object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
|
||||
}
|
||||
#line 809 "ext/standard/var_unserializer.c"
|
||||
#line 814 "ext/standard/var_unserializer.c"
|
||||
yy32:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych == '+') goto yy33;
|
||||
@ -826,11 +831,12 @@ yy34:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych != '{') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 659 "ext/standard/var_unserializer.re"
|
||||
#line 662 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
long elements = parse_iv(start + 2);
|
||||
/* use iv() not uiv() in order to check data range */
|
||||
*p = YYCURSOR;
|
||||
if (!var_hash) return 0;
|
||||
|
||||
if (elements < 0) {
|
||||
return 0;
|
||||
@ -846,7 +852,7 @@ yy34:
|
||||
|
||||
return finish_nested_data(UNSERIALIZE_PASSTHRU);
|
||||
}
|
||||
#line 850 "ext/standard/var_unserializer.c"
|
||||
#line 856 "ext/standard/var_unserializer.c"
|
||||
yy39:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych == '+') goto yy40;
|
||||
@ -867,7 +873,7 @@ yy41:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych != '"') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 630 "ext/standard/var_unserializer.re"
|
||||
#line 633 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
size_t len, maxlen;
|
||||
char *str;
|
||||
@ -896,7 +902,7 @@ yy41:
|
||||
ZVAL_STRINGL(*rval, str, len, 0);
|
||||
return 1;
|
||||
}
|
||||
#line 900 "ext/standard/var_unserializer.c"
|
||||
#line 906 "ext/standard/var_unserializer.c"
|
||||
yy46:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych == '+') goto yy47;
|
||||
@ -917,7 +923,7 @@ yy48:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych != '"') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 602 "ext/standard/var_unserializer.re"
|
||||
#line 605 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
size_t len, maxlen;
|
||||
char *str;
|
||||
@ -945,7 +951,7 @@ yy48:
|
||||
ZVAL_STRINGL(*rval, str, len, 1);
|
||||
return 1;
|
||||
}
|
||||
#line 949 "ext/standard/var_unserializer.c"
|
||||
#line 955 "ext/standard/var_unserializer.c"
|
||||
yy53:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych <= '/') {
|
||||
@ -1033,7 +1039,7 @@ yy61:
|
||||
}
|
||||
yy63:
|
||||
++YYCURSOR;
|
||||
#line 592 "ext/standard/var_unserializer.re"
|
||||
#line 595 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
#if SIZEOF_LONG == 4
|
||||
use_double:
|
||||
@ -1043,7 +1049,7 @@ use_double:
|
||||
ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL));
|
||||
return 1;
|
||||
}
|
||||
#line 1047 "ext/standard/var_unserializer.c"
|
||||
#line 1053 "ext/standard/var_unserializer.c"
|
||||
yy65:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych <= ',') {
|
||||
@ -1102,7 +1108,7 @@ yy73:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych != ';') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 577 "ext/standard/var_unserializer.re"
|
||||
#line 580 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
*p = YYCURSOR;
|
||||
INIT_PZVAL(*rval);
|
||||
@ -1117,7 +1123,7 @@ yy73:
|
||||
|
||||
return 1;
|
||||
}
|
||||
#line 1121 "ext/standard/var_unserializer.c"
|
||||
#line 1127 "ext/standard/var_unserializer.c"
|
||||
yy76:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych == 'N') goto yy73;
|
||||
@ -1144,7 +1150,7 @@ yy79:
|
||||
if (yych <= '9') goto yy79;
|
||||
if (yych != ';') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 550 "ext/standard/var_unserializer.re"
|
||||
#line 553 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
#if SIZEOF_LONG == 4
|
||||
int digits = YYCURSOR - start - 3;
|
||||
@ -1171,7 +1177,7 @@ yy79:
|
||||
ZVAL_LONG(*rval, parse_iv(start + 2));
|
||||
return 1;
|
||||
}
|
||||
#line 1175 "ext/standard/var_unserializer.c"
|
||||
#line 1181 "ext/standard/var_unserializer.c"
|
||||
yy83:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych <= '/') goto yy18;
|
||||
@ -1179,24 +1185,24 @@ yy83:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych != ';') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 543 "ext/standard/var_unserializer.re"
|
||||
#line 546 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
*p = YYCURSOR;
|
||||
INIT_PZVAL(*rval);
|
||||
ZVAL_BOOL(*rval, parse_iv(start + 2));
|
||||
return 1;
|
||||
}
|
||||
#line 1190 "ext/standard/var_unserializer.c"
|
||||
#line 1196 "ext/standard/var_unserializer.c"
|
||||
yy87:
|
||||
++YYCURSOR;
|
||||
#line 536 "ext/standard/var_unserializer.re"
|
||||
#line 539 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
*p = YYCURSOR;
|
||||
INIT_PZVAL(*rval);
|
||||
ZVAL_NULL(*rval);
|
||||
return 1;
|
||||
}
|
||||
#line 1200 "ext/standard/var_unserializer.c"
|
||||
#line 1206 "ext/standard/var_unserializer.c"
|
||||
yy89:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych <= ',') {
|
||||
@ -1219,7 +1225,7 @@ yy91:
|
||||
if (yych <= '9') goto yy91;
|
||||
if (yych != ';') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 513 "ext/standard/var_unserializer.re"
|
||||
#line 516 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
long id;
|
||||
|
||||
@ -1242,7 +1248,7 @@ yy91:
|
||||
|
||||
return 1;
|
||||
}
|
||||
#line 1246 "ext/standard/var_unserializer.c"
|
||||
#line 1252 "ext/standard/var_unserializer.c"
|
||||
yy95:
|
||||
yych = *++YYCURSOR;
|
||||
if (yych <= ',') {
|
||||
@ -1265,7 +1271,7 @@ yy97:
|
||||
if (yych <= '9') goto yy97;
|
||||
if (yych != ';') goto yy18;
|
||||
++YYCURSOR;
|
||||
#line 492 "ext/standard/var_unserializer.re"
|
||||
#line 495 "ext/standard/var_unserializer.re"
|
||||
{
|
||||
long id;
|
||||
|
||||
@ -1278,7 +1284,7 @@ yy97:
|
||||
}
|
||||
|
||||
if (*rval != NULL) {
|
||||
zval_ptr_dtor(rval);
|
||||
var_push_dtor_no_addref(var_hash, rval);
|
||||
}
|
||||
*rval = *rval_ref;
|
||||
Z_ADDREF_PP(rval);
|
||||
@ -1286,9 +1292,9 @@ yy97:
|
||||
|
||||
return 1;
|
||||
}
|
||||
#line 1290 "ext/standard/var_unserializer.c"
|
||||
#line 1296 "ext/standard/var_unserializer.c"
|
||||
}
|
||||
#line 841 "ext/standard/var_unserializer.re"
|
||||
#line 847 "ext/standard/var_unserializer.re"
|
||||
|
||||
|
||||
return 0;
|
||||
|
@ -67,7 +67,7 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
|
||||
|
||||
var_hash = (*var_hashx)->last_dtor;
|
||||
#if VAR_ENTRIES_DBG
|
||||
fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
|
||||
fprintf(stderr, "var_push_dtor(%p, %ld): %d\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
|
||||
#endif
|
||||
|
||||
if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
|
||||
@ -98,7 +98,7 @@ PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rv
|
||||
|
||||
var_hash = (*var_hashx)->last_dtor;
|
||||
#if VAR_ENTRIES_DBG
|
||||
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));
|
||||
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));
|
||||
#endif
|
||||
|
||||
if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
|
||||
@ -177,6 +177,9 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
|
||||
|
||||
while (var_hash) {
|
||||
for (i = 0; i < var_hash->used_slots; i++) {
|
||||
#if VAR_ENTRIES_DBG
|
||||
fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_hash->data[i], Z_REFCOUNT_P(var_hash->data[i]));
|
||||
#endif
|
||||
zval_ptr_dtor(&var_hash->data[i]);
|
||||
}
|
||||
next = var_hash->next;
|
||||
@ -501,7 +504,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
|
||||
}
|
||||
|
||||
if (*rval != NULL) {
|
||||
zval_ptr_dtor(rval);
|
||||
var_push_dtor_no_addref(var_hash, rval);
|
||||
}
|
||||
*rval = *rval_ref;
|
||||
Z_ADDREF_PP(rval);
|
||||
@ -660,6 +663,7 @@ use_double:
|
||||
long elements = parse_iv(start + 2);
|
||||
/* use iv() not uiv() in order to check data range */
|
||||
*p = YYCURSOR;
|
||||
if (!var_hash) return 0;
|
||||
|
||||
if (elements < 0) {
|
||||
return 0;
|
||||
@ -677,6 +681,7 @@ use_double:
|
||||
}
|
||||
|
||||
"o:" iv ":" ["] {
|
||||
if (!var_hash) return 0;
|
||||
|
||||
INIT_PZVAL(*rval);
|
||||
|
||||
@ -699,6 +704,7 @@ object ":" uiv ":" ["] {
|
||||
zval **args[1];
|
||||
zval *arg_func_name;
|
||||
|
||||
if (!var_hash) return 0;
|
||||
if (*start == 'C') {
|
||||
custom_object = 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user