php / php-src

The PHP Interpreter
https://www.php.net
Other
38.1k stars 7.74k forks source link

Segmentation fault in Zend/zend_vm_execute.h #15981

Open YuanchengJiang opened 3 weeks ago

YuanchengJiang commented 3 weeks ago

Description

The following code:

<?php
require('tester.inc');
$cfg = <<<EOT
EOT;
$code = <<<EOT
EOT;
$tester = new FPM\Tester($cfg, $code);
$tester->start();

Resulted in this output:

Zend/zend_vm_execute.h:6025:2: runtime error: member access within misaligned address 0x7fd6e5c0520f for type 'zval' (aka 'struct _zval_struct'), which requires 8 byte alignment
0x7fd6e5c0520f: note: pointer points here
 00 00 00 00 00  f8 12 0c 41 00 00 00 00  00 00 00 00 00 00 00 00  f0 51 c0 e5 d6 7f 00 00  58 09 0c
             ^ 
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior Zend/zend_vm_execute.h:6025:2

To reproduce:

-d "opcache.jit_hot_loop=1" -d "zend_extension=/php-src/modules/opcache.so" -d "opcache.enable_cli=1" -d "opcache.jit=1131"

tester.inc:

sapi/fpm/tests/tester.inc

PHP Version

PHP 8.4.0-dev

Operating System

ubuntu 22.04

cmb69 commented 3 weeks ago

Looks related to #15658.

nielsdos commented 3 weeks ago

Semi-automatically reduced to:

<?php

namespace NS { // Namespace is important to reproduce the issue
    class Tester {
        static public function findExecutable(): string {
            for ($i = 0; $i < 2; $i++) {
                // Need this loop to reproduce
            }
            return dirname(__DIR__);
        }
    }
}

namespace {
    NS\Tester::findExecutable();
}

Or with opcache.jit=1111:

<?php

namespace NS { // Namespace is important to reproduce the issue
    class Tester {
        static public function findExecutable(): string {
            return dirname(__DIR__);
        }
    }
}

namespace {
    NS\Tester::findExecutable();
}
nielsdos commented 3 weeks ago

It's again related to FLFs.

ZEND_INIT_NS_FCALL_BY_NAME VM handler is called with opline->opcode == JMP_FRAMELESS and pointing at the wrong cache slot as a consequence.

I think the JIT should generate a move to %r15 with the right opline in both possible successor blocks of JMP_FRAMELESS, but I'm not sure how to do that properly. Using zend_jit_reset_last_valid_opline in zend_jit_jmp_frameless doesn't work because the following code resets the last valid opline:

https://github.com/php/php-src/blob/d95e222402029cf1142847d5bb0cdcb9434ab90e/ext/opcache/jit/zend_jit.c#L1409-L1419

nielsdos commented 3 weeks ago

cc @dstogov I'm not sure how to fix this, I would need some input to know how to tackle this issue please.

dstogov commented 3 weeks ago

I don't know this new code... It looks like JIT code generated by zend_jit_jmp_frameless() doesn't update EX(opline). Actually JIT with optimization_level=1 shouldn't generate the complex code at all.

cc @iluuu1994