From c4859b86a93918f86cd00cc9774f96b34d2a5af1 Mon Sep 17 00:00:00 2001 From: PurHur Date: Sat, 18 Jul 2026 23:23:43 +0000 Subject: [PATCH] Stdlib: curl_escape/unescape(null) TypeError on PROFILE=8.4 (#20695) Use Z_PARAM_STR coerce/lower paths so null rejects under forward 8.4 like urlencode, with AOT early-return to avoid linking rawurlencode after abort. Co-authored-by: Cursor --- ext/curl/curl_escape.php | 13 ++++++++-- ext/curl/curl_unescape.php | 13 ++++++++-- .../cases/stdlib/curl_escape_null.phpt | 15 ++++++++++++ .../stdlib/curl_escape_null_forward84.phpt | 23 ++++++++++++++++++ .../curl_escape_null_forward84_jit.phpt | 24 +++++++++++++++++++ .../aot/cases/curl_escape_null_forward84.phpt | 12 ++++++++++ 6 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 test/compliance/cases/stdlib/curl_escape_null.phpt create mode 100644 test/compliance/cases/stdlib/curl_escape_null_forward84.phpt create mode 100644 test/compliance/cases/stdlib/curl_escape_null_forward84_jit.phpt create mode 100644 test/fixtures/aot/cases/curl_escape_null_forward84.phpt diff --git a/ext/curl/curl_escape.php b/ext/curl/curl_escape.php index eb2addf4abf..2f00c70fa1e 100644 --- a/ext/curl/curl_escape.php +++ b/ext/curl/curl_escape.php @@ -37,7 +37,8 @@ public function execute(Frame $frame): void if (null === $frame->returnVar) { return; } - $value = VmString::coerceStringBuiltinArg( + // Z_PARAM_STR — null TypeError on 8.4 forward profile (#20695, ext/curl/interface.c) + $value = VmString::coerceZparamStrBuiltinArg( $frame->calledArgs[1], 'curl_escape', 1, @@ -56,7 +57,15 @@ public function call(Context $context, JITVariable ...$args): Value } // Handle is required for php-src-strict arity/type; encoding matches curl_easy_escape // unreserved set (rawurlencode) and does not depend on handle state. - $str = JitStringBuiltinArg::lower($context, $args[1], 'curl_escape', 1, 'string'); + // Z_PARAM_STR — null TypeError on 8.4 forward profile (#20695). + // Early-return like urlencode: after TypeError+abort, do not link rawurlencode (#20695 AOT). + if ( + (JITVariable::TYPE_NULL === $args[1]->type || ($args[1]->isNullConstant ?? false)) + && ($context->callerStrictTypes || JitStringBuiltinArg::requiresZparamStrStrictNullOnForwardProfile()) + ) { + return JitStringBuiltinArg::lowerZparamStr($context, $args[1], 'curl_escape', 1, 'string'); + } + $str = JitStringBuiltinArg::lowerZparamStr($context, $args[1], 'curl_escape', 1, 'string'); return JitUrlencode::rawurlencode($context, $str); } diff --git a/ext/curl/curl_unescape.php b/ext/curl/curl_unescape.php index e0650ffa783..ed6e0f6c418 100644 --- a/ext/curl/curl_unescape.php +++ b/ext/curl/curl_unescape.php @@ -37,7 +37,8 @@ public function execute(Frame $frame): void if (null === $frame->returnVar) { return; } - $value = VmString::coerceStringBuiltinArg( + // Z_PARAM_STR — null TypeError on 8.4 forward profile (#20695, ext/curl/interface.c) + $value = VmString::coerceZparamStrBuiltinArg( $frame->calledArgs[1], 'curl_unescape', 1, @@ -54,7 +55,15 @@ public function call(Context $context, JITVariable ...$args): Value \count($args) )); } - $str = JitStringBuiltinArg::lower($context, $args[1], 'curl_unescape', 1, 'string'); + // Z_PARAM_STR — null TypeError on 8.4 forward profile (#20695). + // Early-return like urlencode: after TypeError+abort, do not link rawurldecode (#20695 AOT). + if ( + (JITVariable::TYPE_NULL === $args[1]->type || ($args[1]->isNullConstant ?? false)) + && ($context->callerStrictTypes || JitStringBuiltinArg::requiresZparamStrStrictNullOnForwardProfile()) + ) { + return JitStringBuiltinArg::lowerZparamStr($context, $args[1], 'curl_unescape', 1, 'string'); + } + $str = JitStringBuiltinArg::lowerZparamStr($context, $args[1], 'curl_unescape', 1, 'string'); return JitUrlencode::rawurldecode($context, $str); } diff --git a/test/compliance/cases/stdlib/curl_escape_null.phpt b/test/compliance/cases/stdlib/curl_escape_null.phpt new file mode 100644 index 00000000000..e6fabf6aa8f --- /dev/null +++ b/test/compliance/cases/stdlib/curl_escape_null.phpt @@ -0,0 +1,15 @@ +--TEST-- +stdlib curl_escape()/curl_unescape(null) — coerce under 8.2 profile (#20695, ext/curl/interface.c) +--ENV-- +PHP_COMPILER_PROFILE=8.2 +--FILE-- + +--EXPECT-- +'' +'' diff --git a/test/compliance/cases/stdlib/curl_escape_null_forward84.phpt b/test/compliance/cases/stdlib/curl_escape_null_forward84.phpt new file mode 100644 index 00000000000..1b83be68192 --- /dev/null +++ b/test/compliance/cases/stdlib/curl_escape_null_forward84.phpt @@ -0,0 +1,23 @@ +--TEST-- +stdlib curl_escape()/curl_unescape(null) — TypeError on 8.4 forward profile (#20695, ext/curl/interface.c) +--ENV-- +PHP_COMPILER_PROFILE=8.4 +--FILE-- +getMessage(), "\n"; +} +try { + curl_unescape($ch, null); + echo "unescape_uncaught\n"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +curl_escape(): Argument #2 ($string) must be of type string, null given +curl_unescape(): Argument #2 ($string) must be of type string, null given diff --git a/test/compliance/cases/stdlib/curl_escape_null_forward84_jit.phpt b/test/compliance/cases/stdlib/curl_escape_null_forward84_jit.phpt new file mode 100644 index 00000000000..7dab2746151 --- /dev/null +++ b/test/compliance/cases/stdlib/curl_escape_null_forward84_jit.phpt @@ -0,0 +1,24 @@ +--TEST-- +stdlib curl_escape()/curl_unescape(null) — TypeError on 8.4 forward profile JIT (#20695, ext/curl/interface.c) +--ENV-- +PHP_COMPILER_PROFILE=8.4 +--JIT-- +--FILE-- +getMessage(), "\n"; +} +try { + curl_unescape($ch, null); + echo "unescape_uncaught\n"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +curl_escape(): Argument #2 ($string) must be of type string, null given +curl_unescape(): Argument #2 ($string) must be of type string, null given diff --git a/test/fixtures/aot/cases/curl_escape_null_forward84.phpt b/test/fixtures/aot/cases/curl_escape_null_forward84.phpt new file mode 100644 index 00000000000..82b3abe5b6c --- /dev/null +++ b/test/fixtures/aot/cases/curl_escape_null_forward84.phpt @@ -0,0 +1,12 @@ +--TEST-- +AOT: curl_escape(null) — TypeError on 8.4 forward profile (#20695, ext/curl/interface.c) +--ENV-- +PHP_COMPILER_PROFILE=8.4 +--FILE-- +