Bug #29890 (crash if error handler fails)
--FILE--
<?php
-function customErrorHandler($fErrNo,$fErrStr,$fErrFile,$fErrLine,&$fClass) {
+function customErrorHandler($fErrNo,$fErrStr,$fErrFile,$fErrLine,$fClass) {
echo "error :".$fErrStr."\n";
}
zend_fcall_info_cache fci_cache_local;
zend_function *func;
zend_class_entry *orig_scope;
- zval tmp;
ZVAL_UNDEF(fci->retval);
for (i=0; i<fci->param_count; i++) {
zval *param;
+ zval *arg = &fci->params[i];
if (ARG_SHOULD_BE_SENT_BY_REF(func, i + 1)) {
- // TODO: Scalar values don't have reference counters anymore.
- // They are assumed to be 1, and they may be easily passed by
- // reference now. However, previously scalars with refcount==1
- // might be passed and with refcount>1 might not. We can support
- // only single behavior ???
-#if 0
- if (Z_REFCOUNTED(fci->params[i]) &&
- // This solution breaks the following test (omit warning message) ???
- // Zend/tests/bug61273.phpt
- // ext/reflection/tests/bug42976.phpt
- // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
-#else
- if (!Z_REFCOUNTED(fci->params[i]) ||
- // This solution breaks the following test (emit warning message) ???
- // ext/pdo_sqlite/tests/pdo_005.phpt
-#endif
- (!Z_ISREF(fci->params[i]) && Z_REFCOUNT(fci->params[i]) > 1)) {
-
+ if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (fci->no_separation &&
!ARG_MAY_BE_SENT_BY_REF(func, i + 1)) {
if (i) {
return FAILURE;
}
- if (Z_REFCOUNTED(fci->params[i])) {
- Z_DELREF(fci->params[i]);
- }
- ZVAL_DUP(&tmp, &fci->params[i]);
- ZVAL_NEW_REF(&fci->params[i], &tmp);
- Z_ADDREF(fci->params[i]);
- } else if (!Z_ISREF(fci->params[i])) {
- ZVAL_NEW_REF(&fci->params[i], &fci->params[i]);
- Z_ADDREF(fci->params[i]);
- } else if (Z_REFCOUNTED(fci->params[i])) {
- Z_ADDREF(fci->params[i]);
+ ZVAL_NEW_REF(arg, arg);
}
- param = ZEND_CALL_ARG(call, i+1);
- ZVAL_COPY_VALUE(param, &fci->params[i]);
- } else if (Z_ISREF(fci->params[i]) &&
- /* don't separate references for __call */
- (func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 ) {
- param = ZEND_CALL_ARG(call, i+1);
- ZVAL_DUP(param, Z_REFVAL(fci->params[i]));
+ Z_ADDREF_P(arg);
} else {
- param = ZEND_CALL_ARG(call, i+1);
- ZVAL_COPY(param, &fci->params[i]);
+ if (Z_ISREF_P(arg) &&
+ (func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 ) {
+ /* don't separate references for __call */
+ arg = Z_REFVAL_P(arg);
+ }
+ if (Z_OPT_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
}
+ param = ZEND_CALL_ARG(call, i+1);
+ ZVAL_COPY_VALUE(param, arg);
}
EG(scope) = calling_scope;
Z_TYPE_INFO_P(__z) = IS_REFERENCE_EX; \
} while (0)
+#define ZVAL_NEW_EMPTY_REF(z) do { \
+ zend_reference *_ref = emalloc(sizeof(zend_reference)); \
+ GC_REFCOUNT(_ref) = 1; \
+ GC_TYPE_INFO(_ref) = IS_REFERENCE; \
+ Z_REF_P(z) = _ref; \
+ Z_TYPE_INFO_P(z) = IS_REFERENCE_EX; \
+ } while (0)
+
#define ZVAL_NEW_REF(z, r) do { \
zend_reference *_ref = emalloc(sizeof(zend_reference)); \
GC_REFCOUNT(_ref) = 1; \
} else {
uint32_t arg_num;
HashTable *ht;
- zval *arg, *param, tmp;
+ zval *arg, *param;
ZEND_VM_C_LABEL(send_array):
ht = Z_ARRVAL_P(args);
param = ZEND_CALL_ARG(EX(call), 1);
ZEND_HASH_FOREACH_VAL(ht, arg) {
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
- // TODO: Scalar values don't have reference counters anymore.
- // They are assumed to be 1, and they may be easily passed by
- // reference now. However, previously scalars with refcount==1
- // might be passed and with refcount>1 might not. We can support
- // only single behavior ???
-#if 0
- if (Z_REFCOUNTED_P(arg) &&
- // This solution breaks the following test (omit warning message) ???
- // Zend/tests/bug61273.phpt
- // ext/reflection/tests/bug42976.phpt
- // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
-#else
- if (!Z_REFCOUNTED_P(arg) ||
- // This solution breaks the following test (emit warning message) ???
- // ext/pdo_sqlite/tests/pdo_005.phpt
-#endif
- (!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
-
+ if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
break;
}
- if (Z_REFCOUNTED_P(arg)) {
- Z_DELREF_P(arg);
- }
- ZVAL_DUP(&tmp, arg);
- ZVAL_NEW_REF(arg, &tmp);
- Z_ADDREF_P(arg);
- } else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
- Z_ADDREF_P(arg);
- } else if (Z_REFCOUNTED_P(arg)) {
+ }
+ Z_ADDREF_P(arg);
+ } else{
+ if (Z_ISREF_P(arg) &&
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ /* don't separate references for __call */
+ arg = Z_REFVAL_P(arg);
+ }
+ if (Z_OPT_REFCOUNTED_P(arg)) {
Z_ADDREF_P(arg);
}
- ZVAL_COPY_VALUE(param, arg);
- } else if (Z_ISREF_P(arg) &&
- /* don't separate references for __call */
- (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
- ZVAL_DUP(param, Z_REFVAL_P(arg));
- } else {
- ZVAL_COPY(param, arg);
}
+ ZVAL_COPY_VALUE(param, arg);
ZEND_CALL_NUM_ARGS(EX(call))++;
arg_num++;
param++;
ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
{
USE_OPLINE
- zval *arg, *param, tmp;
+ zval *arg, *param;
zend_free_op free_op1;
SAVE_OPLINE();
param = ZEND_CALL_VAR(EX(call), opline->result.var);
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
- // TODO: Scalar values don't have reference counters anymore.
- // They are assumed to be 1, and they may be easily passed by
- // reference now. However, previously scalars with refcount==1
- // might be passed and with refcount>1 might not. We can support
- // only single behavior ???
-#if 0
- if (Z_REFCOUNTED_P(arg) &&
- // This solution breaks the following test (omit warning message) ???
- // Zend/tests/bug61273.phpt
- // ext/reflection/tests/bug42976.phpt
- // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
-#else
- if (!Z_REFCOUNTED_P(arg) ||
- // This solution breaks the following test (emit warning message) ???
- // ext/pdo_sqlite/tests/pdo_005.phpt
-#endif
- (!Z_ISREF_P(arg) /*&& Z_REFCOUNT_P(arg) > 1???*/)) {
+ if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
ZEND_VM_NEXT_OPCODE();
}
- if (Z_REFCOUNTED_P(arg)) {
- Z_DELREF_P(arg);
- }
- ZVAL_DUP(&tmp, arg);
- ZVAL_NEW_REF(arg, &tmp);
- Z_ADDREF_P(arg);
- } else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
- Z_ADDREF_P(arg);
- } else if (Z_REFCOUNTED_P(arg)) {
- Z_ADDREF_P(arg);
}
- ZVAL_COPY_VALUE(param, arg);
- } else if (Z_ISREF_P(arg) &&
- /* don't separate references for __call */
- (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
- ZVAL_DUP(param, Z_REFVAL_P(arg));
+ Z_ADDREF_P(arg);
} else {
- ZVAL_COPY(param, arg);
+ if (Z_ISREF_P(arg) &&
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ /* don't separate references for __call */
+ arg = Z_REFVAL_P(arg);
+ }
+ if (Z_OPT_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
}
+ ZVAL_COPY_VALUE(param, arg);
FREE_OP1();
CHECK_EXCEPTION();
} else {
uint32_t arg_num;
HashTable *ht;
- zval *arg, *param, tmp;
+ zval *arg, *param;
send_array:
ht = Z_ARRVAL_P(args);
param = ZEND_CALL_ARG(EX(call), 1);
ZEND_HASH_FOREACH_VAL(ht, arg) {
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
- // TODO: Scalar values don't have reference counters anymore.
- // They are assumed to be 1, and they may be easily passed by
- // reference now. However, previously scalars with refcount==1
- // might be passed and with refcount>1 might not. We can support
- // only single behavior ???
-#if 0
- if (Z_REFCOUNTED_P(arg) &&
- // This solution breaks the following test (omit warning message) ???
- // Zend/tests/bug61273.phpt
- // ext/reflection/tests/bug42976.phpt
- // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
-#else
- if (!Z_REFCOUNTED_P(arg) ||
- // This solution breaks the following test (emit warning message) ???
- // ext/pdo_sqlite/tests/pdo_005.phpt
-#endif
- (!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
-
+ if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
break;
}
- if (Z_REFCOUNTED_P(arg)) {
- Z_DELREF_P(arg);
- }
- ZVAL_DUP(&tmp, arg);
- ZVAL_NEW_REF(arg, &tmp);
- Z_ADDREF_P(arg);
- } else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
- Z_ADDREF_P(arg);
- } else if (Z_REFCOUNTED_P(arg)) {
+ }
+ Z_ADDREF_P(arg);
+ } else{
+ if (Z_ISREF_P(arg) &&
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ /* don't separate references for __call */
+ arg = Z_REFVAL_P(arg);
+ }
+ if (Z_OPT_REFCOUNTED_P(arg)) {
Z_ADDREF_P(arg);
}
- ZVAL_COPY_VALUE(param, arg);
- } else if (Z_ISREF_P(arg) &&
- /* don't separate references for __call */
- (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
- ZVAL_DUP(param, Z_REFVAL_P(arg));
- } else {
- ZVAL_COPY(param, arg);
}
+ ZVAL_COPY_VALUE(param, arg);
ZEND_CALL_NUM_ARGS(EX(call))++;
arg_num++;
param++;
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval *arg, *param, tmp;
+ zval *arg, *param;
zend_free_op free_op1;
SAVE_OPLINE();
param = ZEND_CALL_VAR(EX(call), opline->result.var);
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
- // TODO: Scalar values don't have reference counters anymore.
- // They are assumed to be 1, and they may be easily passed by
- // reference now. However, previously scalars with refcount==1
- // might be passed and with refcount>1 might not. We can support
- // only single behavior ???
-#if 0
- if (Z_REFCOUNTED_P(arg) &&
- // This solution breaks the following test (omit warning message) ???
- // Zend/tests/bug61273.phpt
- // ext/reflection/tests/bug42976.phpt
- // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
-#else
- if (!Z_REFCOUNTED_P(arg) ||
- // This solution breaks the following test (emit warning message) ???
- // ext/pdo_sqlite/tests/pdo_005.phpt
-#endif
- (!Z_ISREF_P(arg) /*&& Z_REFCOUNT_P(arg) > 1???*/)) {
+ if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
ZEND_VM_NEXT_OPCODE();
}
- if (Z_REFCOUNTED_P(arg)) {
- Z_DELREF_P(arg);
- }
- ZVAL_DUP(&tmp, arg);
- ZVAL_NEW_REF(arg, &tmp);
- Z_ADDREF_P(arg);
- } else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
- Z_ADDREF_P(arg);
- } else if (Z_REFCOUNTED_P(arg)) {
- Z_ADDREF_P(arg);
}
- ZVAL_COPY_VALUE(param, arg);
- } else if (Z_ISREF_P(arg) &&
- /* don't separate references for __call */
- (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
- ZVAL_DUP(param, Z_REFVAL_P(arg));
+ Z_ADDREF_P(arg);
} else {
- ZVAL_COPY(param, arg);
+ if (Z_ISREF_P(arg) &&
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ /* don't separate references for __call */
+ arg = Z_REFVAL_P(arg);
+ }
+ if (Z_OPT_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
}
+ ZVAL_COPY_VALUE(param, arg);
zval_ptr_dtor_nogc(free_op1);
CHECK_EXCEPTION();
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval *arg, *param, tmp;
+ zval *arg, *param;
SAVE_OPLINE();
param = ZEND_CALL_VAR(EX(call), opline->result.var);
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
- // TODO: Scalar values don't have reference counters anymore.
- // They are assumed to be 1, and they may be easily passed by
- // reference now. However, previously scalars with refcount==1
- // might be passed and with refcount>1 might not. We can support
- // only single behavior ???
-#if 0
- if (Z_REFCOUNTED_P(arg) &&
- // This solution breaks the following test (omit warning message) ???
- // Zend/tests/bug61273.phpt
- // ext/reflection/tests/bug42976.phpt
- // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
-#else
- if (!Z_REFCOUNTED_P(arg) ||
- // This solution breaks the following test (emit warning message) ???
- // ext/pdo_sqlite/tests/pdo_005.phpt
-#endif
- (!Z_ISREF_P(arg) /*&& Z_REFCOUNT_P(arg) > 1???*/)) {
+ if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
ZEND_VM_NEXT_OPCODE();
}
- if (Z_REFCOUNTED_P(arg)) {
- Z_DELREF_P(arg);
- }
- ZVAL_DUP(&tmp, arg);
- ZVAL_NEW_REF(arg, &tmp);
- Z_ADDREF_P(arg);
- } else if (!Z_ISREF_P(arg)) {
ZVAL_NEW_REF(arg, arg);
- Z_ADDREF_P(arg);
- } else if (Z_REFCOUNTED_P(arg)) {
- Z_ADDREF_P(arg);
}
- ZVAL_COPY_VALUE(param, arg);
- } else if (Z_ISREF_P(arg) &&
- /* don't separate references for __call */
- (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
- ZVAL_DUP(param, Z_REFVAL_P(arg));
+ Z_ADDREF_P(arg);
} else {
- ZVAL_COPY(param, arg);
+ if (Z_ISREF_P(arg) &&
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ /* don't separate references for __call */
+ arg = Z_REFVAL_P(arg);
+ }
+ if (Z_OPT_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
}
+ ZVAL_COPY_VALUE(param, arg);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
{
spl_array_object *intern = Z_SPLARRAY_P(getThis());
HashTable *aht = spl_array_get_hash_table(intern, 0);
- zval tmp, *arg = NULL;
- zval retval;
+ zval function_name, params[2], *arg = NULL;
uint32_t old_refcount;
+ ZVAL_STRINGL(&function_name, fname, fname_len);
+
/* A tricky way to pass "aht" by reference, reset refcount */
//??? It may be not safe, if user comparison handler accesses "aht"
old_refcount = GC_REFCOUNT(aht);
GC_REFCOUNT(aht) = 1;
- ZVAL_ARR(&tmp, aht);
+ ZVAL_NEW_EMPTY_REF(¶ms[0]);
+ ZVAL_ARR(Z_REFVAL(params[0]), aht);
if (!use_arg) {
aht->u.v.nApplyCount++;
- zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval, 1, &tmp, NULL);
+ call_user_function_ex(EG(function_table), NULL, &function_name, return_value, 1, params, 1, NULL);
aht->u.v.nApplyCount--;
} else if (use_arg == SPL_ARRAY_METHOD_MAY_USER_ARG) {
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "|z", &arg) == FAILURE) {
- GC_REFCOUNT(aht) = old_refcount;
zend_throw_exception(spl_ce_BadMethodCallException, "Function expects one argument at most", 0);
- return;
+ goto exit;
+ }
+ if (arg) {
+ ZVAL_COPY_VALUE(¶ms[1], arg);
}
aht->u.v.nApplyCount++;
- zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval, arg? 2 : 1, &tmp, arg);
+ call_user_function_ex(EG(function_table), NULL, &function_name, return_value, arg ? 2 : 1, params, 1, NULL);
aht->u.v.nApplyCount--;
} else {
if (ZEND_NUM_ARGS() != 1 || zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
- GC_REFCOUNT(aht) = old_refcount;
zend_throw_exception(spl_ce_BadMethodCallException, "Function expects exactly one argument", 0);
- return;
+ goto exit;
}
+ ZVAL_COPY_VALUE(¶ms[1], arg);
aht->u.v.nApplyCount++;
- zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval, 2, &tmp, arg);
+ call_user_function_ex(EG(function_table), NULL, &function_name, return_value, 2, params, 1, NULL);
aht->u.v.nApplyCount--;
}
+
+exit:
/* A tricky way to pass "aht" by reference, copy back and cleanup */
- if (Z_ISREF(tmp) && Z_TYPE_P(Z_REFVAL(tmp))) {
- *aht = *Z_ARRVAL_P(Z_REFVAL(tmp));
- GC_REMOVE_FROM_BUFFER(Z_ARR_P(Z_REFVAL(tmp)));
- efree(Z_REF(tmp));
- }
GC_REFCOUNT(aht) = old_refcount;
- if (!Z_ISUNDEF(retval)) {
- ZVAL_COPY_VALUE(return_value, &retval);
- }
+ efree(Z_REF(params[0]));
+ zend_string_free(Z_STR(function_name));
} /* }}} */
#define SPL_ARRAY_METHOD(cname, fname, use_arg) \
[0]=>
&string(2) "v1"
[1]=>
- &array(4) {
- ["k1"]=>
- &string(2) "v1"
- ["k2"]=>
- string(2) "v2"
- [0]=>
- &string(2) "v1"
- [1]=>
- *RECURSION*
- }
+ *RECURSION*
}
}
}