Bug #80959 [Ver]: infinite loop in building cfg during JIT compilation
| From: | nikic@php.net | Date: | Fri, 16 Apr 2021 09:25:52 +0000 |
| Subject: | Bug #80959 [Ver]: infinite loop in building cfg during JIT compilation | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-233465@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=80959&edit=1
ID: 80959
Updated by: nikic@php.net
Reported by: zengyhkyle at asu dot edu
Summary: infinite loop in building cfg during JIT compilation
Status: Verified
Type: Bug
Package: JIT
Operating System: Linux
PHP Version: 8.0.4RC1
Block user comment: N
Private report: N
New Comment:
Though it would be great if we could not run loop identification in the first place if a function
has try/catch. In that case we don't perform SSA construction or register allocation, which
need this for correctness. It seems like the only places that use the information in that case is
timeout checks and hot loop counters. Maybe we just want to pessimize those in the presence of
try/catch?
Previous Comments:
------------------------------------------------------------------------
[2021-04-16 09:12:46] nikic@php.net
CFG for reference: https://gist.github.com/nikic/84d11192df4d7b6cc42f971c7d15920b
The core problem here is that BB5 and BB6 are not reachable from entry through normal edges (they
have domtree level 0 and will be processed last). They are reachable in the reverse graph though,
which is traversed when loop headers are assigned. We end up with BB3 having BB6 as loop header, and
BB6 having BB3 as loop header, causing an infinite loop.
There's probably two separate problems here: We should be treating unreachable code more
gracefully, and we should not be treating this code as unreachable in the first place, though
I'm not sure what the right way to handle unwind edges would be right now.
------------------------------------------------------------------------
[2021-04-16 08:23:48] nikic@php.net
Nice find! Here's a somewhat reduced version:
<?php
function test($a, $b) {
echo "Start\n";
do {
$i++;
try {
continue;
} catch (Exception $e) {
}
do {
$j++;
} while ($j < $b);
} while ($i < $a);
echo "Done\n";
}
test();
------------------------------------------------------------------------
[2021-04-15 22:34:42] zengyhkyle at asu dot edu
in our experiment, the execution is stuck at function
zend_cfg_identify_loops
------------------------------------------------------------------------
[2021-04-15 22:32:33] zengyhkyle at asu dot edu
Description:
------------
if JIT compilation is enabled, PHP will get stuck in infinitely during loop identification when
building the cfg for some code.
the configuration we use is listed as follow:
~~~
opcache.jit_debug=263
opcache.enable_cli=1
opcache.jit=1205
opcache.jit_buffer_size=1G
zend_extension=/home/user/php-src-php-8.0.3/modules/opcache.so
~~~
This vulnerability is found by Yihui Zeng, Jayakrishna Menon, Steven Wirsz, and Gokul Krishna P from
Arizona State University for class CSE598 Applied Vulnerability Research
a poc is attached.
Test script:
---------------
<?php
$v0 = -815;
$v1 = True;
$v2 = $v0;
if($v1){
$v3 = [5, 5];
$v2 = $v3;
}else{
$v4 = False;
$v5 = $v0;
if($v4){
$v6 = $v4|$v0;
$v5 = $v6;
}else{
$v5 = $v5 + 1;
$v7 = $v5 + 1;
$v5 = $v7;
}
$v2 = $v5;
}
$v8 = 0;
$v9 = 4;
$v10 = $v8;
do{
$v10 = $v10 + 1;
$v11 = $v10 + 1;
$v12 = $v10;
try{
continue;
$v10 = $v12;
}catch(Exception $e){
$v2 = $v12;
}
$v13 = False;
$v14 = $v13;
if($v13){
$v15 = $v12/$v14;
$v14 = $v15;
}else{
$v16 = 0;
$v17 = 9;
$v18 = $v16;
do{
$v18 = $v18 + 1;
$v19 = $v18 + 1;
$v19 = $v19 - 1;
$v20 = $v19 - 1;
}while($v18<$v17);
$v21 = "kBBqSjjO63";
$v21[0] = $v12;
$v14 = $v21;
}
}while($v10<$v9);
$v22 = [3.055141489223973, 5.118032201755781, 1.6115282496633003, 2.796353888054253,
1.607762036181039, 5.52005061408927, 2.4609577104054896];
$v23 = [4, 1, 4, 4];
echo "Done";
?>
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=80959&edit=1