Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

native: mysqli fetch with ssl delivers no results, hhvm crashes #4272

Closed
matzematthaey opened this issue Nov 18, 2014 · 27 comments
Closed

native: mysqli fetch with ssl delivers no results, hhvm crashes #4272

matzematthaey opened this issue Nov 18, 2014 · 27 comments

Comments

@matzematthaey
Copy link

Ran fine on hhvm 3.3. On 3.4. and 3.5 nightly hhvm crashes completely.

mbrock# hhvm --version
HipHop VM 3.4.0 (rel)
Compiler: tags/HHVM-3.4.0-0-g817b3a07fc4e509ce15635dbc87778e5b3496663
Repo schema: 0e12aaa31fae66b5591f65603de50c9d62caafac
Extension API: 20140829

mbrock# uname -a
Linux mbrock 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

Connection is established via Doctrine2 (latest), MySQLi-Driver, SSL enabled:

$this->_conn = mysqli_init();
$ssl_key = $driverOptions['1010'];
$ssl_cert = $driverOptions['1011'];
$ssl_ca = $driverOptions['1012'];
$this->setDriverOptions($driverOptions);
$this->_conn->ssl_set($ssl_key, $ssl_cert, $ssl_ca, null, null);

Worked just fine prior the update.

How to reproduce:

  1. Use Symfony2 Standard with Doctrine connection.
  2. Establish SSL connection
  3. Trigger a query: $this->entityManager->getConnection()->query('SHOW VARIABLES;')->fetchAll();
  4. Let it crash

The hhvm error.log does not give any information (or at least for what I can see). The dev.log of Symfony2 does:

[2014-11-18 17:05:29] request.CRITICAL: Uncaught PHP Exception ErrorException: "Notice: Array to string conversion in /var/www/project/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php line 224" at /var/www/project/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php line 224 {"exception":"[object](ErrorException: Notice: Array to string conversion in /var/www/project/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php line 224 at /var/www/project/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php:224)"} []

If I can provide more information, please let me know.

@fredemmott
Copy link
Contributor

Possibly related: #4165

@matzematthaey
Copy link
Author

The error occurs when calling native mysqli->fetch()
I am just getting hhvm-dbg running. Will provide you with a crash report shortly.

@paulbiss
Copy link
Contributor

By the way, there haven't been very many commits to the mysqli extension since the 3.3 branch cut if you want to try bisecting.

https://github.com/facebook/hhvm/commits/45032639de8c30fa1e5eb80eb877857aece756f8/hphp/runtime/ext/mysql/ext_mysqli.cpp

@matzematthaey
Copy link
Author

could not find yet any hint. what i have found:

the statement has the correct value for num_rows, num_columns, etc.
the "statement->fetch" crashes the server. as far as I can see, this seems not to be related to doctrine or symfony.

@paulbiss
Copy link
Contributor

Can you run things in gdb and get a backtrace for the segfault?

@matzematthaey
Copy link
Author

yes here you go:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffdf3ff700 (LWP 6207)]
0x00000000020c6e20 in HPHP::Variant::assign(HPHP::Variant const&) ()
(gdb) backtrace
#0  0x00000000020c6e20 in HPHP::Variant::assign(HPHP::Variant const&) ()
#1  0x000000000134fca8 in HPHP::MySQLStmt::fetch() ()
#2  0x000000000133dae7 in HPHP::c_mysqli_stmt_ni_fetch(HPHP::ObjectData*) ()
#3  0x0000000002100799 in void HPHP::Native::callFunc<false, false>(HPHP::Func const*, void*, HPHP::TypedValue*, HPHP::TypedValue&) ()
#4  0x0000000000b60863 in HPHP::TypedValue* HPHP::Native::methodWrapper<false, false>(HPHP::ActRec*) ()
#5  0x0000000000c18816 in void HPHP::ExecutionContext::dispatchImpl<false>() ()
#6  0x00000000020b5b68 in HPHP::ExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Variant const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::ExecutionContext::InvokeFlags) ()
#7  0x00000000020bbbd3 in HPHP::vm_call_user_func(HPHP::Variant const&, HPHP::Variant const&, bool) ()
#8  0x000000000123178e in HPHP::f_call_user_func(int, HPHP::Variant const&, HPHP::Array const&) ()
#9  0x0000000001b53aec in HPHP::fg_call_user_func(HPHP::ActRec*) ()
#10 0x0000000000c18816 in void HPHP::ExecutionContext::dispatchImpl<false>() ()
#11 0x00000000020b5b68 in HPHP::ExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Variant const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::ExecutionContext::InvokeFlags) ()
#12 0x00000000020bbbd3 in HPHP::vm_call_user_func(HPHP::Variant const&, HPHP::Variant const&, bool) ()
#13 0x000000000123178e in HPHP::f_call_user_func(int, HPHP::Variant const&, HPHP::Array const&) ()
#14 0x0000000001b53aec in HPHP::fg_call_user_func(HPHP::ActRec*) ()
#15 0x0000000000c18816 in void HPHP::ExecutionContext::dispatchImpl<false>() ()
#16 0x00000000020b5b68 in HPHP::ExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Variant const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::ExecutionContext::InvokeFlags) ()
#17 0x0000000002112889 in HPHP::ExecutionContext::invokeUnit(HPHP::TypedValue*, HPHP::Unit const*) ()
#18 0x00000000009d1c77 in HPHP::include_impl_invoke(HPHP::String const&, bool, char const*) ()
#19 0x000000000083b170 in HPHP::hphp_invoke(HPHP::ExecutionContext*, std::string const&, bool, HPHP::Array const&, HPHP::VRefParamValue const&, std::string const&, std::string const&, bool&, std::string&, bool, bool, bool) ()
#20 0x0000000000b11835 in HPHP::HttpRequestHandler::executePHPRequest(HPHP::Transport*, HPHP::RequestURI&, HPHP::SourceRootInfo&, bool) ()
#21 0x0000000000b130da in HPHP::HttpRequestHandler::handleRequest(HPHP::Transport*) ()
#22 0x0000000000b1eb01 in HPHP::ServerWorker<std::shared_ptr<HPHP::FastCGIJob>, HPHP::FastCGITransportTraits>::doJob(std::shared_ptr<HPHP::FastCGIJob>) ()
#23 0x0000000000b265b6 in HPHP::JobQueueWorker<std::shared_ptr<HPHP::FastCGIJob>, HPHP::Server*, true, false, HPHP::JobQueueDropVMStack>::start() ()
#24 0x0000000001bf0607 in HPHP::AsyncFuncImpl::ThreadFunc(void*) ()
#25 0x00000000009a6940 in HPHP::start_routine_wrapper(void*) ()
#26 0x00007ffff14d7182 in start_thread (arg=0x7fffdf3ff700) at pthread_create.c:312
#27 0x00007ffff09e3fbd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

@rrh
Copy link
Contributor

rrh commented Nov 18, 2014

I have a similar issue running a self-built debug-build on ubuntu 14 very near HEAD, and then carefully exporting environment variables MYSQL_TEST_*** to make sure that I can connect to a viable mysql data base server, and then the known-good tests. There are issues handling prepared statements.

Two tests fail, each with its own assertion failure. There are corrupt objects about!

test/run --threads 2 'test/zend/good/ext/mysqli/tests/mysqli_stmt_bind_result_bit.php' 'test/zend/good/ext/mysqli/tests/mysqli_stmt_fetch_geom.php'

Running one of these under gdb:

   gdb --args ./hhvm/hhvm -c test/zend/config.ini -vEval.EnableArgsInBacktraces=true -vEval.EnableIntrinsicsExtension=true -vRepo.Local.Mode=-- -vRepo.Central.Path=/home/rrh/dev/hhvm/verify.hhbc -vEval.Jit=true    -vResourceLimit.CoreFileSize=0    --file 'test/zend/good/ext/mysqli/tests/mysqli_stmt_fetch_geom.php' 
gives me a backtrace with these 18 topmost frames:
#0  0x00007ffff02ddbb9 in __GI_raise (sig=sig@entry=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1  0x00007ffff02e0fc8 in __GI_abort () at abort.c:89
#2  0x00007ffff02d6a76 in __assert_fail_base (
    fmt=0x7ffff0428370 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", 
    assertion=assertion@entry=0x4052434 "m_type == KindOfRef", 
    file=file@entry=0x40523d8 "/home/rrh/dev/hhvm/hphp/runtime/base/type-variant.h", line=line@entry=747, 
    function=function@entry=0x4056e40 <HPHP::Variant::getRefData() const::__PRETTY_FUNCTION__> "HPHP::Variant* HPHP::Variant::getRefData() const")
    at assert.c:92
#3  0x00007ffff02d6b22 in __GI___assert_fail (
    assertion=0x4052434 "m_type == KindOfRef", 
    file=0x40523d8 "/home/rrh/dev/hhvm/hphp/runtime/base/type-variant.h", 
    line=747, 
    function=0x4056e40 <HPHP::Variant::getRefData() const::__PRETTY_FUNCTION__> "HPHP::Variant* HPHP::Variant::getRefData() const") at assert.c:101
#4  0x000000000248ce0d in HPHP::Variant::getRefData (this=0x7fffe401cbc0)
    at /home/rrh/dev/hhvm/hphp/runtime/base/type-variant.h:747
#5  0x00000000033824b7 in HPHP::MySQLStmtVariables::update_result (
    this=0x7fffdee30170)
---Type <return> to continue, or q <return> to quit---
    at /home/rrh/dev/hhvm/hphp/runtime/ext/mysql/mysql_common.cpp:883
#6  0x000000000338342f in HPHP::MySQLStmt::fetch (this=0x7fffe401a2e0)
    at /home/rrh/dev/hhvm/hphp/runtime/ext/mysql/mysql_common.cpp:1100
#7  0x0000000003378040 in HPHP::c_mysqli_stmt_ni_fetch (this_=0x7fffe401ac30)
    at /home/rrh/dev/hhvm/hphp/runtime/ext/mysql/ext_mysqli.cpp:563
#8  0x0000000003fcdb2e in HPHP::Native::callFuncInt64Impl (
    f=0x3378012 <HPHP::c_mysqli_stmt_ni_fetch(HPHP::ObjectData*)>, 
    GP=0x7fffffffc270, GP_count=2, SIMD=0x7fffffffc230, SIMD_count=0)
    at /home/rrh/dev/hhvm/hphp/runtime/vm/native-func-caller.h:788
#9  0x0000000003fade07 in HPHP::Native::callFunc<false, false> (
    func=0x7fffdf0d7180, ctx=0x7fffe401ac30, args=0x7fffdedffd30, ret=...)
    at /home/rrh/dev/hhvm/hphp/runtime/vm/native.cpp:175
#10 0x0000000002922606 in HPHP::Native::methodWrapper<false, false> (
    ar=0x7fffdedffd40) at /home/rrh/dev/hhvm/hphp/runtime/vm/native.cpp:418
#11 0x000000000a209907 in ?? ()
#12 0x0000000003f78381 in enterTCHelper ()
    at /home/rrh/dev/hhvm/hphp/runtime/vm/jit/translator-asm-helpers.S:66
#13 0x0000000003f78425 in HPHP::jit::x64::BackEnd::enterTCHelper (
    this=0x7fffe4f7c548, 
    start=0xa2098c9 "H\215\r\371\377\377\377H\211\357H\211\336H\307¼\032", 
    info=...) at /home/rrh/dev/hhvm/hphp/runtime/vm/jit/back-end-x64.cpp:118
---Type <return> to continue, or q <return> to quit---
#14 0x0000000003f77bd1 in HPHP::jit::MCGenerator::enterTC (
    this=0x7fffe7d61000, 
    start=0xa2098c9 "H\215\r\371\377\377\377H\211\357H\211\336H\307¼\032", 
    data=0x0) at /home/rrh/dev/hhvm/hphp/runtime/vm/jit/mc-generator.cpp:1065
#15 0x00000000029274f2 in HPHP::jit::MCGenerator::enterTCAfterPrologue (
    this=0x7fffe7d61000, 
    start=0x6600230 "H\211\357H\211\336蓮\230\375\377\340\017\v\314UH\211\345\377w\b\377\067H\307", <incomplete sequence \306>)
    at /home/rrh/dev/hhvm/hphp/runtime/vm/jit/mc-generator.h:252
#16 0x0000000003f78070 in HPHP::ExecutionContext::enterVMAtFunc (
    this=0x7fffe40064d0, enterFnAr=0x7fffdedfffc0, 
    stk=HPHP::ExecutionContext::Untrimmed)
    at /home/rrh/dev/hhvm/hphp/runtime/vm/bytecode.cpp:1888
#17 0x0000000003f774ea in HPHP::ExecutionContext::enterVM (
    this=0x7fffe40064d0, ar=0x7fffdedfffc0, 
    stk=HPHP::ExecutionContext::Untrimmed, resumable=0x0, exception=0x0)
    at /home/rrh/dev/hhvm/hphp/runtime/vm/bytecode.cpp:1946

@matzematthaey matzematthaey changed the title symfony2, doctrine2: mysqli with ssl delivers no results, hhvm crashes native: mysqli fetch with ssl delivers no results, hhvm crashes Nov 18, 2014
@rrh
Copy link
Contributor

rrh commented Nov 18, 2014

Fred Emmott implies, via IRC, that mysqli/mysql tests are probably not run on a regular basis within FB/travis, because the necessary environment variables (to configure the mysql client's notion of where the server is) are not set in the test harness.

See this PR comment from February 2014:
fdb69ef
apparently the gist of the comment is still true.

@fredemmott
Copy link
Contributor

We have several hundred concurrent test runs - each of which runs the tests in parallel. If we made the tests sequential, that would be way too slow.

The hard part of running the tests is getting an efficient way to run literally thousands of mysql instances that are automatically cleaned up, and locked to a specific test (not just test run). At the moment, there's higher-reward uses of our time.

@WizKid
Copy link
Contributor

WizKid commented Nov 18, 2014

There should be a diff that was done later that set the right params for Travis to run them. And I specifically changed the tests to use different dbs so they could run on the same MySQL instance in parallel. That was the state when I implemented stuff at least.

@paulbiss
Copy link
Contributor

@rrh I'm still looking into things but I'm not seeing either of those tests fail for me on master :/

@rrh
Copy link
Contributor

rrh commented Nov 19, 2014

@paulbiss yikes. another instance of "it fails for me on ubuntu 14.04 systems compiled for debugging, but FB can't reproduce". Will our various corporate legal beagles let us share something as basic as a spun up EC2 instance that contains an instance(s) of the problem?

@ViolentVotan
Copy link

This seems like the same problem I am still having with my piwik crashing hhvm (also using ssl) which I mentioned in my ticket that got closed: #4171

@matzematthaey
Copy link
Author

hi all. latest php debugging gave us a hint, that it also corresponds to array handling. while running doctrine2 with the mysqli driver, and running the query "show variables;" and trying to get the results via "fetchAll" hhvm crashes. Deep-diving into the MysqliStatement.php (bundled with Doctrine2), Line 276 it runs the following code:

public function fetchAll($fetchMode = null)
    {
        $fetchMode = $fetchMode ?: $this->_defaultFetchMode;

        $rows = array();
        if (PDO::FETCH_COLUMN == $fetchMode) {
            while (($row = $this->fetchColumn()) !== false) {
                $rows[] = $row;
            }
        } else {
            while (($row = $this->fetch($fetchMode)) !== null) {
                $rows[] = $row;
            }
        }

        return $rows;
    }

We found, that the query is processed correctly (thats why all your tests pass). While performing this operation

$rows[] = $row

hhvm crashes just after giving the error "Array to String conversion". Outputting $row directly runs fine. Only the array assignment seems to fail.
Do you have made changes, how variables are assigned into an array? (array_push gives the same result).

@paulbiss
Copy link
Contributor

@matthiasbrock: not that I'm aware of. The previous stacktraces all indicate that the crash is occurring with in the call to fetch, is your stacktrace showing something different?

@matzematthaey
Copy link
Author

Not really, it shows the same stacktrace. Having this kind of conversion warning made me curious. What I can think of (have not done a deep dive into the hhvm code):

mysqli_stmt::fetch() returns a bool or null, if the pointer exceeds the row count.
As far as I understand - it does.

I'll try to sum up, what Doctrine2 does at this point:

  1. Query gets prepared
  2. It binds the params to the prepared statement
  3. Fetch column names
  4. Array_fill the row values with nulls
  5. Bind the result to the row values
  6. Array_combine column names and row values as a result row
  7. Assign each result row to the set (as previously mentioned, here it crashes)

This one makes me thinking: Step 5 & 6:

$this->_rowBindedValues = array_fill(0, count($columnNames), NULL);
$refs = array();

// STEP 5
foreach ($this->_rowBindedValues as $key => &$value) {
  $refs[$key] =& $value;
}

// STEP 6
if (!call_user_func_array(array($this->_stmt, 'bind_result'), $refs)) {
  throw new MysqliException(...);
}

Is it possible, that the pointer assignments in step 5 result in different data types and assigning it to another variable causes a hick up?

@fredemmott
Copy link
Contributor

Can you try this with tonight's nightly? One possible issues with that was just fixed - array_fill() would give false if count($columnNames) == 0

@matzematthaey
Copy link
Author

same result. here is the full stack trace:

root@mbrock:~# gdb --args /usr/bin/hhvm --mode server --config /etc/hhvm/php.ini --config /etc/hhvm/server.ini
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/hhvm...done.
(gdb) run
Starting program: /usr/bin/hhvm --mode server --config /etc/hhvm/php.ini --config /etc/hhvm/server.ini
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffe63ff700 (LWP 18307)]
[Thread 0x7fffe63ff700 (LWP 18307) exited]
[New Thread 0x7fffdffff700 (LWP 18308)]
[New Thread 0x7fffe63ff700 (LWP 18309)]
[Thread 0x7fffdffff700 (LWP 18308) exited]
[New Thread 0x7fffdf3ff700 (LWP 18310)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffdf3ff700 (LWP 18310)]
0x0000000002144e60 in HPHP::Variant::assign(HPHP::Variant const&) ()
(gdb) backtrace
#0  0x0000000002144e60 in HPHP::Variant::assign(HPHP::Variant const&) ()
#1  0x0000000001364568 in HPHP::MySQLStmt::fetch() ()
#2  0x00000000013520c7 in HPHP::c_mysqli_stmt_ni_fetch(HPHP::ObjectData*) ()
#3  0x000000000217c001 in void HPHP::Native::callFunc<false, false>(HPHP::Func const*, void*, HPHP::TypedValue*, HPHP::TypedValue&) ()
#4  0x0000000000b7b363 in HPHP::TypedValue* HPHP::Native::methodWrapper<false, false>(HPHP::ActRec*) ()
#5  0x0000000000c356ac in void HPHP::ExecutionContext::dispatchImpl<false>() ()
#6  0x00000000021340d9 in HPHP::ExecutionContext::enterVM(HPHP::ActRec*, HPHP::ExecutionContext::StackArgsState, HPHP::Resumable*, HPHP::ObjectData*) ()
#7  0x000000000212c58c in HPHP::ExecutionContext::invokeFuncFew(HPHP::TypedValue*, HPHP::Func const*, void*, HPHP::StringData*, int, HPHP::TypedValue const*) ()
#8  0x00000000008dbbb5 in HPHP::ObjectData::iterableObject(bool&, bool) ()
#9  0x00000000008757f5 in HPHP::Iter::init(HPHP::TypedValue*) ()
#10 0x0000000000c37441 in void HPHP::ExecutionContext::dispatchImpl<false>() ()
#11 0x00000000021339f0 in HPHP::ExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Variant const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::ExecutionContext::InvokeFlags) ()
#12 0x0000000002139fe3 in HPHP::vm_call_user_func(HPHP::Variant const&, HPHP::Variant const&, bool) ()
#13 0x0000000001393c2e in HPHP::f_call_user_func(HPHP::Variant const&, HPHP::Array const&) ()
#14 0x000000000216d8a9 in void HPHP::Native::callFunc<false, true>(HPHP::Func const*, void*, HPHP::TypedValue*, HPHP::TypedValue&) ()
#15 0x000000000216d4f7 in HPHP::TypedValue* HPHP::Native::functionWrapper<false, true>(HPHP::ActRec*) ()
#16 0x0000000000c356ac in void HPHP::ExecutionContext::dispatchImpl<false>() ()
#17 0x00000000021339f0 in HPHP::ExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Variant const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::ExecutionContext::InvokeFlags) ()
#18 0x0000000002139fe3 in HPHP::vm_call_user_func(HPHP::Variant const&, HPHP::Variant const&, bool) ()
#19 0x0000000001393c2e in HPHP::f_call_user_func(HPHP::Variant const&, HPHP::Array const&) ()
#20 0x000000000216d8a9 in void HPHP::Native::callFunc<false, true>(HPHP::Func const*, void*, HPHP::TypedValue*, HPHP::TypedValue&) ()
#21 0x000000000216d4f7 in HPHP::TypedValue* HPHP::Native::functionWrapper<false, true>(HPHP::ActRec*) ()
#22 0x0000000000c356ac in void HPHP::ExecutionContext::dispatchImpl<false>() ()
#23 0x00000000021339f0 in HPHP::ExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Variant const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::ExecutionContext::InvokeFlags) ()
root@mbrock:~# hhvm --version
HipHop VM 3.5.0-dev~nightly.2014.11.20 (rel)
Compiler: heads/master-0-gdcc8da3e1d3a7f37c4b18f7130c1b45879f53d21
Repo schema: 78479aa1c280799ff4b810f400590365089d3093
Extension API: 20140829

@matzematthaey
Copy link
Author

okay, got an example script for you. run it in browser or cli - same result:
(just to make sure: credits for the base script go to Doctrine project!)

Update: and for completeness:
Runs on HHVM 3.3.1
Crashes on HHVM 3.4.0 and 3.5.x nightly

Update 2:
SSL has NO impact on this. You could connect without it - same result.

<?php

use PDO;

/**
 * @author Kim Hemsø Rasmussen <kimhemsoe@gmail.com>
 */
class MysqliStatement
{
    /**
     * @var array
     */
    protected static $_paramTypeMap = array(
        PDO::PARAM_STR => 's',
        PDO::PARAM_BOOL => 'i',
        PDO::PARAM_NULL => 's',
        PDO::PARAM_INT => 'i',
        PDO::PARAM_LOB => 's' // TODO Support LOB bigger then max package size.
    );

    /**
     * @var \mysqli
     */
    protected $_conn;

    /**
     * @var \mysqli_stmt
     */
    protected $_stmt;

    /**
     * @var null|boolean|array
     */
    protected $_columnNames;

    /**
     * @var null|array
     */
    protected $_rowBindedValues;

    /**
     * @var array
     */
    protected $_bindedValues;

    /**
     * @var string
     */
    protected $types;

    /**
     * Contains ref values for bindValue().
     *
     * @var array
     */
    protected $_values = array();

    /**
     * @var integer
     */
    protected $_defaultFetchMode = PDO::FETCH_BOTH;

    /**
     * @param \mysqli $conn
     * @param string  $prepareString
     *
     * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException
     */
    public function __construct(\mysqli $conn, $prepareString)
    {
        $this->_conn = $conn;
        $this->_stmt = $conn->prepare($prepareString);
        if (false === $this->_stmt) {
            throw new \Exception("no statement");
        }

        $paramCount = $this->_stmt->param_count;
        if (0 < $paramCount) {
            $this->types = str_repeat('s', $paramCount);
            $this->_bindedValues = array_fill(1 , $paramCount, null);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function bindParam($column, &$variable, $type = null, $length = null)
    {
        if (null === $type) {
            $type = 's';
        } else {
            if (isset(self::$_paramTypeMap[$type])) {
                $type = self::$_paramTypeMap[$type];
            } else {
                throw new \Exception("exception");
            }
        }

        $this->_bindedValues[$column] =& $variable;
        $this->types[$column - 1] = $type;

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function bindValue($param, $value, $type = null)
    {
        if (null === $type) {
            $type = 's';
        } else {
            if (isset(self::$_paramTypeMap[$type])) {
                $type = self::$_paramTypeMap[$type];
            } else {
                throw new \Exception("exception");
            }
        }

        $this->_values[$param] = $value;
        $this->_bindedValues[$param] =& $this->_values[$param];
        $this->types[$param - 1] = $type;

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function execute($params = null)
    {
        if (null !== $this->_bindedValues) {
            if (null !== $params) {
                if ( ! $this->_bindValues($params)) {
                    throw new \Exception("exception");
                }
            } else {
                if (!call_user_func_array(array($this->_stmt, 'bind_param'), array($this->types) + $this->_bindedValues)) {
                    throw new \Exception("exception");
                }
            }
        }

        if ( ! $this->_stmt->execute()) {
            throw new \Exception("error");
        }

        if (null === $this->_columnNames) {
            $meta = $this->_stmt->result_metadata();
            if (false !== $meta) {
                // We have a result.
                $this->_stmt->store_result();

                $columnNames = array();
                foreach ($meta->fetch_fields() as $col) {
                    $columnNames[] = $col->name;
                }
                $meta->free();

                $this->_columnNames = $columnNames;
                $this->_rowBindedValues = array_fill(0, count($columnNames), NULL);

                $refs = array();

                foreach ($this->_rowBindedValues as $key => &$value) {
                    $refs[$key] =& $value;
                }

                if (!call_user_func_array(array($this->_stmt, 'bind_result'), $refs)) {
                    throw new \Exception("exception");
                }
            } else {
                $this->_columnNames = false;
            }
        }

        return true;
    }

    /**
     * Binds a array of values to bound parameters.
     *
     * @param array $values
     *
     * @return boolean
     */
    private function _bindValues($values)
    {
        $params = array();
        $types = str_repeat('s', count($values));
        $params[0] = $types;

        foreach ($values as &$v) {
            $params[] =& $v;
        }

        return call_user_func_array(array($this->_stmt, 'bind_param'), $params);
    }

    /**
     * @return boolean|array
     */
    private function _fetch()
    {
        $ret = $this->_stmt->fetch();

        if (true === $ret) {
            $values = array();

            foreach ($this->_rowBindedValues as $v) {
                $values[] = $v;   
            }

            return $values;
        }

        return $ret;
    }

    /**
     * {@inheritdoc}
     */
    public function fetch($fetchMode = null)
    {
        $values = $this->_fetch();
        if (null === $values) {
            return null;
        }

        if (false === $values) {
            throw new \Exception("no values");
        }

        $fetchMode = $fetchMode ?: $this->_defaultFetchMode;

        switch ($fetchMode) {
            case PDO::FETCH_NUM:
                return $values;

            case PDO::FETCH_ASSOC:
                return array_combine($this->_columnNames, $values);

            case PDO::FETCH_BOTH:
                $ret = array_combine($this->_columnNames, $values);
                $ret += $values;
                return $ret;

            default:
                throw new \Exception("");
        }
    }

    /**
     * {@inheritdoc}
     */
    public function fetchAll($fetchMode = null)
    {
        $fetchMode = $fetchMode ?: $this->_defaultFetchMode;

        $rows = array();
        if (PDO::FETCH_COLUMN == $fetchMode) {
            while (($row = $this->fetchColumn()) !== false) {
                $rows[] = $row;
            }
        } else {
            while (($row = $this->fetch($fetchMode)) !== null) {
                $rows[] = $row;
            }
        }

        return $rows;
    }

    /**
     * {@inheritdoc}
     */
    public function rowCount()
    {
        if (false === $this->_columnNames) {
            return $this->_stmt->affected_rows;
        }
        return $this->_stmt->num_rows;
    }
}

$_conn = mysqli_init();
$_conn->ssl_set('client-key.pem', 'client-cert.pem', 'ca-cert.pem', null, null) or die("no ssl");
$_conn->real_connect('192.168.0.3', 'ssl_user', 'ssl_pass', 'ssl_db', 3306, null, MYSQLI_CLIENT_SSL) or die("no connection");
$_conn->set_charset('UTF-8');

$statement = new MysqliStatement($_conn, 'SHOW VARIABLES');
$statement->execute();

// works - 329 in my case
var_dump($statement->rowCount());

// segfaults
var_dump($statement->fetchAll(PDO::FETCH_BOTH));

@matzematthaey
Copy link
Author

is there anything I can help you with?

@h-no
Copy link

h-no commented Dec 12, 2014

I just wanted to add that I also get this error at mysqli_stmt->fetch().
It segfaults immediately when I make the first request to MySQL.
(Not running Doctrine or any 3rd party frameworks in regards to the database layer.)

HHVM 3.4.0 on Ubuntu 12.04 installed from these instructions:
https://github.com/facebook/hhvm/wiki/Prebuilt-Packages-on-Ubuntu-12.04

I'll try to build 3.3.1 and see what happens.

Host: XXXXXX
ProcessID: 7760
ThreadID: 7ffb5ffff700
ThreadPID: 7764
Name: unknown program
Type: Segmentation fault
Runtime: hhvm
Version: tags/HHVM-3.4.0-0-g817b3a07fc4e509ce15635dbc87778e5b3496663
DebuggerCount: 0

ThreadType: Web Request
Server_SERVER_NAME: 10.85.5.101
Server: 10.85.5.101
URL: /user.php?user_action=login

# 0  ?? at hhvm:0
# 1  HPHP::Variant::assign(HPHP::Variant const&) at hhvm:0
# 2  HPHP::MySQLStmt::fetch() at hhvm:0
# 3  ?? at hhvm:0
# 4  void HPHP::Native::callFunc<false, false>(HPHP::Func const*, void*, HPHP::TypedValue*, HPHP::TypedValue&) at hhvm:0
# 5  HPHP::TypedValue* HPHP::Native::methodWrapper<false, false>(HPHP::ActRec*) at hhvm:0
# 6  void HPHP::ExecutionContext::dispatchImpl<false>() at hhvm:0
# 7  HPHP::ExecutionContext::invokeFunc(HPHP::TypedValue*, HPHP::Func const*, HPHP::Variant const&, HPHP::ObjectData*, HPHP::Class*, HPHP::VarEnv*, HPHP::StringData*, HPHP::ExecutionContext::InvokeFlags) at hhvm:0
# 8  HPHP::ExecutionContext::invokeUnit(HPHP::TypedValue*, HPHP::Unit const*) at hhvm:0
# 9  HPHP::include_impl_invoke(HPHP::String const&, bool, char const*) at hhvm:0
# 10 HPHP::hphp_invoke(HPHP::ExecutionContext*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool, HPHP::Array const&, HPHP::VRefParamValue const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, bool, bool, bool) at hhvm:0
# 11 HPHP::HttpRequestHandler::executePHPRequest(HPHP::Transport*, HPHP::RequestURI&, HPHP::SourceRootInfo&, bool) at hhvm:0
# 12 HPHP::HttpRequestHandler::handleRequest(HPHP::Transport*) at hhvm:0
# 13 HPHP::ServerWorker<std::shared_ptr<HPHP::FastCGIJob>, HPHP::FastCGITransportTraits>::doJob(std::shared_ptr<HPHP::FastCGIJob>) at hhvm:0
# 14 HPHP::JobQueueWorker<std::shared_ptr<HPHP::FastCGIJob>, HPHP::Server*, true, false, HPHP::JobQueueDropVMStack>::start() at hhvm:0
# 15 HPHP::AsyncFuncImpl::ThreadFunc(void*) at hhvm:0
# 16 HPHP::start_routine_wrapper(void*) at hhvm:0
# 17 start_thread at /lib/x86_64-linux-gnu/libpthread.so.0:0
# 18 __clone at /lib/x86_64-linux-gnu/libc.so.6:0

PHP Stacktrace:

#0  mysqli_stmt->fetch() called at [/home/XXXXXX/web/functions/database_mysqli.php:146]
    if ($stmt->affected_rows == -1) {
      $meta = $stmt->result_metadata();
      if ($meta === false) {
        return false;
      }
      while ($field = $meta->fetch_field()) {
        $params[] = &$row[$field->name];
      }

      call_user_func_array(array($stmt, 'bind_result'), $params);
      while ($stmt->fetch()) {  // SEGFAULT HERE
        foreach($row as $key => $val) {
          $c[$key] = $val;
        }
        if ($key_index) {
          $results[$c[$key_index]] = $c;
        } else {
          $results[] = $c;
        }
      }
      $num_rows = count($results);
    }

@deeky666
Copy link

This issue broke our testsuite @ Doctrine/DBAL. As previously mentioned $stmt->fetch() returns null where expected to return true. Not sure if that is of any help...

@Majkl578
Copy link
Contributor

I was able t reproduce this on my local ~1 week old build of master.
Crashed with:
hhvm-clion-debug: /build/hhvm/hhvm/hphp/runtime/base/type-variant.h:746: HPHP::Variant* HPHP::Variant::getRefData() const: Assertion `m_type == KindOfRef' failed.

SSL is irrelevant, crashes even without it.

@paulbiss
Copy link
Contributor

@Majkl578 got a stacktrace?

@Majkl578
Copy link
Contributor

@paulbiss: Sure:

#0  0x00007fffede89107 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1  0x00007fffede8a4e8 in __GI_abort () at abort.c:89
#2  0x00007fffede82226 in __assert_fail_base (fmt=0x7fffedfb8948 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=assertion@entry=0x3f8d2eb "m_type == KindOfRef", 
    file=file@entry=0x3f8d298 "/build/hhvm/hhvm/hphp/runtime/base/type-variant.h", line=line@entry=746, 
    function=function@entry=0x3f8e5c0 <HPHP::Variant::getRefData() const::__PRETTY_FUNCTION__> "HPHP::Variant* HPHP::Variant::getRefData() const") at assert.c:92
#3  0x00007fffede822d2 in __GI___assert_fail (assertion=0x3f8d2eb "m_type == KindOfRef", file=0x3f8d298 "/build/hhvm/hhvm/hphp/runtime/base/type-variant.h", line=746, 
    function=0x3f8e5c0 <HPHP::Variant::getRefData() const::__PRETTY_FUNCTION__> "HPHP::Variant* HPHP::Variant::getRefData() const") at assert.c:101
#4  0x0000000002528059 in HPHP::Variant::getRefData (this=0x7fffe2c1b950) at /build/hhvm/hhvm/hphp/runtime/base/type-variant.h:746
#5  0x00000000033087dd in HPHP::MySQLStmtVariables::update_result (this=0x7fffdeb02100) at /build/hhvm/hhvm/hphp/runtime/ext/mysql/mysql_common.cpp:891
#6  0x0000000003309789 in HPHP::MySQLStmt::fetch (this=0x7fffe2c099a0) at /build/hhvm/hhvm/hphp/runtime/ext/mysql/mysql_common.cpp:1108
#7  0x0000000003310cc3 in HPHP::c_mysqli_stmt_ni_fetch (this_=0x7fffe2c06320) at /build/hhvm/hhvm/hphp/runtime/ext/mysql/ext_mysqli.cpp:563
#8  0x0000000003f1ff25 in HPHP::Native::callFuncInt64Impl (f=0x3310c95 <HPHP::c_mysqli_stmt_ni_fetch(HPHP::ObjectData*)>, GP=0x7fffffffc570, GP_count=2, SIMD=0x7fffffffc530, SIMD_count=0)
    at /build/hhvm/hhvm/hphp/runtime/vm/native-func-caller.h:788
#9  0x0000000003f01052 in HPHP::Native::callFunc<false, false> (func=0x7fffe532f2a0, ctx=0x7fffe2c06320, args=0x7fffdeaffdf0, ret=...) at /build/hhvm/hhvm/hphp/runtime/vm/native.cpp:175
#10 0x0000000002a9531b in HPHP::Native::methodWrapper<false, false> (ar=0x7fffdeaffe00) at /build/hhvm/hhvm/hphp/runtime/vm/native.cpp:418
#11 0x000000000a406907 in ?? ()
#12 0x0000000003ece4c1 in enterTCHelper () at /build/hhvm/hhvm/hphp/runtime/vm/jit/translator-asm-helpers.S:66
#13 0x0000000003ece565 in HPHP::jit::x64::BackEnd::enterTCHelper (this=0x7fffe6073f90, start=0xa4068c9 "H\215\r\371\377\377\377H\211\357H\211\336H\307¼\032", info=...) at /build/hhvm/hhvm/hphp/runtime/vm/jit/back-end-x64.cpp:118
#14 0x0000000003ecdcda in HPHP::jit::MCGenerator::enterTC (this=0x7fffe6126400, start=0xa4068c9 "H\215\r\371\377\377\377H\211\357H\211\336H\307¼\032", data=0x0) at /build/hhvm/hhvm/hphp/runtime/vm/jit/mc-generator.cpp:1065
#15 0x000000000295d59a in HPHP::jit::MCGenerator::enterTCAfterPrologue (this=0x7fffe6126400, start=0x6800230 "H\211\357H\211\336\350G\374m\375\377\340\017\v\314UH\211\345\377w\b\377\067H\307", <incomplete sequence \306>)
    at /build/hhvm/hhvm/hphp/runtime/vm/jit/mc-generator.h:257
#16 0x0000000003ece195 in HPHP::ExecutionContext::enterVMAtFunc (this=0x7fffe2c064b0, enterFnAr=0x7fffdeafffc0, stk=HPHP::ExecutionContext::StackArgsState::Untrimmed) at /build/hhvm/hhvm/hphp/runtime/vm/bytecode.cpp:1898
#17 0x0000000003ecd5f0 in HPHP::ExecutionContext::enterVM (this=0x7fffe2c064b0, ar=0x7fffdeafffc0, stk=HPHP::ExecutionContext::StackArgsState::Untrimmed, resumable=0x0, exception=0x0) at /build/hhvm/hhvm/hphp/runtime/vm/bytecode.cpp:1956
#18 0x0000000003ecd0d4 in HPHP::ExecutionContext::invokeFunc (this=0x7fffe2c064b0, retptr=0x7fffffffd160, f=0x7fffe53f1de0, args_=..., this_=0x0, cls=0x0, varEnv=0x7fffe2c11240, invName=0x0, 
    flags=HPHP::ExecutionContext::InvokePseudoMain) at /build/hhvm/hhvm/hphp/runtime/vm/bytecode.cpp:2123
#19 0x0000000003f0d798 in HPHP::ExecutionContext::invokeUnit (this=0x7fffe2c064b0, retval=0x7fffffffd160, unit=0x7fffe53e33c0) at /build/hhvm/hhvm/hphp/runtime/vm/bytecode.cpp:2317
#20 0x00000000024dfca3 in HPHP::invoke_file_impl (res=..., path=..., once=true, currentDir=0x3fddbec "") at /build/hhvm/hhvm/hphp/runtime/base/builtin-functions.cpp:690
#21 0x0000000003f0d6c0 in HPHP::invoke_file (s=..., once=true, currentDir=0x3fddbec "") at /build/hhvm/hhvm/hphp/runtime/base/builtin-functions.cpp:703
#22 0x00000000024dfd6f in HPHP::include_impl_invoke (file=..., once=true, currentDir=0x3fddbec "") at /build/hhvm/hhvm/hphp/runtime/base/builtin-functions.cpp:714
#23 0x00000000025c4935 in HPHP::hphp_invoke (context=0x7fffe2c064b0, cmd="/tmp/test.php", func=false, funcParams=..., funcRet=..., reqInitFunc="", reqInitDoc="", error=@0x7fffffffd3cf: false, errorMsg="", once=true, warmupOnly=false, 
    richErrorMsg=false) at /build/hhvm/hhvm/hphp/runtime/base/program-functions.cpp:1871
#24 0x00000000025c4501 in HPHP::hphp_invoke_simple (filename="/tmp/test.php", warmupOnly=false) at /build/hhvm/hhvm/hphp/runtime/base/program-functions.cpp:1832
#25 0x00000000025c2662 in HPHP::execute_program_impl (argc=2, argv=0x7fffffffe308) at /build/hhvm/hhvm/hphp/runtime/base/program-functions.cpp:1560
#26 0x00000000025bf496 in HPHP::execute_program (argc=2, argv=0x7fffffffe308) at /build/hhvm/hhvm/hphp/runtime/base/program-functions.cpp:965
#27 0x00000000024857b2 in main (argc=2, argv=0x7fffffffe308) at /build/hhvm/hhvm/hphp/hhvm/main.cpp:62

Based on code provided by @matthiasbrock, but with SSL disabled.

Build based on 6b7e3a9.

@wjywbs
Copy link
Contributor

wjywbs commented Jan 6, 2015

This is related to #4505. Please see part 3 in #4505 (comment).

@matzematthaey
Copy link
Author

This issue seems to be fixed in v3.7.0. Works like charme. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants