Skip to content

Commit fafe01b

Browse files
committed
Make call_user_func() on reference args consistent
Previously reference arguments were allowed if call_user_func() was compiled to SEND_USER and not otherwise. Make it consistent by always forbidding them.
1 parent 76370f3 commit fafe01b

File tree

3 files changed

+84
-71
lines changed

3 files changed

+84
-71
lines changed

Zend/tests/call_user_func_006.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
call_user_func() should error on reference arguments
3+
--FILE--
4+
<?php
5+
6+
namespace Foo;
7+
8+
function bar(&$ref) {
9+
$ref = 24;
10+
}
11+
12+
$x = 42;
13+
$ref =& $x;
14+
\call_user_func('Foo\bar', $x);
15+
var_dump($x);
16+
17+
$y = 42;
18+
$ref =& $y;
19+
call_user_func('Foo\bar', $y);
20+
var_dump($y);
21+
22+
?>
23+
--EXPECTF--
24+
Warning: Parameter 1 to Foo\bar() expected to be a reference, value given in %s on line %d
25+
int(42)
26+
27+
Warning: Parameter 1 to Foo\bar() expected to be a reference, value given in %s on line %d
28+
int(42)

Zend/zend_vm_def.h

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4654,32 +4654,27 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
46544654
arg = GET_OP1_ZVAL_PTR(BP_VAR_R);
46554655
param = ZEND_CALL_VAR(EX(call), opline->result.var);
46564656

4657-
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
4658-
if (UNEXPECTED(!Z_ISREF_P(arg))) {
4659-
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
4657+
if (UNEXPECTED(ARG_MUST_BE_SENT_BY_REF(EX(call)->func, opline->op2.num))) {
4658+
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
4659+
opline->op2.num,
4660+
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
4661+
EX(call)->func->common.scope ? "::" : "",
4662+
ZSTR_VAL(EX(call)->func->common.function_name));
46604663

4661-
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
4662-
opline->op2.num,
4663-
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
4664-
EX(call)->func->common.scope ? "::" : "",
4665-
ZSTR_VAL(EX(call)->func->common.function_name));
4666-
4667-
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
4668-
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
4669-
}
4670-
if (Z_OBJ(EX(call)->This)) {
4671-
OBJ_RELEASE(Z_OBJ(EX(call)->This));
4672-
}
4673-
ZVAL_UNDEF(param);
4674-
EX(call)->func = (zend_function*)&zend_pass_function;
4675-
EX(call)->called_scope = NULL;
4676-
Z_OBJ(EX(call)->This) = NULL;
4677-
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
4678-
4679-
FREE_OP1();
4680-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
4681-
}
4664+
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
4665+
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
46824666
}
4667+
if (Z_OBJ(EX(call)->This)) {
4668+
OBJ_RELEASE(Z_OBJ(EX(call)->This));
4669+
}
4670+
ZVAL_UNDEF(param);
4671+
EX(call)->func = (zend_function*)&zend_pass_function;
4672+
EX(call)->called_scope = NULL;
4673+
Z_OBJ(EX(call)->This) = NULL;
4674+
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
4675+
4676+
FREE_OP1();
4677+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
46834678
} else {
46844679
if (Z_ISREF_P(arg) &&
46854680
!(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {

Zend/zend_vm_execute.h

Lines changed: 37 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15358,32 +15358,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_VAR_HANDLER(ZEN
1535815358
arg = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
1535915359
param = ZEND_CALL_VAR(EX(call), opline->result.var);
1536015360

15361-
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
15362-
if (UNEXPECTED(!Z_ISREF_P(arg))) {
15363-
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
15361+
if (UNEXPECTED(ARG_MUST_BE_SENT_BY_REF(EX(call)->func, opline->op2.num))) {
15362+
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
15363+
opline->op2.num,
15364+
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
15365+
EX(call)->func->common.scope ? "::" : "",
15366+
ZSTR_VAL(EX(call)->func->common.function_name));
1536415367

15365-
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
15366-
opline->op2.num,
15367-
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
15368-
EX(call)->func->common.scope ? "::" : "",
15369-
ZSTR_VAL(EX(call)->func->common.function_name));
15370-
15371-
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
15372-
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
15373-
}
15374-
if (Z_OBJ(EX(call)->This)) {
15375-
OBJ_RELEASE(Z_OBJ(EX(call)->This));
15376-
}
15377-
ZVAL_UNDEF(param);
15378-
EX(call)->func = (zend_function*)&zend_pass_function;
15379-
EX(call)->called_scope = NULL;
15380-
Z_OBJ(EX(call)->This) = NULL;
15381-
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
15382-
15383-
zval_ptr_dtor_nogc(free_op1);
15384-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
15385-
}
15368+
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
15369+
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
1538615370
}
15371+
if (Z_OBJ(EX(call)->This)) {
15372+
OBJ_RELEASE(Z_OBJ(EX(call)->This));
15373+
}
15374+
ZVAL_UNDEF(param);
15375+
EX(call)->func = (zend_function*)&zend_pass_function;
15376+
EX(call)->called_scope = NULL;
15377+
Z_OBJ(EX(call)->This) = NULL;
15378+
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
15379+
15380+
zval_ptr_dtor_nogc(free_op1);
15381+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
1538715382
} else {
1538815383
if (Z_ISREF_P(arg) &&
1538915384
!(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
@@ -28980,31 +28975,26 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_CV_HANDLER(ZEND
2898028975
arg = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
2898128976
param = ZEND_CALL_VAR(EX(call), opline->result.var);
2898228977

28983-
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
28984-
if (UNEXPECTED(!Z_ISREF_P(arg))) {
28985-
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
28986-
28987-
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
28988-
opline->op2.num,
28989-
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
28990-
EX(call)->func->common.scope ? "::" : "",
28991-
ZSTR_VAL(EX(call)->func->common.function_name));
28992-
28993-
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
28994-
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
28995-
}
28996-
if (Z_OBJ(EX(call)->This)) {
28997-
OBJ_RELEASE(Z_OBJ(EX(call)->This));
28998-
}
28999-
ZVAL_UNDEF(param);
29000-
EX(call)->func = (zend_function*)&zend_pass_function;
29001-
EX(call)->called_scope = NULL;
29002-
Z_OBJ(EX(call)->This) = NULL;
29003-
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
28978+
if (UNEXPECTED(ARG_MUST_BE_SENT_BY_REF(EX(call)->func, opline->op2.num))) {
28979+
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
28980+
opline->op2.num,
28981+
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
28982+
EX(call)->func->common.scope ? "::" : "",
28983+
ZSTR_VAL(EX(call)->func->common.function_name));
2900428984

29005-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
29006-
}
28985+
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
28986+
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
2900728987
}
28988+
if (Z_OBJ(EX(call)->This)) {
28989+
OBJ_RELEASE(Z_OBJ(EX(call)->This));
28990+
}
28991+
ZVAL_UNDEF(param);
28992+
EX(call)->func = (zend_function*)&zend_pass_function;
28993+
EX(call)->called_scope = NULL;
28994+
Z_OBJ(EX(call)->This) = NULL;
28995+
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
28996+
28997+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
2900828998
} else {
2900928999
if (Z_ISREF_P(arg) &&
2901029000
!(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {

0 commit comments

Comments
 (0)