CVE-2016-7411 (published 2016-09-17T21:59Z)

ext/standard/var_unserializer.re in PHP before 5.6.26 mishandles object-deserialization failures, which allows remote attackers to cause a denial of service (memory corruption) or possibly have unspecified other impact via an unserialize call that references a partially constructed object.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-119
cwe_name: Improper Restriction of Operations within the Bounds of a Memory Buffer
description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
extended_description: 
            Certain languages allow direct addressing of memory locations and do not automatically ensure that these locations are valid for the memory buffer that is being referenced. This can cause read or write operations to be performed on memory locations that may be associated with other variables, data structures, or internal program data.
         
         

Fix information:
repo_url: https://github.com/php/php-src
commit_details: repo_url: https://github.com/php/php-src
author: Stanislav Malyshev
author_date: 2016-09-11 21:19:29-07:00
msg: Fix bug #73052 - Memory Corruption in During Deserialized-object Destruction
repository: repo_url: https://github.com/php/php-src
repo_name: php/php-src
description: The PHP Interpreter
repo_language: C
owner: php
file_changes: filename: zend_objects_API.c
change_type: ModificationType.MODIFY
diff: @@ -215,7 +215,7 @@ ZEND_API void zend_objects_store_del_ref_by_handle_ex(zend_object_handle handle,
 					} zend_end_try();
 				}
 			}
-			
+
 			/* re-read the object from the object store as the store might have been reallocated in the dtor */
 			obj = &EG(objects_store).object_buckets[handle].bucket.obj;
 
@@ -306,8 +306,8 @@ ZEND_API void zend_object_store_ctor_failed(zval *zobject TSRMLS_DC)
 {
 	zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
 	zend_object_store_bucket *obj_bucket = &EG(objects_store).object_buckets[handle];
-	
-	obj_bucket->bucket.obj.handlers = Z_OBJ_HT_P(zobject);;
+
+	obj_bucket->bucket.obj.handlers = Z_OBJ_HT_P(zobject);
 	obj_bucket->destructor_called = 1;
 }
 

programming_language: C
filename: bug73052.phpt
change_type: ModificationType.ADD
diff: @@ -0,0 +1,18 @@
+--TEST--
+Bug #73052: Memory Corruption in During Deserialized-object Destruction
+--FILE--
+<?php
+
+class obj {
+    var $ryat;
+    public function __destruct() {
+        $this->ryat = null;
+    }
+}
+
+$poc = 'O:3:"obj":1:{';
+var_dump(unserialize($poc));
+?>
+--EXPECTF--
+Notice: unserialize(): Error at offset 13 of 13 bytes in %sbug73052.php on line %d
+bool(false)

programming_language: PHP
filename: var_unserializer.c
change_type: ModificationType.MODIFY
diff: @@ -437,6 +437,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
 	    /* We've got partially constructed object on our hands here. Wipe it. */
 	    if(Z_TYPE_PP(rval) == IS_OBJECT) {
 	       zend_hash_clean(Z_OBJPROP_PP(rval));
+	       zend_object_store_ctor_failed(*rval TSRMLS_CC);
 	    }
 	    ZVAL_NULL(*rval);
 		return 0;
@@ -491,7 +492,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 
 
 
-#line 495 "ext/standard/var_unserializer.c"
+#line 496 "ext/standard/var_unserializer.c"
 {
 	YYCTYPE yych;
 	static const unsigned char yybm[] = {
@@ -551,9 +552,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	yych = *(YYMARKER = ++YYCURSOR);
 	if (yych == ':') goto yy95;
 yy3:
-#line 860 "ext/standard/var_unserializer.re"
+#line 861 "ext/standard/var_unserializer.re"
 	{ return 0; }
-#line 557 "ext/standard/var_unserializer.c"
+#line 558 "ext/standard/var_unserializer.c"
 yy4:
 	yych = *(YYMARKER = ++YYCURSOR);
 	if (yych == ':') goto yy89;
@@ -596,13 +597,13 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	goto yy3;
 yy14:
 	++YYCURSOR;
-#line 854 "ext/standard/var_unserializer.re"
+#line 855 "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 606 "ext/standard/var_unserializer.c"
+#line 607 "ext/standard/var_unserializer.c"
 yy16:
 	yych = *++YYCURSOR;
 	goto yy3;
@@ -633,7 +634,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	yych = *++YYCURSOR;
 	if (yych != '"') goto yy18;
 	++YYCURSOR;
-#line 707 "ext/standard/var_unserializer.re"
+#line 708 "ext/standard/var_unserializer.re"
 	{
 	size_t len, len2, len3, maxlen;
 	long elements;
@@ -780,7 +781,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 
 	return object_common2(UNSERIALIZE_PASSTHRU, elements);
 }
-#line 784 "ext/standard/var_unserializer.c"
+#line 785 "ext/standard/var_unserializer.c"
 yy25:
 	yych = *++YYCURSOR;
 	if (yych <= ',') {
@@ -805,7 +806,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	yych = *++YYCURSOR;
 	if (yych != '"') goto yy18;
 	++YYCURSOR;
-#line 698 "ext/standard/var_unserializer.re"
+#line 699 "ext/standard/var_unserializer.re"
 	{
     if (!var_hash) return 0;
 
@@ -814,7 +815,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	return object_common2(UNSERIALIZE_PASSTHRU,
 			object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
 }
-#line 818 "ext/standard/var_unserializer.c"
+#line 819 "ext/standard/var_unserializer.c"
 yy32:
 	yych = *++YYCURSOR;
 	if (yych == '+') goto yy33;
@@ -835,7 +836,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	yych = *++YYCURSOR;
 	if (yych != '{') goto yy18;
 	++YYCURSOR;
-#line 677 "ext/standard/var_unserializer.re"
+#line 678 "ext/standard/var_unserializer.re"
 	{
 	long elements = parse_iv(start + 2);
 	/* use iv() not uiv() in order to check data range */
@@ -856,7 +857,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 
 	return finish_nested_data(UNSERIALIZE_PASSTHRU);
 }
-#line 860 "ext/standard/var_unserializer.c"
+#line 861 "ext/standard/var_unserializer.c"
 yy39:
 	yych = *++YYCURSOR;
 	if (yych == '+') goto yy40;
@@ -877,7 +878,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	yych = *++YYCURSOR;
 	if (yych != '"') goto yy18;
 	++YYCURSOR;
-#line 642 "ext/standard/var_unserializer.re"
+#line 643 "ext/standard/var_unserializer.re"
 	{
 	size_t len, maxlen;
 	char *str;
@@ -912,7 +913,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	ZVAL_STRINGL(*rval, str, len, 0);
 	return 1;
 }
-#line 916 "ext/standard/var_unserializer.c"
+#line 917 "ext/standard/var_unserializer.c"
 yy46:
 	yych = *++YYCURSOR;
 	if (yych == '+') goto yy47;
@@ -933,7 +934,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	yych = *++YYCURSOR;
 	if (yych != '"') goto yy18;
 	++YYCURSOR;
-#line 609 "ext/standard/var_unserializer.re"
+#line 610 "ext/standard/var_unserializer.re"
 	{
 	size_t len, maxlen;
 	char *str;
@@ -966,7 +967,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	ZVAL_STRINGL(*rval, str, len, 1);
 	return 1;
 }
-#line 970 "ext/standard/var_unserializer.c"
+#line 971 "ext/standard/var_unserializer.c"
 yy53:
 	yych = *++YYCURSOR;
 	if (yych <= '/') {
@@ -1054,7 +1055,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	}
 yy63:
 	++YYCURSOR;
-#line 599 "ext/standard/var_unserializer.re"
+#line 600 "ext/standard/var_unserializer.re"
 	{
 #if SIZEOF_LONG == 4
 use_double:
@@ -1064,7 +1065,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL));
 	return 1;
 }
-#line 1068 "ext/standard/var_unserializer.c"
+#line 1069 "ext/standard/var_unserializer.c"
 yy65:
 	yych = *++YYCURSOR;
 	if (yych <= ',') {
@@ -1123,7 +1124,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	yych = *++YYCURSOR;
 	if (yych != ';') goto yy18;
 	++YYCURSOR;
-#line 584 "ext/standard/var_unserializer.re"
+#line 585 "ext/standard/var_unserializer.re"
 	{
 	*p = YYCURSOR;
 	INIT_PZVAL(*rval);
@@ -1138,7 +1139,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 
 	return 1;
 }
-#line 1142 "ext/standard/var_unserializer.c"
+#line 1143 "ext/standard/var_unserializer.c"
 yy76:
 	yych = *++YYCURSOR;
 	if (yych == 'N') goto yy73;
@@ -1165,7 +1166,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	if (yych <= '9') goto yy79;
 	if (yych != ';') goto yy18;
 	++YYCURSOR;
-#line 557 "ext/standard/var_unserializer.re"
+#line 558 "ext/standard/var_unserializer.re"
 	{
 #if SIZEOF_LONG == 4
 	int digits = YYCURSOR - start - 3;
@@ -1192,7 +1193,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	ZVAL_LONG(*rval, parse_iv(start + 2));
 	return 1;
 }
-#line 1196 "ext/standard/var_unserializer.c"
+#line 1197 "ext/standard/var_unserializer.c"
 yy83:
 	yych = *++YYCURSOR;
 	if (yych <= '/') goto yy18;
@@ -1200,24 +1201,24 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	yych = *++YYCURSOR;
 	if (yych != ';') goto yy18;
 	++YYCURSOR;
-#line 550 "ext/standard/var_unserializer.re"
+#line 551 "ext/standard/var_unserializer.re"
 	{
 	*p = YYCURSOR;
 	INIT_PZVAL(*rval);
 	ZVAL_BOOL(*rval, parse_iv(start + 2));
 	return 1;
 }
-#line 1211 "ext/standard/var_unserializer.c"
+#line 1212 "ext/standard/var_unserializer.c"
 yy87:
 	++YYCURSOR;
-#line 543 "ext/standard/var_unserializer.re"
+#line 544 "ext/standard/var_unserializer.re"
 	{
 	*p = YYCURSOR;
 	INIT_PZVAL(*rval);
 	ZVAL_NULL(*rval);
 	return 1;
 }
-#line 1221 "ext/standard/var_unserializer.c"
+#line 1222 "ext/standard/var_unserializer.c"
 yy89:
 	yych = *++YYCURSOR;
 	if (yych <= ',') {
@@ -1240,7 +1241,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	if (yych <= '9') goto yy91;
 	if (yych != ';') goto yy18;
 	++YYCURSOR;
-#line 520 "ext/standard/var_unserializer.re"
+#line 521 "ext/standard/var_unserializer.re"
 	{
 	long id;
 
@@ -1263,7 +1264,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 
 	return 1;
 }
-#line 1267 "ext/standard/var_unserializer.c"
+#line 1268 "ext/standard/var_unserializer.c"
 yy95:
 	yych = *++YYCURSOR;
 	if (yych <= ',') {
@@ -1286,7 +1287,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 	if (yych <= '9') goto yy97;
 	if (yych != ';') goto yy18;
 	++YYCURSOR;
-#line 499 "ext/standard/var_unserializer.re"
+#line 500 "ext/standard/var_unserializer.re"
 	{
 	long id;
 
@@ -1307,9 +1308,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
 
 	return 1;
 }
-#line 1311 "ext/standard/var_unserializer.c"
+#line 1312 "ext/standard/var_unserializer.c"
 }
-#line 862 "ext/standard/var_unserializer.re"
+#line 863 "ext/standard/var_unserializer.re"
 
 
 	return 0;

programming_language: C
filename: var_unserializer.re
change_type: ModificationType.MODIFY
diff: @@ -441,6 +441,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
 	    /* We've got partially constructed object on our hands here. Wipe it. */
 	    if(Z_TYPE_PP(rval) == IS_OBJECT) {
 	       zend_hash_clean(Z_OBJPROP_PP(rval));
+	       zend_object_store_ctor_failed(*rval TSRMLS_CC);
 	    }
 	    ZVAL_NULL(*rval);
 		return 0;

programming_language: C