php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77275 OPcache optimization problem for ArrayAccess->offsetGet(string)
Submitted: 2018-12-10 05:46 UTC Modified: 2018-12-10 11:33 UTC
From: kentaro at ranvis dot com Assigned:
Status: Closed Package: opcache
PHP Version: 7.3.0 OS: Windows 10
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: kentaro at ranvis dot com
New email:
PHP Version: OS:

 

 [2018-12-10 05:46 UTC] kentaro at ranvis dot com
Description:
------------
In a certain condition, a call to the class method offsetGet() via $instance['literalString'] is turned into $instance[anotherLiteral] when OPcache is enabled.

Steps to reproduce:
1. On Windows x64, extract php-7.3.0-nts-Win32-VC15-x64.zip into the current directory
2. Save test script as test.php
3. Make sure no PHP built-in server is running (as shared memory matters?)
4. Launch PHP built-in server by running a command:
   php -c php.ini-development -d extension_dir=ext -d zend_extension=php_opcache.dll -S 127.0.0.1:8000
5. Open browser and go to http://127.0.0.1:8000/test.php
   The output is: string(1) "a"
6. Touch test.php to update the modification time, and wait 3 seconds (opcache.revalidate_freq)
7. Reload the browser and confirm the output


Test script:
---------------
<?php
namespace Foo;
class Bar { public function get() {} }
class Record implements \ArrayAccess {
    public function offsetSet($offset, $value) { throw new \Exception; }
    public function offsetGet($offset) { var_dump($offset); }
    public function offsetExists($offset) { throw new \Exception; }
    public function offsetUnset($offset) { throw new \Exception; }
}
class Baz {
    public function run() {
        $a = pow(1, 2);
        $b = new Bar();
        $c = new Bar();
        $d = new Bar();
        $id = $b->get('a', 'b', 'c');
        $rec = new Record();
        $id = $rec['a'];
    }
}
(new Baz())->run();


Expected result:
----------------
The output is: string(1) "a"

Actual result:
--------------
The output is: string(1) "b"

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-12-10 11:06 UTC] nikic@php.net
Can't repro on Ubuntu, though the issue is certainly plausible. At a guess the second literal for the the offset lookup is not being preserved and being overwritten.
 [2018-12-10 11:12 UTC] nikic@php.net
-Status: Open +Status: Verified
 [2018-12-10 11:12 UTC] nikic@php.net
Can reproduce under valgrind:

==23813== Conditional jump or move depends on uninitialised value(s)
==23813==    at 0x91B2F8: ZEND_FETCH_DIM_R_SPEC_CV_CONST_HANDLER (zend_vm_execute.h:39179)
==23813==    by 0x93A255: execute_ex (zend_vm_execute.h:59027)
==23813==    by 0x93B63D: zend_execute (zend_vm_execute.h:60215)
==23813==    by 0x84E2D7: zend_execute_scripts (zend.c:1615)
==23813==    by 0x789D5C: php_execute_script (main.c:2641)
==23813==    by 0x93E6BA: do_cli (php_cli.c:997)
==23813==    by 0x93FB05: main (php_cli.c:1389)
 [2018-12-10 11:29 UTC] chris at xenforo dot com
We have been looking into this issue in our own code as of last week while trying to ensure PHP compatibility with our application.

I can tell you, if it helps, that the issue does not exist in 7.3 RC3, so it was introduced some time after that.
 [2018-12-10 11:33 UTC] nikic@php.net
The problem is that we're merging a Z_EXTRA=0 literal with a Z_EXTRA=undefined literal. This is basically the same as bug #76711, but that one only fixed the case of integer literals, while here it's a string :/
 [2018-12-10 12:37 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=93aabf1533bd3af673bb59cf283e6599ced3ab9a
Log: Fixed bug #77275
 [2018-12-10 12:37 UTC] nikic@php.net
-Status: Verified +Status: Closed
 [2018-12-16 11:54 UTC] greenreaper at hotmail dot com
I think we have been running into this problem (or the integer version of it) on a busy custom website (no framework) with FPM and opcache, that was experiencing sudden crashes and termination of all processes (leading to 502 errors).

We set in php.ini:
opcache.optimization_level=0x7FFFBBFF

The second 'B' represents the removal of 0x400, or ZEND_OPTIMIZER_PASS_11 (1<<10) /* Merge equal constants */

So far the issue has not reoccurred.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 10:01:29 2024 UTC