Commit:    86434be9462c5b14dccc588afe6bc1b4a1433360
Author:    Nikita Popov <ni...@php.net>         Mon, 17 Jun 2013 23:44:13 +0200
Parents:   d587efb7ee6df0d68bb91c3680194089c7d6cb62
Branches:  PHP-5.5 master

Link:       
http://git.php.net/?p=php-src.git;a=commitdiff;h=86434be9462c5b14dccc588afe6bc1b4a1433360

Log:
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.

Bugs:
https://bugs.php.net/65051

Changed paths:
  M  NEWS
  A  Zend/tests/bug65051.phpt
  M  Zend/zend_hash.c


Diff:
diff --git a/NEWS b/NEWS
index 47c3e34..ffb73a5 100644
--- a/NEWS
+++ b/NEWS
@@ -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 0000000..42febf5
--- /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 27b603e..20896ab 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) {


--
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to