Hhvm: Argument unpacking fails to pass values by reference

Created on 11 Sep 2015  路  13Comments  路  Source: facebook/hhvm

When I run the below code, I get the following warnings:

Warning: Parameter 2 to bind_param() expected to be a reference, value given in /in/mZUPI on line 8
Warning: Parameter 3 to bind_param() expected to be a reference, value given in /in/mZUPI on line 8

See https://3v4l.org/mZUPI. The code works perfectly in PHP 5.6 and PHP 7.

<?php

$params = ["first", "second"];
query("sql query", $params);

function query($sql, array $params = []) {
    // ... other code
    bind_param('ss', ...$params);
}

function bind_param($types, &$param1, &$param2) {
    echo 'Types: ' . $types . PHP_EOL;
    echo 'Param 1: ' . $param1 . PHP_EOL;
    echo 'Param 2: ' . $param2 . PHP_EOL;
}
php5 incompatibility

Most helpful comment

Here is a portion of black magic that works in HHVM (MySQLi):

$tmp = [];
foreach (array_keys($result) as $key) {
    $tmp[$key] = &$result[$key];
}
$stmt->bind_result(...$result);

Under PHP can be simplified to:

$stmt->bind_result(...$result);

Tricky workaround that at the first glance doesn't make any sense, but it works 馃槅

Maybe you can use this hack as an inspiration for proper fix in HHVM implementation.

All 13 comments

No one has had a chance to work on variadic support for references (c.f. https://github.com/facebook/hhvm/issues/3888)

Other example: https://3v4l.org/5EBnU

<?php

$a = 123;
$b = 0;
$c = [ $a, &$b ];

function a($a, &$b)
{
    $b = $a;
}

a(...$c);

var_dump($b === $a);

With the error message: Uncaught exception: std::bad_alloc.

@olvlvl The Uncaught exception: std::bad_alloc is actually an unrelated error. hhvm-3.14.4 specifically has been failing on 3v4l recently.

Oh. Good to know.

@paulbiss Is this related to issue #6954 ?

@mofarrell 3v4l is forcing the hhvm php7 mode which is why some things are failing on 3v4l see this item https://3v4l.uservoice.com/forums/219058-general/suggestions/16257685-hhvm-in-php-5-mode

Here is a portion of black magic that works in HHVM (MySQLi):

$tmp = [];
foreach (array_keys($result) as $key) {
    $tmp[$key] = &$result[$key];
}
$stmt->bind_result(...$result);

Under PHP can be simplified to:

$stmt->bind_result(...$result);

Tricky workaround that at the first glance doesn't make any sense, but it works 馃槅

Maybe you can use this hack as an inspiration for proper fix in HHVM implementation.

@nazar-pc That "black magic" actually makes sense. That works because its boxing the members in the $result array. Once they are boxed there is no problem passing them by reference. Most of the complications here occur with the way unpacking is implemented in HHVM, and how we have fixed size stack frames (both making it hard to box the members at the call site). Boxing is also observable from the PHP code, so additionally we need to match php's behavior as closely as possible.

@photodude Looks like that thread has some valid thoughts about running in both php7 and non php7 mode

@mofarrell I'm glad you like my thoughts on testing with both php7 and non php7 mode. I hope to resume that style of testing once issue #7198 is resolved.

I think i have some similar bug.
Application: Typo3 v7.6.18

Result: crash of hhvm
Expected Result: Working Typo 3
Server: Debian

hhvm --version
HipHop VM 3.19.2 (rel)
Compiler: tags/HHVM-3.19.2-0-g8e7759b4b00535d65969dec6a56a03b4ed238b35
Repo schema: a02544d8eb49139aa02e1bc91bbdb6416e02af2c

# cat /tmp/stacktrace.7874.log 
Host: ip-172-31-6-75
ProcessID: 7875
ThreadID: 140652142655232
ThreadPID: 7895
Name: unknown program
Type: Segmentation fault
Runtime: hhvm
Version: tags/HHVM-3.19.2-0-g8e7759b4b00535d65969dec6a56a03b4ed238b35
DebuggerCount: 0

Server: ec2-52-58-234-171.eu-central-1.compute.amazonaws.com
ThreadType: Web Request
Server_SERVER_NAME: ec2-52-58-234-171.eu-central-1.compute.amazonaws.com
URL: /

# 0  000000000350f366
# 1  00000000011d9fa3
# 2  00007fec3b067890
# 3  00007fec3ce32e3f
# 4  00000000011899b1
# 5  000000000346af20
# 6  00000000023d0c3f
# 7  00000000023e15c6
# 8  00000000023e1857
# 9  000000000355e846
# 10 000000000125f784
# 11 0000000003474d2e
# 12 00000000026c5514
# 13 00000000016f1189
# 14 00000000016fef3a
# 15 00000000016ff2aa
# 16 000000000355e846
# 17 000000000125f784
# 18 00000000012604a5
# 19 00000000035182b1
# 20 00000000035185d1
# 21 0000000003516484
# 22 00000000015ba247
# 23 0000000003508b27
# 24 000000000168eb9f
# 25 000000000168f77f
# 26 0000000000e186b7
# 27 00000000011f4367
# 28 00007fec3b060064
# 29 00007fec3531762d

PHP Stacktrace:

#0  mysqli_stmt->bind_result()
#1  call_user_func_array() called at [/var/www/typo3_src-7.6.18/typo3/sysext/core/Classes/Database/PreparedStatement.php:424]
#2  TYPO3\CMS\Core\Database\PreparedStatement->fetch() called at [/var/www/typo3_src-7.6.18/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php:909]
#3  TYPO3\CMS\Core\Authentication\AbstractUserAuthentication->fetchUserSession() called at [/var/www/typo3_src-7.6.18/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php:594]
#4  TYPO3\CMS\Core\Authentication\AbstractUserAuthentication->checkAuthentication() called at [/var/www/typo3_src-7.6.18/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php:423]
#5  TYPO3\CMS\Core\Authentication\AbstractUserAuthentication->start() called at [/var/www/typo3_src-7.6.18/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php:1239]
#6  TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->initializeBackendUser() called at [/var/www/typo3_src-7.6.18/typo3/sysext/frontend/Classes/Http/RequestHandler.php:123]
#7  TYPO3\CMS\Frontend\Http\RequestHandler->handleRequest() called at [/var/www/typo3_src-7.6.18/typo3/sysext/core/Classes/Core/Bootstrap.php:302]
#8  TYPO3\CMS\Core\Core\Bootstrap->handleRequest() called at [/var/www/typo3_src-7.6.18/typo3/sysext/frontend/Classes/Http/Application.php:78]
#9  TYPO3\CMS\Frontend\Http\Application->run() called at [/var/www/typo3_src-7.6.18/index.php:33]
#10 Closure$() called at [/var/www/typo3_src-7.6.18/index.php:34]

Typo3 Function at Stacktrace id 1

/**
     * Fetches a row from a result set associated with a \TYPO3\CMS\Core\Database\PreparedStatement object.
     *
     * @param int $fetch_style Controls how the next row will be returned to the caller. This value must be one of the \TYPO3\CMS\Core\Database\PreparedStatement::FETCH_* constants. If omitted, default fetch mode for this prepared query will be used.
     * @return array Array of rows or FALSE if there are no more rows.
     * @api
     */
    public function fetch($fetch_style = 0)
    {
        if ($fetch_style == 0) {
            $fetch_style = $this->defaultFetchMode;
        }

        if ($this->statement instanceof \mysqli_stmt) {
            if ($this->buffer === null) {
                $variables = [];
                $this->buffer = [];
                foreach ($this->fields as $field) {
                    $this->buffer[$field] = null;
                    $variables[] = &$this->buffer[$field];
                }

                call_user_func_array([$this->statement, 'bind_result'], $variables); // <-- line 424
            }
            $success = $this->statement->fetch();
            $columns = $this->buffer;
        } else {
            $columns = $this->statement->fetch();
            $success = is_array($columns);
        }

        if ($success) {
            $row = [];
            foreach ($columns as $key => $value) {
                switch ($fetch_style) {
                    case self::FETCH_ASSOC:
                        $row[$key] = $value;
                        break;
                    case self::FETCH_NUM:
                        $row[] = $value;
                        break;
                    default:
                        throw new \InvalidArgumentException('$fetch_style must be either TYPO3\\CMS\\Core\\Database\\PreparedStatement::FETCH_ASSOC or TYPO3\\CMS\\Core\\Database\\PreparedStatement::FETCH_NUM', 1281646455);
                }
            }
        } else {
            $row = false;
        }

        return $row;
    }

@Xarno I can't replicate the issue without the mysql calls (https://3v4l.org/MQHMBK). It looks like there may be an issue in the mysqli code. Reproducing the problem using our debug build would be helpful. Identifying a small repro case would help as well. If you get this information open another issue, and hopefully we can help there.

I am going over old issues on this repository, to see which ones apply to the current versions of hhvm.

Hhvm removed &$references from the runtime and Hack nolonger supports them.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

simonwelsh picture simonwelsh  路  6Comments

caioiglesias picture caioiglesias  路  6Comments

ZhijieWang picture ZhijieWang  路  6Comments

jacobconley picture jacobconley  路  4Comments

Sakretsos picture Sakretsos  路  7Comments