php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80255 Opcache bug (bad condition result) in 8.0.0rc1
Submitted: 2020-10-18 19:23 UTC Modified: 2020-10-19 12:42 UTC
Votes:5
Avg. Score:4.2 ± 1.0
Reproduced:2 of 3 (66.7%)
Same Version:2 (100.0%)
Same OS:1 (50.0%)
From: rixafy at gmail dot com Assigned:
Status: Closed Package: opcache
PHP Version: 8.0.0RC2 OS: Ubuntu 20.04
Private report: No CVE-ID: None
 [2020-10-18 19:23 UTC] rixafy at gmail dot com
Description:
------------
I didn't manage to isolate the bug from that package, I provided also html stacktrace in repository and description in readme.

It seems like some condition is behaving differently when opcache is enabled, condition passed, but there is no way this condition would even be executed, because condition above (same condition - strlen($newIndent) > strlen($indent)) cannot pass.

Test script:
---------------
https://github.com/Rixafy/php8.0.0-rc1-opcache-bug


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-10-19 09:01 UTC] nikic@php.net
-Status: Open +Status: Verified
 [2020-10-19 09:01 UTC] nikic@php.net
Confirming that this reproduces on current PHP-8.0 HEAD.
 [2020-10-19 12:37 UTC] tekiela246 at gmail dot com
To ease with the debugging I have reduced the example to the following:
=================
<?php

echo '<pre>';
$tokens = [
    5 => ':',
    6 => 'bar',
    7 => ' ',
];

for ($pos = 5; $pos < 8; $pos++) {
    $t = $tokens[$pos];
    
    if ($t === ':') { // KeyValuePair separator
        echo 'Sep: ' . $pos . PHP_EOL;
        $hasKey = true;
    } elseif ($t[0] === " ") { // Indent
        echo 'Indent: ' . $pos . PHP_EOL;

        if ($hasValue && !$hasKey) {
        } elseif ($hasKey) {
            $hasKey = $hasValue = false;
        }
    } else { // Value
        echo 'Val: ' . $pos . PHP_EOL;
        $hasValue = true;
    }
}

echo 'Return: ' . $pos . PHP_EOL;
=====================
Expected:
Sep: 5
Val: 6
Indent: 7
Return: 8
====================
With OPCache enabled this will go into infinite loop. 
I have also made a slightly different test case https://pastebin.com/sCbzpQ5D 
This one repeats loop no. 7 twice
 [2020-10-19 12:39 UTC] nikic@php.net
I have arrived at a similar reduction:

<?php
function test($a, $b, $c) {
    do {
        if ($a && !$b) {
            break;
        } else if ($b) {
            echo "foo\n";
        }
        echo "bar\n";
    } while ($c);
    echo "baz\n";
}
test(true, true, false);
 [2020-10-19 12:42 UTC] nikic@php.net
Seems to be introduced by the block pass:

test:
     ; (lines=15, args=3, vars=3, tmps=2)
     ; (before block pass)
     ; /home/nikic/php/php-8.0/t007.php:3-13
     ; return  [] RANGE[0..0]
BB0:
     ; start lines=[0-2]
     ; to=(BB1)
0000 CV0($a) = RECV 1
0001 CV1($b) = RECV 2
0002 CV2($c) = RECV 3

BB1:
     ; follow target lines=[3-3]
     ; to=(BB5, BB2)
0003 T3 = JMPZ_EX CV0($a) BB5

BB2:
     ; follow lines=[4-6]
     ; to=(BB5, BB8)
0004 T4 = BOOL_NOT CV1($b)
0005 T3 = BOOL T4
0006 JMPZNZ T3 BB5 BB8

BB3:
     ; unreachable lines=[7-7]
     ; to=(BB8)
0007 JMP BB8

BB4:
     ; unreachable lines=[8-8]
     ; to=(BB7)
0008 JMP BB7

BB5:
     ; target lines=[9-9]
     ; to=(BB7, BB6)
0009 JMPZ CV1($b) BB7

BB6:
     ; follow lines=[10-10]
     ; to=(BB7)
0010 ECHO string("foo
")

BB7:
     ; follow target lines=[11-12]
     ; to=(BB1, BB8)
0011 ECHO string("bar
")
0012 JMPNZ CV2($c) BB1

BB8:
     ; follow target exit lines=[13-14]
0013 ECHO string("baz
")
0014 RETURN null

test:
     ; (lines=11, args=3, vars=3, tmps=2)
     ; (after block pass)
     ; /home/nikic/php/php-8.0/t007.php:3-13
     ; return  [] RANGE[0..0]
BB0:
     ; start target lines=[0-2]
     ; to=(BB1)
0000 CV0($a) = RECV 1
0001 CV1($b) = RECV 2
0002 CV2($c) = RECV 3

BB1:
     ; follow target lines=[3-3]
     ; to=(BB5, BB2)
0003 JMPZ CV0($a) BB5

BB2:
     ; follow lines=[4-4]
     ; to=(BB8, BB0)
0004 JMPZNZ CV1($b) BB8 BB0

BB5:
     ; target lines=[5-5]
     ; to=(BB7, BB6)
0005 JMPZ CV1($b) BB7

BB6:
     ; follow lines=[6-6]
     ; to=(BB7)
0006 ECHO string("foo
")

BB7:
     ; follow target lines=[7-8]
     ; to=(BB1, BB8)
0007 ECHO string("bar
")
0008 JMPNZ CV2($c) BB1

BB8:
     ; follow target exit lines=[9-10]
0009 ECHO string("baz
")
0010 RETURN null
 [2020-10-19 13:15 UTC] nikic@php.net
Automatic comment on behalf of nikita.ppv@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d3812ca41b8ff40cc2d38fb4d2a79ae1902761cb
Log: Fixed bug #80255
 [2020-10-19 13:15 UTC] nikic@php.net
-Status: Verified +Status: Closed
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Mon Jan 18 09:01:28 2021 UTC