Skip to content

Commit

Permalink
Make call_user_func() on reference args consistent
Browse files Browse the repository at this point in the history
Previously reference arguments were allowed if call_user_func()
was compiled to SEND_USER and not otherwise. Make it consistent
by always forbidding them.
  • Loading branch information
nikic committed Jun 28, 2016
1 parent 76370f3 commit fafe01b
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 71 deletions.
28 changes: 28 additions & 0 deletions Zend/tests/call_user_func_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
call_user_func() should error on reference arguments
--FILE--
<?php

namespace Foo;

function bar(&$ref) {
$ref = 24;
}

$x = 42;
$ref =& $x;
\call_user_func('Foo\bar', $x);
var_dump($x);

$y = 42;
$ref =& $y;
call_user_func('Foo\bar', $y);
var_dump($y);

?>
--EXPECTF--
Warning: Parameter 1 to Foo\bar() expected to be a reference, value given in %s on line %d
int(42)

Warning: Parameter 1 to Foo\bar() expected to be a reference, value given in %s on line %d
int(42)
43 changes: 19 additions & 24 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -4654,32 +4654,27 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
arg = GET_OP1_ZVAL_PTR(BP_VAR_R);
param = ZEND_CALL_VAR(EX(call), opline->result.var);

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

zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
opline->op2.num,
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
EX(call)->func->common.scope ? "::" : "",
ZSTR_VAL(EX(call)->func->common.function_name));

if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (Z_OBJ(EX(call)->This)) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
ZVAL_UNDEF(param);
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);

FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (Z_OBJ(EX(call)->This)) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
ZVAL_UNDEF(param);
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);

FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
if (Z_ISREF_P(arg) &&
!(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
Expand Down
84 changes: 37 additions & 47 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -15358,32 +15358,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_VAR_HANDLER(ZEN
arg = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
param = ZEND_CALL_VAR(EX(call), opline->result.var);

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

zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
opline->op2.num,
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
EX(call)->func->common.scope ? "::" : "",
ZSTR_VAL(EX(call)->func->common.function_name));

if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (Z_OBJ(EX(call)->This)) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
ZVAL_UNDEF(param);
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);

zval_ptr_dtor_nogc(free_op1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (Z_OBJ(EX(call)->This)) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
ZVAL_UNDEF(param);
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);

zval_ptr_dtor_nogc(free_op1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
if (Z_ISREF_P(arg) &&
!(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
Expand Down Expand Up @@ -28980,31 +28975,26 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_CV_HANDLER(ZEND
arg = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
param = ZEND_CALL_VAR(EX(call), opline->result.var);

if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {

zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
opline->op2.num,
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
EX(call)->func->common.scope ? "::" : "",
ZSTR_VAL(EX(call)->func->common.function_name));

if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (Z_OBJ(EX(call)->This)) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
ZVAL_UNDEF(param);
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
if (UNEXPECTED(ARG_MUST_BE_SENT_BY_REF(EX(call)->func, opline->op2.num))) {
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
opline->op2.num,
EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
EX(call)->func->common.scope ? "::" : "",
ZSTR_VAL(EX(call)->func->common.function_name));

ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
if (Z_OBJ(EX(call)->This)) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
ZVAL_UNDEF(param);
EX(call)->func = (zend_function*)&zend_pass_function;
EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);

ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
if (Z_ISREF_P(arg) &&
!(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
Expand Down

0 comments on commit fafe01b

Please sign in to comment.