Edit report at https://bugs.php.net/bug.php?id=64722&edit=1
ID: 64722 User updated by: tj dot botha at plista dot com Reported by: tj dot botha at plista dot com Summary: PDO extension causes zend_mm_heap corrupted -Status: Assigned +Status: Closed Type: Bug Package: PDO related Operating System: Ubuntu Server 12.10 PHP Version: master-Git-2013-04-26 (Git) Block user comment: N Private report: N New Comment: Hey guys - thanks so much for fixing this - I checked out git commit 1143f58a7094ed9a4960bfb714fa2773dda7c704, built it and can confirm its no longer segfaulting. cool thanks :) Previous Comments: ------------------------------------------------------------------------ [2013-06-14 10:35:34] tj dot botha at plista dot com Ok - so I managed to create a script which breaks it: namespace SomeName\db; class PDO extends \PDO { public $name; public function setName($name) { $this->name = $name; } } class DatabaseManager { private static $connections; private static $options = array( PDO::ATTR_PERSISTENT => 1 ); public static function createConnection($name) { $pdo = null; $pdo = new PDO("mysql:host=localhost;port=3306;dbname=db_youfilter", "root", "lmnop", self::$options); self::$connections[$name] = $pdo; } } DatabaseManager::createConnection("foo"); DatabaseManager::createConnection("bar"); Turns out this is a similar bug to https://bugs.php.net/bug.php?id=63176 And this issue is still open. We will probably work around this problem by not extending PDO. ------------------------------------------------------------------------ [2013-06-12 08:57:49] tj dot botha at plista dot com This issue can be closed - There is a problem between the combination of ( or rather lack of using imagick ), pdo, and the error handling, and possibly the logging system, unique to the site and not 100% reproducible using a reduced test script (and I also do not want spend a whole day writing one) using imagick 3 RC 2 with pdo enabled works for now. If you want to reproduce the behavior, attempt to imagine the circumstances in which zend_object_std_dtor gets called twice for the same object. ------------------------------------------------------------------------ [2013-05-02 15:08:42] tj dot botha at plista dot com Even more correct: ZEND_API void zend_object_std_dtor(zend_object *object TSRMLS_DC) { if (object->guards) { zend_hash_destroy(object->guards); FREE_HASHTABLE(object->guards); object->guards = NULL; } if (object->properties) { zend_hash_destroy(object->properties); FREE_HASHTABLE(object->properties); object->properties = NULL; } if (object->properties_table) { int i; for (i = 0; i < object->ce->default_properties_count; i++) { if (object->properties_table[i]) { zval_ptr_dtor(&object->properties_table[i]); object->properties_table[i] = NULL; } } efree(object->properties_table); object->properties_table = NULL; } } ------------------------------------------------------------------------ [2013-05-02 14:42:26] tj dot botha at plista dot com In my mind, the correct function should look like this (without knowing the exact context in which it runs) //source_code/Zend/zend_objects.c: ZEND_API void zend_object_std_dtor(zend_object *object TSRMLS_DC) { if (object->guards) { zend_hash_destroy(object->guards); FREE_HASHTABLE(object->guards); } if (object->properties) { zend_hash_destroy(object->properties); FREE_HASHTABLE(object->properties); } if (object->properties_table) { int i; for (i = 0; i < object->ce->default_properties_count; i++) { if (object->properties_table[i]) { zval_ptr_dtor(&object->properties_table[i]); object->properties_table[i] = NULL; } } efree(object->properties_table); object->properties_table = NULL; } } However, this appears in my apache logs, only when running in debug mode: [Thu May 02 16:41:28.476657 2013] [auth_digest:notice] [pid 23396] AH01757: generating secret for digest authentication ... [Thu May 02 16:41:29.052276 2013] [ssl:warn] [pid 23396] AH01873: Init: Session Cache is not configured [hint: SSLSessionCache] [Thu May 02 16:41:29.052747 2013] [lbmethod_heartbeat:notice] [pid 23396] AH02282: No slotmem from mod_heartmonitor [Thu May 02 16:41:29.141345 2013] [core:warn] [pid 23396] AH00098: pid file /usr/local/apache2/logs/httpd.pid overwritten -- Unclean shutdown of previous Apache run? [Thu May 2 16:41:35 2013] Script: '/.../index.php' /home/tj/php-5.4.14/Zend/zend_API.c(1127) : Freeing 0x7FFFE89B6050 (16 bytes), script=/usr/local/apache2/htdocs/www/plista/www.2.3.2/app/webroot/index.php === Total 1 memory leaks detected === But the project loads without problems. ------------------------------------------------------------------------ [2013-05-02 13:51:47] tj dot botha at plista dot com Hey guys - I have run various tests - using valgrind I did not find anything. I have debugged the code, and I have narrowed down the problem. It is not a race condition, the problem is dangling pointers are getting freed: below is still okay: //php_source/ext/pdo/pdo_dbh.c static void pdo_dbh_free_storage(pdo_dbh_t *dbh TSRMLS_DC) { ... zend_object_std_dtor(&dbh->std TSRMLS_CC); dbh->std.properties = NULL; ... } The problem arrives here in zend_object_std_dtor, in php_source/Zend/zend_objects.c The situation can be recreated when a zend_object has properties AND a properties_table Look at the code below: In my case, the below function gets called TWICE: ZEND_API void zend_object_std_dtor(zend_object *object TSRMLS_DC) { if (object->guards) { zend_hash_destroy(object->guards); FREE_HASHTABLE(object->guards); } if (object->properties) { zend_hash_destroy(object->properties); FREE_HASHTABLE(object->properties); if (object->properties_table) { efree(object->properties_table); } } else if (object->properties_table) { int i; for (i = 0; i < object->ce->default_properties_count; i++) { if (object->properties_table[i]) { zval_ptr_dtor(&object->properties_table[i]); } } efree(object->properties_table); } } The important bits are: ... if (object->properties) { ... if (object->properties_table) { efree(object_properties_table); //this creates dangling pointers 0x5a5a5a5a5a5a5a5a //Now, object->properties_table[0, 1, 2, ... n] are all dangling pointers } ... } else if (object->properties_table) { ... //now the second time this function gets called, at some point during the zval_ptr_dtor call on object- >properties_table[i], a function tries to decrease the ref val on the dangling >pointer, and this causes SIGSEGV 11 (zend_mm_heap corrupted) ... } I do not know what the correct behavior should be. I have tried removing the "else if" and then setting the properties_table to NULL after de-allocation and forcing de- allocation of both, however different modifications to the code have lead to different problems and I do not seem to find a 100% correct solution. The solution I had in mind to work perfect lead to for example: /home/tj/php-5.4.14/Zend/zend_API.c(1127) : Freeing 0x7FFFE89B6418 (16 bytes), script=/.../index.php === Total 1 memory leaks detected === I do not know if the function should indeed be called twice, or whether the object should only contain properties or properties_table and not both, and why the properties_table does not get set to NULL after being de-allocated, as well as the other properties_table[] pointers. In either way - it turns out that this situation is created because we are extending the PDO class in PHP, and adding custom properties and methods. I will still investigate further - but I will really appreciate some advice / help! ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at https://bugs.php.net/bug.php?id=64722 -- Edit this bug report at https://bugs.php.net/bug.php?id=64722&edit=1