Bug #64722 [Asn->Csd]: PDO extension causes zend_mm_heap corrupted

From: Date: Mon, 17 Jun 2013 08:17:32 +0000
Subject: Bug #64722 [Asn->Csd]: PDO extension causes zend_mm_heap corrupted
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-179592@lists.php.net to get a copy of this message
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


Thread (16 messages)

« previous php.bugs (#179592) next »