Uploaded image for project: 'Thrift'
  1. Thrift
  2. THRIFT-1908

Using php thrift_protocol accelerated transfer causes core dump

VotersWatch issueWatchersLinkCloneUpdate Comment AuthorReplace String in CommentUpdate Comment VisibilityDelete Comments
    XMLWordPrintableJSON

Details

    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.

      Attachments

        Activity

          This comment will be Viewable by All Users Viewable by All Users
          Cancel

          People

            thobbs Tom Hobbs
            siliconengine Tim Behrendsen
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Slack

                Issue deployment