From 865f85718fae288c2a2023c537000c9d1a9356ed Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 29 Jun 2010 00:58:31 +0000 Subject: [PATCH] fix SplObjectStorage unserialization (CVE-2010-2225) --- NEWS | 2 + ext/spl/spl_observer.c | 20 ++++++++ .../SplObjectStorage_unserialize_bad.phpt | 45 ++++++++++++++++++ .../SplObjectStorage_unserialize_nested.phpt | 47 +++++++++++++++++++ ext/standard/php_var.h | 1 + ext/standard/var_unserializer.c | 4 +- ext/standard/var_unserializer.re | 2 +- 7 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 ext/spl/tests/SplObjectStorage_unserialize_bad.phpt create mode 100644 ext/spl/tests/SplObjectStorage_unserialize_nested.phpt diff --git a/NEWS b/NEWS index 0b7755c52c..9a2e8897fe 100644 --- a/NEWS +++ b/NEWS @@ -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) diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index 7676d03c56..4b1dd1fa3e 100755 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -115,6 +115,7 @@ static void spl_object_storage_dtor(spl_SplObjectStorageElement *element) /* {{{ 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 @@ SPL_METHOD(SplObjectStorage, unserialize) 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 @@ SPL_METHOD(SplObjectStorage, unserialize) 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); diff --git a/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt b/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt new file mode 100644 index 0000000000..9e3f3605b7 --- /dev/null +++ b/ext/spl/tests/SplObjectStorage_unserialize_bad.phpt @@ -0,0 +1,45 @@ +--TEST-- +SPL: Test that serialized blob contains unique elements (CVE-2010-2225) +--FILE-- +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) { + } + } + } +} + diff --git a/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt b/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt new file mode 100644 index 0000000000..56e83f805d --- /dev/null +++ b/ext/spl/tests/SplObjectStorage_unserialize_nested.phpt @@ -0,0 +1,47 @@ +--TEST-- +SPL: Test unserializing tested & linked storage +--FILE-- +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) + } + } +} + diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h index ed03e3f2eb..60c7782de6 100644 --- a/ext/standard/php_var.h +++ b/ext/standard/php_var.h @@ -60,6 +60,7 @@ PHPAPI int php_var_unserialize(zval **rval, const unsigned char **p, const unsig 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) \ diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index 1ed2710f1f..bd5d43b29d 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -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 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval) 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; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 689fd90cd6..43c230f527 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -54,7 +54,7 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval) 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; -- 2.40.0