Details
-
Bug
-
Status: Closed
-
Critical
-
Resolution: Fixed
-
0.9
-
CentOS 6.4 64 bit
Description
Unfortunately, this is a bit of a Heisenbug among a complex code base and I
wasn't able to make a small reproducible example that actually crashes.
However, I might have been able to make a small example that shows possible
memory corruption that leads to the crash.
The crash happens as my script terminates, so it does appear to be memory
corruption. Here is the gdb stack trace:
(gdb) bt #0 0x00007f6e7dad4ad9 in gc_zval_possible_root () from /etc/httpd/modules/libphp5.so #1 0x00007f6e7dac36db in zend_hash_destroy () from /etc/httpd/modules/libphp5.so #2 0x00007f6e7dab6a6f in _zval_dtor_func () from /etc/httpd/modules/libphp5.so #3 0x00007f6e7daab0da in _zval_ptr_dtor () from /etc/httpd/modules/libphp5.so #4 0x00007f6e7dac36db in zend_hash_destroy () from /etc/httpd/modules/libphp5.so #5 0x00007f6e7dad60c9 in zend_object_std_dtor () from /etc/httpd/modules/libphp5.so #6 0x00007f6e7dad60e9 in zend_objects_free_object_storage () from /etc/httpd/modules/libphp5.so #7 0x00007f6e7dad95bc in zend_objects_store_free_object_storage () from /etc/httpd/modules/libphp5.so #8 0x00007f6e7daab4e4 in ?? () from /etc/httpd/modules/libphp5.so #9 0x00007f6e7dab7792 in ?? () from /etc/httpd/modules/libphp5.so #10 0x00007f6e7da658e5 in php_request_shutdown () from /etc/httpd/modules/libphp5.so #11 0x00007f6e7db3fbd7 in ?? () from /etc/httpd/modules/libphp5.so #12 0x00007f6e88f9abb0 in ap_run_handler () #13 0x00007f6e88f9e46e in ap_invoke_handler () #14 0x00007f6e88fa9b30 in ap_process_request () #15 0x00007f6e88fa69a8 in ?? () #16 0x00007f6e88fa26b8 in ap_run_process_connection () #17 0x00007f6e88fae977 in ?? () #18 0x00007f6e88faec8a in ?? () #19 0x00007f6e88faefbb in ap_mpm_run () #20 0x00007f6e88f86900 in main ()
One thing I noticed in tracking this down was that a php data structure
that I sent to the thrift call came back modified, which I don't think is
normal behavior. This is the behavior I was able to reproduce. This example
is loosely based on the tutorial.
Thrift File (smallbug.thrift):
namespace php tutorial struct Date_Range { 1: string low_date, 2: string high_date } service Calculator { Date_Range List_Range( 1:string session_id, 2:map<string,string> options), }
Client File client.php:
#!/usr/bin/env php <?php error_reporting(E_ALL); define('THRIFT_ROOT', '/home/tim/projects/inquisic/thrift-0.9.0/lib/php/lib'); require_once(THRIFT_ROOT . '/Thrift/ClassLoader/ThriftClassLoader.php'); use Thrift\ClassLoader\ThriftClassLoader; $GEN_DIR = '/home/tim/public_html/smallbug/gen-php'; $loader = new ThriftClassLoader(); $loader->registerNamespace('Thrift', THRIFT_ROOT); $loader->registerDefinition('shared', $GEN_DIR); $loader->registerDefinition('tutorial', $GEN_DIR); $loader->register(); use Thrift\Protocol\TBinaryProtocol; use Thrift\Protocol\TBinaryProtocolAccelerated; use Thrift\Transport\TSocket; use Thrift\Transport\THttpClient; use Thrift\Transport\TBufferedTransport; use Thrift\Exception\TException; try { $socket = new THttpClient('localhost', 8383, '/~tim/smallbug/server.php'); $transport = new TBufferedTransport($socket, 1024, 1024); $protocol = new TBinaryProtocolAccelerated($transport); $client = new \tutorial\CalculatorClient($protocol); $transport->open(); $options = array( 'type' => 'C', 'all' => 1, 'client' => 1 ); print "before: options = " . print_r($options, true); $client->List_Range('session_id', $options); print "afterward: options = " . print_r($options, true); $transport->close(); } catch (TException $tx) { print 'TException: '.$tx->getMessage()."\n"; } ?>
Server File server.php:
<?php namespace tutorial\php; error_reporting(E_ALL); define('THRIFT_ROOT', '/home/tim/projects/inquisic/thrift-0.9.0/lib/php/lib'); require_once(THRIFT_ROOT . '/Thrift/ClassLoader/ThriftClassLoader.php'); use Thrift\ClassLoader\ThriftClassLoader; $GEN_DIR = '/home/tim/public_html/smallbug/gen-php'; $loader = new ThriftClassLoader(); $loader->registerNamespace('Thrift', THRIFT_ROOT); $loader->registerDefinition('tutorial', $GEN_DIR); $loader->register(); if (php_sapi_name() == 'cli') { ini_set("display_errors", "stderr"); } use Thrift\Protocol\TBinaryProtocol; use Thrift\Protocol\TBinaryProtocolAccelerated; use Thrift\Transport\TPhpStream; use Thrift\Transport\TBufferedTransport; class CalculatorHandler implements \tutorial\CalculatorIf { public function List_Range($session_id, $options) { error_log("List_Range()"); $ret = new \tutorial\Date_Range; return $ret; } }; header('Content-Type', 'application/x-thrift'); if (php_sapi_name() == 'cli') { echo "\r\n"; } $handler = new CalculatorHandler(); $processor = new \tutorial\CalculatorProcessor($handler); $transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W)); $protocol = new TBinaryProtocolAccelerated($transport, true, true); error_log("protocol = " . get_class($protocol)); $transport->open(); $processor->process($protocol, $protocol); $transport->close();
Output after running the test program:
before: options = Array ( [type] => C [all] => 1 [client] => 1 ) afterward: options = Array ( [type] => C [all] => [client] => )
As you can see, the $options array was corrupted on return. This does not
happen when using the non-accelerated class.
WARNING! If you run this example, please make sure the accelerated protocol
is really being used! Take a look at my other bug report; the normal PHP
Thrift library has a bug where the accelerated thrift_protocol module will
not be used. It's bug THRIFT-1903.
Thanks for your help.