Merge branch 'PHP-8.2' into PHP-8.3

* PHP-8.2:
  Fix zend_separate_if_call_and_write for FUNC_ARGs
This commit is contained in:
Ilija Tovilo 2023-09-07 14:26:09 +02:00
commit c2bb9bc0df
No known key found for this signature in database
GPG key ID: A4F5D403F118200A
6 changed files with 133 additions and 4 deletions

3
NEWS
View file

@ -12,7 +12,8 @@ PHP NEWS
(ju1ius)
. Fixed OSS Fuzz #61865 (Undef variable in ++/-- for declared property
that is unset in error handler). (Girgias)
. Fixed bug GH-12102 (Incorrect compile error when using array access on TMP
value in function call). (ilutov)
- FPM:
. Fixed GH-12077 (PHP 8.3.0RC1 borked socket-close-on-exec.phpt).

View file

@ -31,6 +31,7 @@
typedef struct _optimizer_call_info {
zend_function *func;
zend_op *opline;
zend_op *last_check_func_arg_opline;
bool is_prototype;
bool try_inline;
uint32_t func_arg_num;
@ -235,6 +236,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (call_stack[call - 1].func_arg_num != (uint32_t)-1
&& has_known_send_mode(&call_stack[call - 1], call_stack[call - 1].func_arg_num)) {
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
/* There's no TMP specialization for FETCH_OBJ_W/FETCH_DIM_W. Avoid
* converting it and error at runtime in the FUNC_ARG variant. */
if ((opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG || opline->opcode == ZEND_FETCH_DIM_FUNC_ARG)
&& (opline->op1_type == IS_TMP_VAR || call_stack[call - 1].last_check_func_arg_opline == NULL)) {
/* Don't remove the associated CHECK_FUNC_ARG opcode. */
call_stack[call - 1].last_check_func_arg_opline = NULL;
break;
}
if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
opline->opcode -= 9;
} else {
@ -278,11 +287,21 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (has_known_send_mode(&call_stack[call - 1], opline->op2.num)) {
call_stack[call - 1].func_arg_num = opline->op2.num;
MAKE_NOP(opline);
call_stack[call - 1].last_check_func_arg_opline = opline;
}
break;
case ZEND_SEND_VAR_EX:
case ZEND_SEND_FUNC_ARG:
/* Don't transform SEND_FUNC_ARG if any FETCH opcodes weren't transformed. */
if (call_stack[call - 1].last_check_func_arg_opline == NULL) {
if (opline->op2_type == IS_CONST) {
call_stack[call - 1].try_inline = 0;
}
break;
}
MAKE_NOP(call_stack[call - 1].last_check_func_arg_opline);
call_stack[call - 1].last_check_func_arg_opline = NULL;
ZEND_FALLTHROUGH;
case ZEND_SEND_VAR_EX:
if (opline->op2_type == IS_CONST) {
call_stack[call - 1].try_inline = 0;
break;

30
Zend/tests/gh12102_1.phpt Normal file
View file

@ -0,0 +1,30 @@
--TEST--
GH-12102: Incorrect "Cannot use temporary expression in write context" error for BP_VAR_FUNC_ARG
--FILE--
<?php
function test() {
byVal(func_get_args()[0]);
try {
byRef(func_get_args()[0]);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
}
/* Intentionally declared after test() to avoid compile-time checking of ref args. */
function byVal($arg) {
var_dump($arg);
}
function byRef(&$arg) {
var_dump($arg);
}
test('y');
?>
--EXPECT--
string(1) "y"
Cannot use temporary expression in write context

43
Zend/tests/gh12102_2.phpt Normal file
View file

@ -0,0 +1,43 @@
--TEST--
GH-12102: Incorrect "Cannot use temporary expression in write context" error for BP_VAR_FUNC_ARG
--FILE--
<?php
function test() {
global $ref;
byVal(getRef()[0]);
var_dump($ref);
byRef(getRef()[0]);
var_dump($ref);
}
/* Intentionally declared after test() to avoid compile-time checking of ref args. */
function &getRef() {
global $ref;
$ref = [];
return $ref;
}
function byVal($arg) {
$arg[] = 42;
}
function byRef(&$arg) {
$arg[] = 42;
}
test();
?>
--EXPECTF--
Warning: Undefined array key 0 in %s on line %d
array(0) {
}
array(1) {
[0]=>
array(1) {
[0]=>
int(42)
}
}

32
Zend/tests/gh12102_3.phpt Normal file
View file

@ -0,0 +1,32 @@
--TEST--
GH-12102: Incorrect "Cannot use temporary expression in write context" error for BP_VAR_FUNC_ARG
--FILE--
<?php
function test() {
byVal(C[0]);
try {
byRef(C[0]);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
}
/* Intentionally declared after test() to avoid compile-time checking of ref args. */
const C = ['foo'];
function byVal($arg) {
var_dump($arg);
}
function byRef(&$arg) {
var_dump($arg);
}
test('y');
?>
--EXPECT--
string(3) "foo"
Cannot use temporary expression in write context

View file

@ -2929,7 +2929,11 @@ static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t t
static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t type) /* {{{ */
{
if (type != BP_VAR_R && type != BP_VAR_IS && zend_is_call(ast)) {
if (type != BP_VAR_R
&& type != BP_VAR_IS
/* Whether a FUNC_ARG is R may only be determined at runtime. */
&& type != BP_VAR_FUNC_ARG
&& zend_is_call(ast)) {
if (node->op_type == IS_VAR) {
zend_op *opline = zend_emit_op(NULL, ZEND_SEPARATE, node, NULL);
opline->result_type = IS_VAR;