mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Add JIT guards for INIT_METHOD_CALL when the method may be modified (#8600)
Non-polymorphic methods can be modified from one request to an other due to recompilation or conditional declaration. Fixes GH-8591 Co-authored-by: Oleg Stepanischev <Oleg.Stepanischev@tatar.ru>
This commit is contained in:
@@ -8998,7 +8998,7 @@ static int zend_jit_init_method_call(dasm_State **Dst,
|
||||
|2:
|
||||
}
|
||||
|
||||
if (!func
|
||||
if ((!func || zend_jit_may_be_modified(func, op_array))
|
||||
&& trace
|
||||
&& trace->op == ZEND_JIT_TRACE_INIT_CALL
|
||||
&& trace->func
|
||||
@@ -9006,7 +9006,7 @@ static int zend_jit_init_method_call(dasm_State **Dst,
|
||||
int32_t exit_point;
|
||||
const void *exit_addr;
|
||||
|
||||
exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_METHOD_CALL);
|
||||
exit_point = zend_jit_trace_get_exit_point(opline, func ? ZEND_JIT_EXIT_INVALIDATE : ZEND_JIT_EXIT_METHOD_CALL);
|
||||
exit_addr = zend_jit_trace_get_exit_addr(exit_point);
|
||||
if (!exit_addr) {
|
||||
return 0;
|
||||
|
||||
@@ -719,6 +719,26 @@ static zend_always_inline const zend_op* zend_jit_trace_get_exit_opline(zend_jit
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool zend_jit_may_be_modified(const zend_function *func, const zend_op_array *called_from)
|
||||
{
|
||||
if (func->type == ZEND_INTERNAL_FUNCTION) {
|
||||
#ifdef _WIN32
|
||||
/* ASLR */
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
} else if (func->type == ZEND_USER_FUNCTION) {
|
||||
if (func->common.fn_flags & ZEND_ACC_PRELOADED) {
|
||||
return 0;
|
||||
}
|
||||
if (func->op_array.filename == called_from->filename && !func->op_array.scope) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static zend_always_inline bool zend_jit_may_be_polymorphic_call(const zend_op *opline)
|
||||
{
|
||||
if (opline->opcode == ZEND_INIT_FCALL
|
||||
|
||||
@@ -366,26 +366,6 @@ static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool zend_jit_may_be_modified(const zend_function *func, const zend_op_array *called_from)
|
||||
{
|
||||
if (func->type == ZEND_INTERNAL_FUNCTION) {
|
||||
#ifdef _WIN32
|
||||
/* ASLR */
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
} else if (func->type == ZEND_USER_FUNCTION) {
|
||||
if (func->common.fn_flags & ZEND_ACC_PRELOADED) {
|
||||
return 0;
|
||||
}
|
||||
if (func->op_array.filename == called_from->filename && !func->op_array.scope) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static zend_always_inline uint32_t zend_jit_trace_type_to_info_ex(zend_uchar type, uint32_t info)
|
||||
{
|
||||
if (type == IS_UNKNOWN) {
|
||||
|
||||
@@ -9630,7 +9630,7 @@ static int zend_jit_init_method_call(dasm_State **Dst,
|
||||
|2:
|
||||
}
|
||||
|
||||
if (!func
|
||||
if ((!func || zend_jit_may_be_modified(func, op_array))
|
||||
&& trace
|
||||
&& trace->op == ZEND_JIT_TRACE_INIT_CALL
|
||||
&& trace->func
|
||||
@@ -9641,7 +9641,7 @@ static int zend_jit_init_method_call(dasm_State **Dst,
|
||||
int32_t exit_point;
|
||||
const void *exit_addr;
|
||||
|
||||
exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_METHOD_CALL);
|
||||
exit_point = zend_jit_trace_get_exit_point(opline, func ? ZEND_JIT_EXIT_INVALIDATE : ZEND_JIT_EXIT_METHOD_CALL);
|
||||
exit_addr = zend_jit_trace_get_exit_addr(exit_point);
|
||||
if (!exit_addr) {
|
||||
return 0;
|
||||
|
||||
7
ext/opcache/tests/jit/gh8591-001.inc
Normal file
7
ext/opcache/tests/jit/gh8591-001.inc
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
interface ModelInterface
|
||||
{
|
||||
}
|
||||
49
ext/opcache/tests/jit/gh8591-001.phpt
Normal file
49
ext/opcache/tests/jit/gh8591-001.phpt
Normal file
@@ -0,0 +1,49 @@
|
||||
--TEST--
|
||||
Bug GH-8591 001 (JIT does not account for class re-compile)
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.jit_buffer_size=1M
|
||||
opcache.jit=1255
|
||||
opcache.file_update_protection=0
|
||||
opcache.revalidate_freq=0
|
||||
opcache.protect_memory=1
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
// Checks that JITed code does not crash in --repeat 2 after the ModelInterface
|
||||
// interface is recompiled and Model is re-linked.
|
||||
|
||||
require __DIR__ . '/gh8591-001.inc';
|
||||
|
||||
class Model implements ModelInterface
|
||||
{
|
||||
protected static int $field = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$this->cast();
|
||||
}
|
||||
}
|
||||
|
||||
private function cast()
|
||||
{
|
||||
global $x;
|
||||
$x = static::$field;
|
||||
}
|
||||
}
|
||||
|
||||
new Model();
|
||||
|
||||
// mark the file as changed (important)
|
||||
touch(__DIR__ . '/gh8591-001.inc');
|
||||
|
||||
var_dump($x);
|
||||
|
||||
print "OK";
|
||||
--EXPECT--
|
||||
int(1)
|
||||
OK
|
||||
7
ext/opcache/tests/jit/gh8591-002.inc
Normal file
7
ext/opcache/tests/jit/gh8591-002.inc
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
interface ModelInterface
|
||||
{
|
||||
}
|
||||
52
ext/opcache/tests/jit/gh8591-002.phpt
Normal file
52
ext/opcache/tests/jit/gh8591-002.phpt
Normal file
@@ -0,0 +1,52 @@
|
||||
--TEST--
|
||||
Bug GH-8591 002 (JIT does not account for class re-compile)
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.jit_buffer_size=1M
|
||||
opcache.jit=1255
|
||||
opcache.file_update_protection=0
|
||||
opcache.revalidate_freq=0
|
||||
opcache.protect_memory=1
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
// Checks that JITed code does not crash in --repeat 2 after the ModelInterface
|
||||
// interface changes and Model is re-linked.
|
||||
|
||||
if (!isset(opcache_get_status()['scripts'][__DIR__ . '/gh8591-002.inc'])) {
|
||||
require __DIR__ . '/gh8591-001.inc';
|
||||
} else {
|
||||
interface ModelInterace
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Model implements ModelInterface
|
||||
{
|
||||
protected static int $field = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$this->cast();
|
||||
}
|
||||
}
|
||||
|
||||
private function cast()
|
||||
{
|
||||
global $x;
|
||||
$x = static::$field;
|
||||
}
|
||||
}
|
||||
|
||||
new Model();
|
||||
|
||||
var_dump($x);
|
||||
|
||||
print "OK";
|
||||
--EXPECT--
|
||||
int(1)
|
||||
OK
|
||||
45
ext/opcache/tests/jit/gh8591-003.phpt
Normal file
45
ext/opcache/tests/jit/gh8591-003.phpt
Normal file
@@ -0,0 +1,45 @@
|
||||
--TEST--
|
||||
Bug GH-8591 003 (JIT does not account for class re-compile)
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.jit_buffer_size=1M
|
||||
opcache.jit=1255
|
||||
opcache.file_update_protection=0
|
||||
opcache.revalidate_freq=0
|
||||
opcache.protect_memory=1
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface ModelInterface
|
||||
{
|
||||
}
|
||||
|
||||
class Model implements ModelInterface
|
||||
{
|
||||
protected static int $field = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$this->cast();
|
||||
}
|
||||
}
|
||||
|
||||
private function cast()
|
||||
{
|
||||
global $x;
|
||||
$x = static::$field;
|
||||
}
|
||||
}
|
||||
|
||||
new Model();
|
||||
|
||||
var_dump($x);
|
||||
|
||||
print "OK";
|
||||
--EXPECT--
|
||||
int(1)
|
||||
OK
|
||||
7
ext/opcache/tests/jit/gh8591-004.inc
Normal file
7
ext/opcache/tests/jit/gh8591-004.inc
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
trait ModelTrait
|
||||
{
|
||||
}
|
||||
51
ext/opcache/tests/jit/gh8591-004.phpt
Normal file
51
ext/opcache/tests/jit/gh8591-004.phpt
Normal file
@@ -0,0 +1,51 @@
|
||||
--TEST--
|
||||
Bug GH-8591 004 (JIT does not account for class re-compile)
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.jit_buffer_size=1M
|
||||
opcache.jit=1255
|
||||
opcache.file_update_protection=0
|
||||
opcache.revalidate_freq=0
|
||||
opcache.protect_memory=1
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
// Checks that JITed code does not crash in --repeat 2 after the ModelTrait
|
||||
// trait is recompiled and Model is re-linked.
|
||||
|
||||
require __DIR__ . '/gh8591-004.inc';
|
||||
|
||||
class Model
|
||||
{
|
||||
use ModelTrait;
|
||||
|
||||
protected static int $field = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$this->cast();
|
||||
}
|
||||
}
|
||||
|
||||
private function cast()
|
||||
{
|
||||
global $x;
|
||||
$x = static::$field;
|
||||
}
|
||||
}
|
||||
|
||||
new Model();
|
||||
|
||||
// mark the file as changed (important)
|
||||
touch(__DIR__ . '/gh8591-004.inc');
|
||||
|
||||
var_dump($x);
|
||||
|
||||
print "OK";
|
||||
--EXPECT--
|
||||
int(1)
|
||||
OK
|
||||
12
ext/opcache/tests/jit/gh8591-005.inc
Normal file
12
ext/opcache/tests/jit/gh8591-005.inc
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
class AbstractModel
|
||||
{
|
||||
protected static int $field = 1;
|
||||
|
||||
final protected function cast()
|
||||
{
|
||||
global $x;
|
||||
$x = static::$field;
|
||||
}
|
||||
}
|
||||
41
ext/opcache/tests/jit/gh8591-005.phpt
Normal file
41
ext/opcache/tests/jit/gh8591-005.phpt
Normal file
@@ -0,0 +1,41 @@
|
||||
--TEST--
|
||||
Bug GH-8591 001 (JIT does not account for class re-compile)
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.jit_buffer_size=1M
|
||||
opcache.jit=1255
|
||||
opcache.file_update_protection=0
|
||||
opcache.revalidate_freq=0
|
||||
opcache.protect_memory=1
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
// Checks that JITed code does not crash in --repeat 2 after the AbstractModel
|
||||
// class is recompiled and Model is re-linked.
|
||||
|
||||
require __DIR__ . '/gh8591-005.inc';
|
||||
|
||||
class Model extends AbstractModel
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$this->cast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Model();
|
||||
|
||||
// mark the file as changed (important)
|
||||
touch(__DIR__ . '/gh8591-005.inc');
|
||||
|
||||
var_dump($x);
|
||||
|
||||
print "OK";
|
||||
--EXPECT--
|
||||
int(1)
|
||||
OK
|
||||
12
ext/opcache/tests/jit/gh8591-006.inc
Normal file
12
ext/opcache/tests/jit/gh8591-006.inc
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
class AbstractModel
|
||||
{
|
||||
protected static int $field = 1;
|
||||
|
||||
final protected function cast()
|
||||
{
|
||||
global $x;
|
||||
$x = static::$field;
|
||||
}
|
||||
}
|
||||
38
ext/opcache/tests/jit/gh8591-006.phpt
Normal file
38
ext/opcache/tests/jit/gh8591-006.phpt
Normal file
@@ -0,0 +1,38 @@
|
||||
--TEST--
|
||||
Bug GH-8591 001 (JIT does not account for class re-compile)
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.jit_buffer_size=1M
|
||||
opcache.jit=1255
|
||||
opcache.file_update_protection=0
|
||||
opcache.revalidate_freq=0
|
||||
opcache.protect_memory=1
|
||||
opcache.preload={PWD}/gh8591-006.inc
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Model extends AbstractModel
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$this->cast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Model();
|
||||
|
||||
var_dump($x);
|
||||
|
||||
print "OK";
|
||||
--EXPECT--
|
||||
int(1)
|
||||
OK
|
||||
Reference in New Issue
Block a user