From 16b3003ffc6393e250f069aa28a78dc5a2c064b2 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Fri, 30 Dec 2016 16:59:46 -0800 Subject: [PATCH] CVE-2016-10161 Fix bug #73825 - Heap out of bounds read on unserialize in finish_nested_data() [roberto@debian.org: backported to 5.4.45] Bug: https://bugs.php.net/bug.php?id=73825 Origin: backport, http://git.php.net/?p=php-src.git;a=commitdiff;h=16b3003ffc6393e250f069aa28a78dc5a2c064b2 --- ext/standard/tests/serialize/bug73825.phpt | 12 +++++ ext/standard/var_unserializer.c | 80 ++++++++++++++++++------------ ext/standard/var_unserializer.re | 20 ++++++-- 3 files changed, 76 insertions(+), 36 deletions(-) create mode 100644 ext/standard/tests/serialize/bug73825.phpt --- /dev/null +++ php5.git/ext/standard/tests/serialize/bug73825.phpt @@ -0,0 +1,12 @@ +--TEST-- +Bug #73825 Heap out of bounds read on unserialize in finish_nested_data() +--FILE-- + +--EXPECTF-- +Warning: Bad unserialize data in %sbug73825.php on line %d + +Notice: unserialize(): Error at offset 13 of 15 bytes in %sbug73825.php on line %d +bool(false) --- php5.git.orig/ext/standard/var_unserializer.c +++ php5.git/ext/standard/var_unserializer.c @@ -404,6 +404,11 @@ { long elements; + if( *p >= max - 2) { + zend_error(E_WARNING, "Bad unserialize data"); + return -1; + } + elements = parse_iv2((*p) + 2, p); (*p) += 2; @@ -418,7 +423,7 @@ /* If this class implements Serializable, it should not land here but in object_custom(). The passed string obviously doesn't descend from the regular serializer. */ zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ce->name); - return 0; + return -1; } return elements; @@ -771,6 +776,11 @@ elements = object_common1(UNSERIALIZE_PASSTHRU, ce); + if (elements < 0) { + efree(class_name); + return 0; + } + if (incomplete_class) { php_store_class_name(*rval, class_name, len2); } @@ -803,12 +813,16 @@ if (yych != '"') goto yy18; ++YYCURSOR; { + long elements; if (!var_hash) return 0; INIT_PZVAL(*rval); - return object_common2(UNSERIALIZE_PASSTHRU, - object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); + elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR); + if (elements < 0) { + return 0; + } + return object_common2(UNSERIALIZE_PASSTHRU, elements); } yy32: yych = *++YYCURSOR; --- php5.git.orig/ext/standard/var_unserializer.re +++ php5.git/ext/standard/var_unserializer.re @@ -410,6 +410,11 @@ { long elements; + if( *p >= max - 2) { + zend_error(E_WARNING, "Bad unserialize data"); + return -1; + } + elements = parse_iv2((*p) + 2, p); (*p) += 2; @@ -424,7 +429,7 @@ /* If this class implements Serializable, it should not land here but in object_custom(). The passed string obviously doesn't descend from the regular serializer. */ zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ce->name); - return 0; + return -1; } return elements; @@ -691,12 +696,16 @@ } "o:" iv ":" ["] { + long elements; if (!var_hash) return 0; INIT_PZVAL(*rval); - return object_common2(UNSERIALIZE_PASSTHRU, - object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); + elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR); + if (elements < 0) { + return 0; + } + return object_common2(UNSERIALIZE_PASSTHRU, elements); } object ":" uiv ":" ["] { @@ -838,6 +847,11 @@ elements = object_common1(UNSERIALIZE_PASSTHRU, ce); + if (elements < 0) { + efree(class_name); + return 0; + } + if (incomplete_class) { php_store_class_name(*rval, class_name, len2); }