Create reference wrappers in SEND_UNPACK if necessary

Even if we can't actually pass by reference, we still need to
create the REFERENCE wrapper to satisfy the calling convention.
The particular test case would crash with JIT, because the existence
of the reference was assumed.

Fixes oss-fuzz #39440.
This commit is contained in:
Nikita Popov 2021-09-30 14:53:47 +02:00
parent d0860f67ca
commit e11faad233
3 changed files with 46 additions and 10 deletions

View File

@ -0,0 +1,18 @@
--TEST--
Unpack iterator by reference with type check
--FILE--
<?php
function test(T &$a) {
}
function gen() {
yield null;
}
try {
test(...gen());
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECTF--
Warning: Cannot pass by-reference argument 1 of test() by unpacking a Traversable, passing by-value instead in %s on line %d
test(): Argument #1 ($a) must be of type T, null given, called in %s on line %d

View File

@ -5126,6 +5126,9 @@ ZEND_VM_C_LABEL(send_again):
break;
}
ZVAL_DEREF(arg);
Z_TRY_ADDREF_P(arg);
if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
zend_error(
E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()"
@ -5134,9 +5137,11 @@ ZEND_VM_C_LABEL(send_again):
EX(call)->func->common.scope ? "::" : "",
ZSTR_VAL(EX(call)->func->common.function_name)
);
ZVAL_NEW_REF(top, arg);
} else {
ZVAL_COPY_VALUE(top, arg);
}
ZVAL_COPY_DEREF(top, arg);
zend_string_release(name);
} else {
if (have_named_params) {
@ -5145,6 +5150,11 @@ ZEND_VM_C_LABEL(send_again):
break;
}
zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, 1);
top = ZEND_CALL_ARG(EX(call), arg_num);
ZVAL_DEREF(arg);
Z_TRY_ADDREF_P(arg);
if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
zend_error(
E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()"
@ -5153,12 +5163,11 @@ ZEND_VM_C_LABEL(send_again):
EX(call)->func->common.scope ? "::" : "",
ZSTR_VAL(EX(call)->func->common.function_name)
);
ZVAL_NEW_REF(top, arg);
} else {
ZVAL_COPY_VALUE(top, arg);
}
zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, 1);
top = ZEND_CALL_ARG(EX(call), arg_num);
ZVAL_COPY_DEREF(top, arg);
ZEND_CALL_NUM_ARGS(EX(call))++;
}

View File

@ -2248,6 +2248,9 @@ send_again:
break;
}
ZVAL_DEREF(arg);
Z_TRY_ADDREF_P(arg);
if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
zend_error(
E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()"
@ -2256,9 +2259,11 @@ send_again:
EX(call)->func->common.scope ? "::" : "",
ZSTR_VAL(EX(call)->func->common.function_name)
);
ZVAL_NEW_REF(top, arg);
} else {
ZVAL_COPY_VALUE(top, arg);
}
ZVAL_COPY_DEREF(top, arg);
zend_string_release(name);
} else {
if (have_named_params) {
@ -2267,6 +2272,11 @@ send_again:
break;
}
zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, 1);
top = ZEND_CALL_ARG(EX(call), arg_num);
ZVAL_DEREF(arg);
Z_TRY_ADDREF_P(arg);
if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
zend_error(
E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()"
@ -2275,12 +2285,11 @@ send_again:
EX(call)->func->common.scope ? "::" : "",
ZSTR_VAL(EX(call)->func->common.function_name)
);
ZVAL_NEW_REF(top, arg);
} else {
ZVAL_COPY_VALUE(top, arg);
}
zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, 1);
top = ZEND_CALL_ARG(EX(call), arg_num);
ZVAL_COPY_DEREF(top, arg);
ZEND_CALL_NUM_ARGS(EX(call))++;
}