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:             Closed
+Status:             Assigned
 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:

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.


Previous Comments:
------------------------------------------------------------------------
[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!

------------------------------------------------------------------------
[2013-05-01 13:05:43] larue...@php.net

if you can reproduce it in a 100% chance, please run it with valgrind, let's 
see 
what valgrind says about this..

thanks

------------------------------------------------------------------------


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

Reply via email to