diff options
author | Nikita Popov <nikic@php.net> | 2013-06-17 23:44:13 +0200 |
---|---|---|
committer | Nikita Popov <nikic@php.net> | 2013-06-17 23:44:13 +0200 |
commit | 86434be9462c5b14dccc588afe6bc1b4a1433360 (patch) | |
tree | fa8ad4a310d3a094fc28b7cd57d551f645aa0844 | |
parent | d587efb7ee6df0d68bb91c3680194089c7d6cb62 (diff) | |
download | php-git-86434be9462c5b14dccc588afe6bc1b4a1433360.tar.gz |
Fix bug #65051: count() off by one inside unset()
nNumOfElements was incremented after the pDestructor code, so any
code in the dtor would get a wrong number of elements.
Right now the bucket deletion code is replicated in four places,
it should probably be moved off into one function (or rather,
zend_hash_apply_deleter should be used everywhere). The codes are
subtly different though in that the HANDLE_UNBLOCK_INTERRUPTIONS()
happens in different places. In particular it seems odd that in
some cases interruptions stay blocked during the destructor call.
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | Zend/tests/bug65051.phpt | 23 | ||||
-rw-r--r-- | Zend/zend_hash.c | 6 |
3 files changed, 27 insertions, 3 deletions
@@ -10,6 +10,7 @@ PHP NEWS . Fixed bug #64934 (Apache2 TS crash with get_browser()). (Anatol) . Fixed bug #64166 (quoted-printable-encode stream filter incorrectly discarding whitespace). (Michael M Slusarz) + . Fixed bug #65051 (count() off by one inside unset()). (Nikita) - FPM: . Implemented FR #64764 (add support for FPM init.d script). (Lior Kaplan) diff --git a/Zend/tests/bug65051.phpt b/Zend/tests/bug65051.phpt new file mode 100644 index 0000000000..42febf5b92 --- /dev/null +++ b/Zend/tests/bug65051.phpt @@ -0,0 +1,23 @@ +--TEST-- +Bug #65051: count() off by one inside unset() +--FILE-- +<?php + +class Foo { + public $array; + + public function __destruct() { + var_dump(count($this->array[0])); + var_dump($this->array[0]); + } +} + +$array = [[new Foo]]; +$array[0][0]->array =& $array; +unset($array[0][0]); + +?> +--EXPECT-- +int(0) +array(0) { +} diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 27b603eed2..20896abf3e 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -527,6 +527,7 @@ ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint n if (ht->pInternalPointer == p) { ht->pInternalPointer = p->pListNext; } + ht->nNumOfElements--; if (ht->pDestructor) { ht->pDestructor(p->pData); } @@ -535,7 +536,6 @@ ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint n } pefree(p, ht->persistent); HANDLE_UNBLOCK_INTERRUPTIONS(); - ht->nNumOfElements--; return SUCCESS; } p = p->pNext; @@ -1320,6 +1320,7 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const if (ht->pInternalPointer == p) { ht->pInternalPointer = p->pListNext; } + ht->nNumOfElements--; if (ht->pDestructor) { ht->pDestructor(p->pData); } @@ -1327,7 +1328,6 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const pefree(p->pData, ht->persistent); } pefree(p, ht->persistent); - ht->nNumOfElements--; HANDLE_UNBLOCK_INTERRUPTIONS(); return FAILURE; } @@ -1355,6 +1355,7 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const if (ht->pInternalPointer == q) { ht->pInternalPointer = q->pListNext; } + ht->nNumOfElements--; if (ht->pDestructor) { ht->pDestructor(q->pData); } @@ -1362,7 +1363,6 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const pefree(q->pData, ht->persistent); } pefree(q, ht->persistent); - ht->nNumOfElements--; } if (p->pNext) { |