php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #81256 Assertion `zv != ((void *)0)' failed for "preload" with JIT
Submitted: 2021-07-14 03:49 UTC Modified: 2021-07-20 09:52 UTC
From: hao dot sun at arm dot com Assigned:
Status: Closed Package: JIT
PHP Version: 8.1.0alpha3 OS: Ubuntu 20.04
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: hao dot sun at arm dot com
New email:
PHP Version: OS:

 

 [2021-07-14 03:49 UTC] hao dot sun at arm dot com
Description:
------------
I suppose tracing JIT is tested by the "community_job.yml" in Azure pipeline in upstream. I further tested with functional JIT.

This bug was found when running the "Symfony demo" with functional JIT, i.e. opcache.jit=1205, in NTS+DEBUG+HYBRID+ASAN.

Note: this bug only occurred in HYBRID VM mode, not CALL VM mode.
Note: this bug occurred in both JIT/arm64 and JIT/x86.
Note: this bug occurred with ZEND_JIT_LEVEL_OPT_SCRIPT optimization level. If we run the test with "opcache.jit=1255", this bug showed up as well.

Here is the error msg:
# php -d opcache.preload=var/cache/dev/App_KernelDevDebugContainer.preload.php public/index.php
php: /opt/php-8.1.0alpha3/Zend/zend_vm_execute.h:62757: zend_get_opcode_handler_func: Assertion `zv != ((void *)0)' failed.


One child process would be created to preload the scripts (See function accel_finish_startup()) and zend_jit_script() would be invoked under ZEND_JIT_LEVEL_OPT_SCRIPT.
The assertion occurs when generating the JIT code.

Here the backtrace info for this child process.

(gdb) bt
#0  0x00007ffff568a18b in raise () from /usr/lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff5669859 in abort () from /usr/lib/x86_64-linux-gnu/libc.so.6
#2  0x00007ffff5669729 in ?? () from /usr/lib/x86_64-linux-gnu/libc.so.6
#3  0x00007ffff567af36 in __assert_fail () from /usr/lib/x86_64-linux-gnu/libc.so.6
#4  0x0000555556f9b22e in zend_get_opcode_handler_func (op=0x41af3f48) at /opt/php-8.1.0alpha3/Zend/zend_vm_execute.h:62757
#5  0x00007fffefcd55eb in zend_jit_handler (Dst=0x7fffffffdc60, opline=0x41af3f48, may_throw=1) at /opt/php-8.1.0alpha3/ext/opcache/jit/zend_jit_x86.dasc:3649
#6  0x00007fffefda686d in zend_jit (op_array=0x41af3d78, ssa=0x6120001659e8, rt_opline=0x0) at /opt/php-8.1.0alpha3/ext/opcache/jit/zend_jit.c:4040
#7  0x00007fffefe1d8c5 in zend_jit_script (script=0x41b01e40) at /opt/php-8.1.0alpha3/ext/opcache/jit/zend_jit.c:4511
#8  0x00007fffefc5d4c2 in zend_accel_script_persist (script=0x41b01e40, for_shm=1) at /opt/php-8.1.0alpha3/ext/opcache/zend_persist.c:1379
#9  0x00007fffefc34619 in preload_script_in_shared_memory (new_persistent_script=0x615000069800) at /opt/php-8.1.0alpha3/ext/opcache/ZendAccelerator.c:4423
#10 0x00007fffefc38442 in accel_preload (config=0x607000000b98 "var/cache/dev/App_KernelDevDebugContainer.preload.php", in_child=true)
    at /opt/php-8.1.0alpha3/ext/opcache/ZendAccelerator.c:4840
#11 0x00007fffefc395c1 in accel_finish_startup () at /opt/php-8.1.0alpha3/ext/opcache/ZendAccelerator.c:5038
#12 0x00007fffefc2b39b in accel_post_startup () at /opt/php-8.1.0alpha3/ext/opcache/ZendAccelerator.c:3309
#13 0x0000555556dae1d3 in zend_post_startup () at /opt/php-8.1.0alpha3/Zend/zend.c:1078
#14 0x0000555556c46c9c in php_module_startup (sf=0x5555588bd720 <cli_sapi_module>, additional_modules=0x0, num_additional_modules=0) at /opt/php-8.1.0alpha3/main/main.c:2277
#15 0x0000555557166405 in php_cli_startup (sapi_module=0x5555588bd720 <cli_sapi_module>) at /opt/php-8.1.0alpha3/sapi/cli/php_cli.c:409
#16 0x000055555716aa36 in main (argc=4, argv=0x604000000250) at /opt/php-8.1.0alpha3/sapi/cli/php_cli.c:1333



Test script:
---------------
Following the "community_job.xml", download "Symfony" test case, prepare the "Symfony_demo" and 

run "php -d opcache.preload=var/cache/dev/App_KernelDevDebugContainer.preload.php public/index.php"

The version of Symfony I used is 

commit 39e9d75c95598d6e09428f201f6f1921a40503f5
Author: Nicolas Grekas <nicolas.grekas@gmail.com>
Date:   Mon Jul 12 16:15:46 2021 +0200

    Merge branch '5.3' into 5.4
    
    * 5.3:
      skip Bootstrap 4 tests that do not apply to the Bootstrap 5 form theme


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-07-15 09:40 UTC] hao dot sun at arm dot com
I'd like to share some of my debugging results. Hope that this info could help to diagnose this bug.

I suspected this is because the same function is compiled by JIT for two times.

In the first time, the handler of one opline(in my test, the opcode is #164, and is in function trigger_deprecation() at file symfony_demo/vendor/symfony/deprecation-contracts/function.php) is replaced by the address of JIT-ed code.

Hence, in the second time, during generating the JIT code, the original handler of this opline, i.e. the interpreter handler, is needed by zend_jit_handler(), however, it's not a valid interpreter handler any longer, resulting in the failure in function zend_get_opcode_handler_func().

"Preload" mainly works in function accel_preload().
1) it firstly parses all functions and classes using the filename "$PRELOAD$. See https://github.com/php/php-src/blob/php-8.1.0alpha3/ext/opcache/ZendAccelerator.c#L4780-L4823
2) and then each script would be parsed. See https://github.com/php/php-src/blob/php-8.1.0alpha3/ext/opcache/ZendAccelerator.c#L4836-L4841

In my debugging, I found one opline is parsed by step 1) and step 2) respectively.
 [2021-07-17 15:05 UTC] hao dot sun at arm dot com
Here is one workaround, disabling the JIT process for opcache.jit=1205 if one function has already been preloaded before.
But I don't think it's a good fix. I guess we should add some check in the "preload" module to filter out duplicate functions.

Besides, this workaround only works for jit=1205. 
We should take care of other trigger options, i.e. 1215, 1225, 1235 and 1255. In my local test, if we add similar check before https://github.com/php/php-src/blob/master/ext/opcache/jit/zend_jit.c#L4470, use after free error would be raised.



diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index d700004442..26e5a6d762 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -4514,6 +4514,9 @@ ZEND_EXT_API int zend_jit_script(zend_script *script)
                for (i = 0; i < call_graph.op_arrays_count; i++) {
                        info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
                        if (info) {
+                               if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_PRELOADED) {
+                                       continue;
+                               }
                                if (zend_jit(call_graph.op_arrays[i], &info->ssa, NULL) != SUCCESS) {
                                        goto jit_failure;
                                }
 [2021-07-20 09:52 UTC] dmitry@php.net
The problem is caused by attempt to JIT the same op_array, wrapped by "if", twice.

The simplified test-case:

preload.php
===========
<?php
$a = true;
if ($a) {
        function foo(...$arg) {
                echo "Hello\n";
        }
}
?>

$ php -dopcache.preload=preload.php -d opcache.jit=1205 -r 'foo();'
php: /home/dmitry/php/php-master/Zend/zend_vm_execute.h:62562: zend_get_opcode_handler_func: Assertion `zv != ((void *)0)' failed.
Aborted (core dumped)
 [2021-07-20 12:28 UTC] git@php.net
Automatic comment on behalf of dstogov
Revision: https://github.com/php/php-src/commit/1e4095f03dea465dc1f2e04861a0e9422bdcb6f9
Log: Fixed bug #81256 (Assertion `zv != ((void *)0)' failed for &quot;preload&quot; with JIT)
 [2021-07-20 12:28 UTC] git@php.net
-Status: Open +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 13:01:29 2024 UTC