From c838ae34a8bcd9da2cdb41548cf260cb40361aac Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 11 Jun 2026 18:25:53 +0200 Subject: [PATCH] Advise trusting the input in the allow_named_closures refusal message deepclone_to_array() refuses to serialize a closure over a named callable unless allow_named_closures is set. The message now quotes the option and adds that it should be enabled only for trusted input, since a by-name payload can mint a Closure over any function or method of that name. This mirrors the polyfill's wording (symfony/polyfill#635), which additionally points at this extension; the extension does not suggest installing itself. --- deepclone.c | 2 +- tests/deepclone_attribute_provenance.phpt | 2 +- tests/deepclone_from_array.phpt | 2 +- tests/deepclone_named_closure_optin.phpt | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deepclone.c b/deepclone.c index 3ce45d5..d53ca97 100644 --- a/deepclone.c +++ b/deepclone.c @@ -2100,7 +2100,7 @@ static void dc_copy_value(dc_ctx *ctx, zval *src, zval *dst, zval *mask_dst) * ends must enable. */ if (func && (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE)) { if (!ctx->allow_named_closures) { - zend_value_error("deepclone_to_array(): serializing a closure over the named callable \"%s\" requires enabling the allow_named_closures option", ZSTR_VAL(func->common.function_name)); + zend_value_error("deepclone_to_array(): serializing a closure over the named callable \"%s\" requires enabling the \"allow_named_closures\" option; do it only if you trust the input", ZSTR_VAL(func->common.function_name)); return; } if (!dc_class_allowed(ctx->allowed_ht, zend_ce_closure->name)) { diff --git a/tests/deepclone_attribute_provenance.phpt b/tests/deepclone_attribute_provenance.phpt index 978fcc2..387e0b5 100644 --- a/tests/deepclone_attribute_provenance.phpt +++ b/tests/deepclone_attribute_provenance.phpt @@ -99,5 +99,5 @@ bool(true) bool(true) bool(true) == 5. a callable no attribute declares stays by-name (needs the opt-in) == -uncaptured: ValueError: deepclone_to_array(): serializing a closure over the named callable "loose" requires enabling the allow_named_closures option +uncaptured: ValueError: deepclone_to_array(): serializing a closure over the named callable "loose" requires enabling the "allow_named_closures" option; do it only if you trust the input Done diff --git a/tests/deepclone_from_array.phpt b/tests/deepclone_from_array.phpt index bc93c4c..aa245f5 100644 --- a/tests/deepclone_from_array.phpt +++ b/tests/deepclone_from_array.phpt @@ -70,7 +70,7 @@ try { deepclone_to_array(strlen(...)); echo "no throw\n"; } catch (ValueError $e) { - var_dump($e->getMessage() === 'deepclone_to_array(): serializing a closure over the named callable "strlen" requires enabling the allow_named_closures option'); + var_dump($e->getMessage() === 'deepclone_to_array(): serializing a closure over the named callable "strlen" requires enabling the "allow_named_closures" option; do it only if you trust the input'); } $clone = deepclone_from_array(deepclone_to_array(strlen(...), allow_named_closures: true), allow_named_closures: true); var_dump($clone('hello') === 5); diff --git a/tests/deepclone_named_closure_optin.phpt b/tests/deepclone_named_closure_optin.phpt index bf8baab..3cc236a 100644 --- a/tests/deepclone_named_closure_optin.phpt +++ b/tests/deepclone_named_closure_optin.phpt @@ -89,8 +89,8 @@ bool(true) bool(true) bool(true) == 3. a runtime named closure refuses to_array without the opt-in == -strlen: ValueError: deepclone_to_array(): serializing a closure over the named callable "strlen" requires enabling the allow_named_closures option -Helper::pub: ValueError: deepclone_to_array(): serializing a closure over the named callable "pub" requires enabling the allow_named_closures option +strlen: ValueError: deepclone_to_array(): serializing a closure over the named callable "strlen" requires enabling the "allow_named_closures" option; do it only if you trust the input +Helper::pub: ValueError: deepclone_to_array(): serializing a closure over the named callable "pub" requires enabling the "allow_named_closures" option; do it only if you trust the input == 4. with the opt-in on both ends it round-trips by name == bool(true) bool(true)