stas Tue, 29 Jun 2010 00:58:31 +0000 Revision: http://svn.php.net/viewvc?view=revision&revision=300843
Log: fix SplObjectStorage unserialization (CVE-2010-2225) Changed paths: U php/php-src/branches/PHP_5_2/NEWS A php/php-src/branches/PHP_5_2/ext/spl/SplObjectStorage_unserialize_bad.phpt A php/php-src/branches/PHP_5_2/ext/spl/SplObjectStorage_unserialize_nested.phpt U php/php-src/branches/PHP_5_2/ext/spl/spl_observer.c U php/php-src/branches/PHP_5_3/NEWS U php/php-src/branches/PHP_5_3/ext/spl/spl_observer.c A php/php-src/branches/PHP_5_3/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt A php/php-src/branches/PHP_5_3/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt U php/php-src/branches/PHP_5_3/ext/standard/php_var.h U php/php-src/branches/PHP_5_3/ext/standard/var_unserializer.c U php/php-src/branches/PHP_5_3/ext/standard/var_unserializer.re U php/php-src/trunk/ext/spl/spl_observer.c A php/php-src/trunk/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt A php/php-src/trunk/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt U php/php-src/trunk/ext/standard/php_var.h U php/php-src/trunk/ext/standard/var_unserializer.c U php/php-src/trunk/ext/standard/var_unserializer.re
Modified: php/php-src/branches/PHP_5_2/NEWS =================================================================== --- php/php-src/branches/PHP_5_2/NEWS 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/branches/PHP_5_2/NEWS 2010-06-29 00:58:31 UTC (rev 300843) @@ -1,6 +1,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? Jun 2010, PHP 5.2.14 +- Fixed SplObjectStorage unserialization problems (CVE-2010-2225). (Stas) + - Fixed bug #52163 (SplFileObject::fgetss() fails due to parameter that can't be set). (Felipe) - Fixed bug #52162 (custom request header variables with numbers are removed). Added: php/php-src/branches/PHP_5_2/ext/spl/SplObjectStorage_unserialize_bad.phpt =================================================================== --- php/php-src/branches/PHP_5_2/ext/spl/SplObjectStorage_unserialize_bad.phpt (rev 0) +++ php/php-src/branches/PHP_5_2/ext/spl/SplObjectStorage_unserialize_bad.phpt 2010-06-29 00:58:31 UTC (rev 300843) @@ -0,0 +1,24 @@ +--TEST-- +SPL: Test that serialized blob contains unique elements (CVE-2010-2225) +--FILE-- +<?php + +$badblobs = array( +'x:i:2;i:0;;i:0;;m:a:0:{}', +'x:i:2;O:8:"stdClass":0:{};R:1;;m:a:0:{}', +'x:i:3;O:8:"stdClass":0:{};r:1;;r:1;;m:a:0:{}', +); +foreach($badblobs as $blob) { +try { + $so = new SplObjectStorage(); + $so->unserialize($blob); + var_dump($so); +} catch(UnexpectedValueException $e) { + echo $e->getMessage()."\n"; +} +} +--EXPECTF-- +Error at offset 6 of 24 bytes +Error at offset 26 of 39 bytes +object(SplObjectStorage)#2 (0) { +} Added: php/php-src/branches/PHP_5_2/ext/spl/SplObjectStorage_unserialize_nested.phpt =================================================================== --- php/php-src/branches/PHP_5_2/ext/spl/SplObjectStorage_unserialize_nested.phpt (rev 0) +++ php/php-src/branches/PHP_5_2/ext/spl/SplObjectStorage_unserialize_nested.phpt 2010-06-29 00:58:31 UTC (rev 300843) @@ -0,0 +1,33 @@ +--TEST-- +SPL: Test unserializing tested & linked storage +--FILE-- +<?php +$o = new StdClass(); +$a = new StdClass(); + +$o->a = $a; + +$so = new SplObjectStorage(); + +$so->attach($o); +$so->attach($a); + +$s = serialize($so); +echo $s."\n"; + +$so1 = unserialize($s); +var_dump($so1); +foreach($so1 as $obj) { + var_dump($obj); +} +--EXPECTF-- +C:16:"SplObjectStorage":66:{x:i:2;O:8:"stdClass":1:{s:1:"a";O:8:"stdClass":0:{}};r:2;;m:a:0:{}} +object(SplObjectStorage)#4 (0) { +} +object(stdClass)#5 (1) { + ["a"]=> + object(stdClass)#6 (0) { + } +} +object(stdClass)#6 (0) { +} Modified: php/php-src/branches/PHP_5_2/ext/spl/spl_observer.c =================================================================== --- php/php-src/branches/PHP_5_2/ext/spl/spl_observer.c 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/branches/PHP_5_2/ext/spl/spl_observer.c 2010-06-29 00:58:31 UTC (rev 300843) @@ -182,6 +182,21 @@ intern->index = 0; } /* }}} */ +int spl_object_storage_contains(spl_SplObjectStorage *intern, zval *obj TSRMLS_DC) /* {{{ */ +{ +#if HAVE_PACKED_OBJECT_VALUE + return zend_hash_exists(&intern->storage, (char*)&Z_OBJVAL_P(obj), sizeof(zend_object_value)); +#else + { + zend_object_value zvalue; + memset(&zvalue, 0, sizeof(zend_object_value)); + zvalue.handle = Z_OBJ_HANDLE_P(obj); + zvalue.handlers = Z_OBJ_HT_P(obj); + return zend_hash_exists(&intern->storage, (char*)&zvalue, sizeof(zend_object_value)); + } +#endif +} /* }}} */ + /* {{{ proto bool SplObjectStorage::contains($obj) Determine whethe an object is contained in the storage */ SPL_METHOD(SplObjectStorage, contains) @@ -193,17 +208,7 @@ return; } -#if HAVE_PACKED_OBJECT_VALUE - RETURN_BOOL(zend_hash_exists(&intern->storage, (char*)&Z_OBJVAL_P(obj), sizeof(zend_object_value))); -#else - { - zend_object_value zvalue; - memset(&zvalue, 0, sizeof(zend_object_value)); - zvalue.handle = Z_OBJ_HANDLE_P(obj); - zvalue.handlers = Z_OBJ_HT_P(obj); - RETURN_BOOL(zend_hash_exists(&intern->storage, (char*)&zvalue, sizeof(zend_object_value))); - } -#endif + RETURN_BOOL(spl_object_storage_contains(intern, obj TSRMLS_CC)); } /* }}} */ /* {{{ proto int SplObjectStorage::count() @@ -362,11 +367,22 @@ goto outexcept; } ++p; + if(*p != 'O' && *p != 'C' && *p != 'r') { + goto outexcept; + } ALLOC_INIT_ZVAL(pentry); if (!php_var_unserialize(&pentry, &p, s + buf_len, &var_hash TSRMLS_CC)) { zval_ptr_dtor(&pentry); goto outexcept; } + if(Z_TYPE_P(pentry) != IS_OBJECT) { + zval_ptr_dtor(&pentry); + goto outexcept; + } + if(spl_object_storage_contains(intern, pentry TSRMLS_CC)) { + zval_ptr_dtor(&pentry); + continue; + } spl_object_storage_attach(intern, pentry TSRMLS_CC); zval_ptr_dtor(&pentry); } Modified: php/php-src/branches/PHP_5_3/NEWS =================================================================== --- php/php-src/branches/PHP_5_3/NEWS 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/branches/PHP_5_3/NEWS 2010-06-29 00:58:31 UTC (rev 300843) @@ -1,6 +1,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 2010, PHP 5.3.3 RC2 +- Fixed SplObjectStorage unserialization problems (CVE-2010-2225). (Stas) + - Implemented FR #51295 (SQLite3::busyTimeout not existing). (Mark) - Fixed the mail.log ini setting when no filename was given. (Johannes) Modified: php/php-src/branches/PHP_5_3/ext/spl/spl_observer.c =================================================================== --- php/php-src/branches/PHP_5_3/ext/spl/spl_observer.c 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/branches/PHP_5_3/ext/spl/spl_observer.c 2010-06-29 00:58:31 UTC (rev 300843) @@ -115,6 +115,7 @@ zval_ptr_dtor(&element->inf); } /* }}} */ + spl_SplObjectStorageElement* spl_object_storage_get(spl_SplObjectStorage *intern, zval *obj TSRMLS_DC) /* {{{ */ { spl_SplObjectStorageElement *element; @@ -632,15 +633,24 @@ zval_ptr_dtor(&pcount); while(count-- > 0) { + spl_SplObjectStorageElement *pelement; + if (*p != ';') { goto outexcept; } ++p; + if(*p != 'O' && *p != 'C' && *p != 'r') { + goto outexcept; + } ALLOC_INIT_ZVAL(pentry); if (!php_var_unserialize(&pentry, &p, s + buf_len, &var_hash TSRMLS_CC)) { zval_ptr_dtor(&pentry); goto outexcept; } + if(Z_TYPE_P(pentry) != IS_OBJECT) { + zval_ptr_dtor(&pentry); + goto outexcept; + } ALLOC_INIT_ZVAL(pinf); if (*p == ',') { /* new version has inf */ ++p; @@ -649,6 +659,16 @@ goto outexcept; } } + + pelement = spl_object_storage_get(intern, pentry TSRMLS_CC); + if(pelement) { + if(pelement->inf) { + var_push_dtor(&var_hash, &pelement->inf); + } + if(pelement->obj) { + var_push_dtor(&var_hash, &pelement->obj); + } + } spl_object_storage_attach(intern, pentry, pinf TSRMLS_CC); zval_ptr_dtor(&pentry); zval_ptr_dtor(&pinf); Added: php/php-src/branches/PHP_5_3/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt 2010-06-29 00:58:31 UTC (rev 300843) @@ -0,0 +1,45 @@ +--TEST-- +SPL: Test that serialized blob contains unique elements (CVE-2010-2225) +--FILE-- +<?php + +$badblobs = array( +'x:i:2;i:0;,i:1;;i:0;,i:2;;m:a:0:{}', +'x:i:3;O:8:"stdClass":0:{},O:8:"stdClass":0:{};R:1;,i:1;;O:8:"stdClass":0:{},r:2;;m:a:0:{}', +'x:i:3;O:8:"stdClass":0:{},O:8:"stdClass":0:{};r:1;,i:1;;O:8:"stdClass":0:{},r:2;;m:a:0:{}', +); +foreach($badblobs as $blob) { +try { + $so = new SplObjectStorage(); + $so->unserialize($blob); + var_dump($so); +} catch(UnexpectedValueException $e) { + echo $e->getMessage()."\n"; +} +} +--EXPECTF-- +Error at offset 6 of 34 bytes +Error at offset 46 of 89 bytes +object(SplObjectStorage)#2 (1) { + ["storage":"SplObjectStorage":private]=> + array(2) { + ["%s"]=> + array(2) { + ["obj"]=> + object(stdClass)#3 (0) { + } + ["inf"]=> + int(1) + } + ["%s"]=> + array(2) { + ["obj"]=> + object(stdClass)#1 (0) { + } + ["inf"]=> + object(stdClass)#4 (0) { + } + } + } +} + Added: php/php-src/branches/PHP_5_3/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt 2010-06-29 00:58:31 UTC (rev 300843) @@ -0,0 +1,47 @@ +--TEST-- +SPL: Test unserializing tested & linked storage +--FILE-- +<?php +$o = new StdClass(); +$a = new StdClass(); + +$o->a = $a; + +$so = new SplObjectStorage(); + +$so[$o] = 1; +$so[$a] = 2; + +$s = serialize($so); +echo $s."\n"; + +$so1 = unserialize($s); +var_dump($so1); + +--EXPECTF-- +C:16:"SplObjectStorage":76:{x:i:2;O:8:"stdClass":1:{s:1:"a";O:8:"stdClass":0:{}},i:1;;r:2;,i:2;;m:a:0:{}} +object(SplObjectStorage)#4 (1) { + ["storage":"SplObjectStorage":private]=> + array(2) { + ["%s"]=> + array(2) { + ["obj"]=> + object(stdClass)#5 (1) { + ["a"]=> + object(stdClass)#6 (0) { + } + } + ["inf"]=> + int(1) + } + ["%s"]=> + array(2) { + ["obj"]=> + object(stdClass)#6 (0) { + } + ["inf"]=> + int(2) + } + } +} + Modified: php/php-src/branches/PHP_5_3/ext/standard/php_var.h =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/php_var.h 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/branches/PHP_5_3/ext/standard/php_var.h 2010-06-29 00:58:31 UTC (rev 300843) @@ -60,6 +60,7 @@ var_destroy(&(var_hash)) PHPAPI void var_replace(php_unserialize_data_t *var_hash, zval *ozval, zval **nzval); +PHPAPI void var_push_dtor(php_unserialize_data_t *var_hash, zval **val); PHPAPI void var_destroy(php_unserialize_data_t *var_hash); #define PHP_VAR_UNSERIALIZE_ZVAL_CHANGED(var_hash, ozval, nzval) \ Modified: php/php-src/branches/PHP_5_3/ext/standard/var_unserializer.c =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/var_unserializer.c 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/branches/PHP_5_3/ext/standard/var_unserializer.c 2010-06-29 00:58:31 UTC (rev 300843) @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Mon Apr 12 10:11:22 2010 */ +/* Generated by re2c 0.13.5 on Fri Jun 25 15:36:31 2010 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -56,7 +56,7 @@ var_hash->data[var_hash->used_slots++] = *rval; } -static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) +PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) { var_entries *var_hash = var_hashx->first_dtor, *prev = NULL; Modified: php/php-src/branches/PHP_5_3/ext/standard/var_unserializer.re =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/var_unserializer.re 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/branches/PHP_5_3/ext/standard/var_unserializer.re 2010-06-29 00:58:31 UTC (rev 300843) @@ -54,7 +54,7 @@ var_hash->data[var_hash->used_slots++] = *rval; } -static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) +PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) { var_entries *var_hash = var_hashx->first_dtor, *prev = NULL; Modified: php/php-src/trunk/ext/spl/spl_observer.c =================================================================== --- php/php-src/trunk/ext/spl/spl_observer.c 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/trunk/ext/spl/spl_observer.c 2010-06-29 00:58:31 UTC (rev 300843) @@ -728,15 +728,26 @@ zval_ptr_dtor(&pcount); while(count-- > 0) { + spl_SplObjectStorageElement *pelement; + char *hash; + int hash_len; + if (*p != ';') { goto outexcept; } ++p; + if(*p != 'O' && *p != 'C' && *p != 'r') { + goto outexcept; + } ALLOC_INIT_ZVAL(pentry); if (!php_var_unserialize(&pentry, &p, s + buf_len, &var_hash TSRMLS_CC)) { zval_ptr_dtor(&pentry); goto outexcept; } + if(Z_TYPE_P(pentry) != IS_OBJECT) { + zval_ptr_dtor(&pentry); + goto outexcept; + } ALLOC_INIT_ZVAL(pinf); if (*p == ',') { /* new version has inf */ ++p; @@ -745,6 +756,23 @@ goto outexcept; } } + + hash = spl_object_storage_get_hash(intern, getThis(), pentry, &hash_len TSRMLS_CC); + if (!hash) { + zval_ptr_dtor(&pentry); + zval_ptr_dtor(&pinf); + goto outexcept; + } + pelement = spl_object_storage_get(intern, hash, hash_len TSRMLS_CC); + spl_object_storage_free_hash(intern, hash); + if(pelement) { + if(pelement->inf) { + var_push_dtor(&var_hash, &pelement->inf); + } + if(pelement->obj) { + var_push_dtor(&var_hash, &pelement->obj); + } + } spl_object_storage_attach(intern, getThis(), pentry, pinf TSRMLS_CC); zval_ptr_dtor(&pentry); zval_ptr_dtor(&pinf); Added: php/php-src/trunk/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt =================================================================== --- php/php-src/trunk/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt (rev 0) +++ php/php-src/trunk/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt 2010-06-29 00:58:31 UTC (rev 300843) @@ -0,0 +1,45 @@ +--TEST-- +SPL: Test that serialized blob contains unique elements (CVE-2010-2225) +--FILE-- +<?php + +$badblobs = array( +'x:i:2;i:0;,i:1;;i:0;,i:2;;m:a:0:{}', +'x:i:3;O:8:"stdClass":0:{},O:8:"stdClass":0:{};R:1;,i:1;;O:8:"stdClass":0:{},r:2;;m:a:0:{}', +'x:i:3;O:8:"stdClass":0:{},O:8:"stdClass":0:{};r:1;,i:1;;O:8:"stdClass":0:{},r:2;;m:a:0:{}', +); +foreach($badblobs as $blob) { +try { + $so = new SplObjectStorage(); + $so->unserialize($blob); + var_dump($so); +} catch(UnexpectedValueException $e) { + echo $e->getMessage()."\n"; +} +} +--EXPECTF-- +Error at offset 6 of 34 bytes +Error at offset 46 of 89 bytes +object(SplObjectStorage)#2 (1) { + ["storage":"SplObjectStorage":private]=> + array(2) { + ["%s"]=> + array(2) { + ["obj"]=> + object(stdClass)#3 (0) { + } + ["inf"]=> + int(1) + } + ["%s"]=> + array(2) { + ["obj"]=> + object(stdClass)#1 (0) { + } + ["inf"]=> + object(stdClass)#3 (0) { + } + } + } +} + Added: php/php-src/trunk/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt =================================================================== --- php/php-src/trunk/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt (rev 0) +++ php/php-src/trunk/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt 2010-06-29 00:58:31 UTC (rev 300843) @@ -0,0 +1,47 @@ +--TEST-- +SPL: Test unserializing tested & linked storage +--FILE-- +<?php +$o = new StdClass(); +$a = new StdClass(); + +$o->a = $a; + +$so = new SplObjectStorage(); + +$so[$o] = 1; +$so[$a] = 2; + +$s = serialize($so); +echo $s."\n"; + +$so1 = unserialize($s); +var_dump($so1); + +--EXPECTF-- +C:16:"SplObjectStorage":76:{x:i:2;O:8:"stdClass":1:{s:1:"a";O:8:"stdClass":0:{}},i:1;;r:4;,i:2;;m:a:0:{}} +object(SplObjectStorage)#4 (1) { + ["storage":"SplObjectStorage":private]=> + array(2) { + ["%s"]=> + array(2) { + ["obj"]=> + object(stdClass)#5 (1) { + ["a"]=> + object(stdClass)#6 (0) { + } + } + ["inf"]=> + int(1) + } + ["%s"]=> + array(2) { + ["obj"]=> + object(stdClass)#6 (0) { + } + ["inf"]=> + int(2) + } + } +} + Modified: php/php-src/trunk/ext/standard/php_var.h =================================================================== --- php/php-src/trunk/ext/standard/php_var.h 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/trunk/ext/standard/php_var.h 2010-06-29 00:58:31 UTC (rev 300843) @@ -100,6 +100,7 @@ } while (0) PHPAPI void var_replace(php_unserialize_data_t *var_hash, zval *ozval, zval **nzval); +PHPAPI void var_push_dtor(php_unserialize_data_t *var_hash, zval **val); PHPAPI void var_destroy(php_unserialize_data_t *var_hash); #define PHP_VAR_UNSERIALIZE_ZVAL_CHANGED(var_hash, ozval, nzval) \ Modified: php/php-src/trunk/ext/standard/var_unserializer.c =================================================================== --- php/php-src/trunk/ext/standard/var_unserializer.c 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/trunk/ext/standard/var_unserializer.c 2010-06-29 00:58:31 UTC (rev 300843) @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Mon Jun 14 12:03:55 2010 */ +/* Generated by re2c 0.13.5 on Mon Jun 28 13:40:53 2010 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -59,7 +59,7 @@ var_hash->data[var_hash->used_slots++] = *rval; } -static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) +PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) { var_entries *var_hash = (*var_hashx)->first_dtor, *prev = NULL; #if 0 Modified: php/php-src/trunk/ext/standard/var_unserializer.re =================================================================== --- php/php-src/trunk/ext/standard/var_unserializer.re 2010-06-29 00:57:58 UTC (rev 300842) +++ php/php-src/trunk/ext/standard/var_unserializer.re 2010-06-29 00:58:31 UTC (rev 300843) @@ -57,7 +57,7 @@ var_hash->data[var_hash->used_slots++] = *rval; } -static inline void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) +PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) { var_entries *var_hash = (*var_hashx)->first_dtor, *prev = NULL; #if 0
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php