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