From e1ab7875bb10ca7162bce9c0e0165298656f05c0 Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:05:35 +0100 Subject: [PATCH 1/2] Fix GH-25313: Assertion !EG(exception) in ZEND_FE_FREE when unpacking a generator into a by-ref parameter throws The warning didn't have an exception check after it. I placed the exception check immediately after the warning so that the happy-flow isn't affected, this required some restructuring of the code as well. --- Zend/tests/arg_unpack/gh25313.phpt | 17 +++++++++++++++++ Zend/zend_vm_def.h | 14 ++++++++++---- Zend/zend_vm_execute.h | 14 ++++++++++---- 3 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 Zend/tests/arg_unpack/gh25313.phpt diff --git a/Zend/tests/arg_unpack/gh25313.phpt b/Zend/tests/arg_unpack/gh25313.phpt new file mode 100644 index 000000000000..d3f19a509ec1 --- /dev/null +++ b/Zend/tests/arg_unpack/gh25313.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-25313 (Assertion !EG(exception) in ZEND_FE_FREE when unpacking a generator into a by-ref parameter throws) +--CREDITS-- +YuanchengJiang +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +Exception: Cannot pass by-reference argument 4 of test() by unpacking a Traversable, passing by-value instead diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index f9432cb65856..4d5301ad0233 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5356,6 +5356,7 @@ ZEND_VM_C_LABEL(send_again): Z_TRY_ADDREF_P(arg); if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + ZVAL_NEW_REF(top, arg); zend_error( E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()" " by unpacking a Traversable, passing by-value instead", arg_num, @@ -5363,7 +5364,10 @@ ZEND_VM_C_LABEL(send_again): EX(call)->func->common.scope ? "::" : "", ZSTR_VAL(EX(call)->func->common.function_name) ); - ZVAL_NEW_REF(top, arg); + if (UNEXPECTED(EG(exception) != NULL)) { + zend_string_release(name); + break; + } } else { ZVAL_COPY_VALUE(top, arg); } @@ -5380,8 +5384,10 @@ ZEND_VM_C_LABEL(send_again): top = ZEND_CALL_ARG(EX(call), arg_num); ZVAL_DEREF(arg); Z_TRY_ADDREF_P(arg); + ZEND_CALL_NUM_ARGS(EX(call))++; if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + ZVAL_NEW_REF(top, arg); zend_error( E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()" " by unpacking a Traversable, passing by-value instead", arg_num, @@ -5389,12 +5395,12 @@ ZEND_VM_C_LABEL(send_again): EX(call)->func->common.scope ? "::" : "", ZSTR_VAL(EX(call)->func->common.function_name) ); - ZVAL_NEW_REF(top, arg); + if (UNEXPECTED(EG(exception) != NULL)) { + break; + } } else { ZVAL_COPY_VALUE(top, arg); } - - ZEND_CALL_NUM_ARGS(EX(call))++; } funcs->move_forward(iter); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 3137c55eac0d..812402c79312 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2440,6 +2440,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_ Z_TRY_ADDREF_P(arg); if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + ZVAL_NEW_REF(top, arg); zend_error( E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()" " by unpacking a Traversable, passing by-value instead", arg_num, @@ -2447,7 +2448,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_ EX(call)->func->common.scope ? "::" : "", ZSTR_VAL(EX(call)->func->common.function_name) ); - ZVAL_NEW_REF(top, arg); + if (UNEXPECTED(EG(exception) != NULL)) { + zend_string_release(name); + break; + } } else { ZVAL_COPY_VALUE(top, arg); } @@ -2464,8 +2468,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_ top = ZEND_CALL_ARG(EX(call), arg_num); ZVAL_DEREF(arg); Z_TRY_ADDREF_P(arg); + ZEND_CALL_NUM_ARGS(EX(call))++; if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + ZVAL_NEW_REF(top, arg); zend_error( E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()" " by unpacking a Traversable, passing by-value instead", arg_num, @@ -2473,12 +2479,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_ EX(call)->func->common.scope ? "::" : "", ZSTR_VAL(EX(call)->func->common.function_name) ); - ZVAL_NEW_REF(top, arg); + if (UNEXPECTED(EG(exception) != NULL)) { + break; + } } else { ZVAL_COPY_VALUE(top, arg); } - - ZEND_CALL_NUM_ARGS(EX(call))++; } funcs->move_forward(iter); From c7e1ed2d4f38fd4f84d598b4479d26684898f64c Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 9 Nov 2026 19:04:49 +0100 Subject: [PATCH 2/2] extend test --- Zend/tests/arg_unpack/gh25313.phpt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Zend/tests/arg_unpack/gh25313.phpt b/Zend/tests/arg_unpack/gh25313.phpt index d3f19a509ec1..072a4f2aa71c 100644 --- a/Zend/tests/arg_unpack/gh25313.phpt +++ b/Zend/tests/arg_unpack/gh25313.phpt @@ -5,13 +5,26 @@ YuanchengJiang --FILE-- getMessage(), "\n"; } + +try { + test(...gen(['a' => 1, 'b' => 2]), ...gen(['c' => 3, 'ref' => 4])); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} ?> --EXPECT-- Exception: Cannot pass by-reference argument 4 of test() by unpacking a Traversable, passing by-value instead +Exception: Cannot pass by-reference argument 4 of test() by unpacking a Traversable, passing by-value instead