|  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #81249 Intermittent property assignment failure with JIT enabled
Submitted: 2021-07-12 03:46 UTC Modified: 2021-07-12 08:13 UTC
From: smokey101stair at gmail dot com Assigned: dmitry (profile)
Status: Closed Package: JIT
PHP Version: 8.0.8 OS: Ubuntu 20.04
Private report: No CVE-ID: None
 [2021-07-12 03:46 UTC] smokey101stair at gmail dot com
PHP 8.0.8 (cli) (built: Jul  4 2021 21:22:34) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.8, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.8, Copyright (c), by Zend Technologies

PHP was compiled using PHPBrew, if that makes a difference.


The test script decodes EUC-JP text to Unicode Code Points. I tried to reduce the script as much as possible, but it was very temperamental when removing if statements.

When processing the first byte, 8F, the assignment at fails intermittently. This leaves $this->lead set to its initial value of 0, which eventually leads to the script returning an error instead of a code point.

The script is expected to transform the EUC-JP encoded bytes to the Unicode U+2D8 code point.

Test script:


Add a Patch

Pull Requests

Add a Pull Request


AllCommentsChangesGit/SVN commitsRelated reports
 [2021-07-12 08:13 UTC]
-Status: Open +Status: Verified -Assigned To: +Assigned To: dmitry
 [2021-07-19 00:30 UTC] hao dot sun at arm dot com

1. This bug occurred for master branch as well, for both JIT/x86 and JIT/arm64.
2. In my test, the test case can be further downsized, that is, Lines 85 to 87 can be removed as well, i.e.
3. I suspect this bug is due to "hot side exit".
1) -d opcache.jit_hot_side_exit=0, this bug is gone.
2) -d opcache.jit_hot_side_exit=N, the smaller the number we assigned, the earlier this bug showed up.
 [2021-07-19 06:27 UTC] hao dot sun at arm dot com
Based on my local debugging, I suspected function zend_jit_may_skip_comparison().

Code is not generated as expected for the hot side trace for statement: "if ($byte === 0x8E || $byte === 0x8F || ($byte >= 0xA1 && $byte <= 0xFE)) {"
Here is trace #5: (Note several "echo" statements are inserted in my local debugging). For "$byte === 0x8E || $byte === 0x8F", there are two IS_IDENTICAL opcodes.

     TRACE 3 exit 1 EucJpDecoder::handle() /home/haosun01/php/7.php:91
---- TRACE 5 start (side trace 3/1) EucJpDecoder::handle() /home/haosun01/php/7.php:91
========3333333 143
===== 44444444 set lead here: 143 : 143
0086 T7 = ROPE_INIT 3 string("========3333333 ")
0087 T7 = ROPE_ADD 1 T7 CV1($byte) ; op2(int)
0088 T6 = ROPE_END 2 T7 string("
0089 ECHO T6 ; op1(string)
0090 T6 = IS_IDENTICAL CV1($byte) int(142) ; op1(int)
0091 ;JMPNZ T6 0098
0092 T6 = IS_IDENTICAL CV1($byte) int(143) ; op1(int)
0093 ;JMPNZ T6 0098
0098 ASSIGN_OBJ THIS string("lead") ; op3(int)
0099 ;OP_DATA CV1($byte)
0100 T7 = ROPE_INIT 5 string("===== 44444444 set lead here: ")
0101 T7 = ROPE_ADD 1 T7 CV1($byte) ; op2(int)
0102 T7 = ROPE_ADD 2 T7 string(" : ")
0103 T6 = FETCH_OBJ_R THIS string("lead")
0104 T7 = ROPE_ADD 3 T7 T6 ; op2(int)
0105 T6 = ROPE_END 4 T7 string("
0106 ECHO T6 ; op1(string)
0107 RETURN int(-1)
---- TRACE 5 stop (return)

However, in the compiled code, I found that only the code for "$byte === 0x8E" is generated. Here is part of the code. See "cmp x15, #0x8e".

    ffffbb9c8250:       movz x15, #0x51c0
    ffffbb9c8254:       movk x15, #0xabb6, lsl #16
    ffffbb9c8258:       movk x15, #0xaaaa, lsl #32
    ffffbb9c825c:       ldr x8, [x15]
    ffffbb9c8260:       cbnz x8, #JIT$$exception_handler
    ffffbb9c8264:       ldr x15, [x27, #0x60]
    ffffbb9c8268:       cmp x15, #0x8e
    ffffbb9c826c:       b.eq #jit$$trace_exit_0
    ffffbb9c8270: #jit$$trace_exit_1
    ffffbb9c8274:       ldr x0, [x27, #0x20]
    ffffbb9c8278:       ldrb w16, [x0, #0x30]
    ffffbb9c827c:       cbz w16, #jit$$trace_exit_2
    ffffbb9c8280:       add x0, x0, #0x28
    ffffbb9c8284:       add x1, x27, #0x60
    ffffbb9c8288:       adrp x8, #0xffffb4298000
    ffffbb9c828c:       add x8, x8, #0xa70
    ffffbb9c8290:       str x8, [x27]
    ffffbb9c8294:       bl #JIT$$assign_tmp
    ffffbb9c8298:       movz x15, #0x51c0
    ffffbb9c829c:       movk x15, #0xabb6, lsl #16
    ffffbb9c82a0:       movk x15, #0xaaaa, lsl #32
    ffffbb9c82a4:       ldr x8, [x15]
    ffffbb9c82a8:       cbnz x8, #JIT$$exception_handler
    ffffbb9c82ac:       movz x15, #0x160
    ffffbb9c82b0:       add x28, x28, x15

That is because "skip_comparison" is set as true for the second ZEND_IS_IDENTICAL, i.e. JIT is bypassed.
In details, in file zend_jit_trace.c, these code ( is used to generate the JIT code for ZEND_IS_IDENTICAL. However, I found that variable "skip_comparison" is set as false for "$byte === 0x8E", but is set as true for "$byte === 0x8F".
I further looked at function zend_jit_may_skip_comparison(). I noticed that for $byte === 0x8F, this function returns "1" at

In one word, I guess the root cause of this bug lies in how to determine "skip_comparison" for the case of two consecutive "ZEND_IS_IDENTICAL".
As I tried, if we always set variable "skip_comparison" as false, this bug can be gone.
 [2021-07-19 09:15 UTC]
Automatic comment on behalf of dstogov
Log: Fixed bug #81249 (Intermittent property assignment failure with JIT enabled)
 [2021-07-19 09:15 UTC]
-Status: Verified +Status: Closed
PHP Copyright © 2001-2023 The PHP Group
All rights reserved.
Last updated: Wed Mar 29 02:03:37 2023 UTC