From a480965c90b4f4948f211c8b139788371e031838 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Wed, 17 Jun 2026 04:48:17 +0800 Subject: [PATCH 1/2] ext/opcache: unwrap reference wrappers in typed by-value returns (#21973) --- NEWS | 2 ++ Zend/zend_vm_def.h | 5 +++- Zend/zend_vm_execute.h | 25 +++++++++++++++---- ext/opcache/tests/opt/gh21972.phpt | 39 ++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 ext/opcache/tests/opt/gh21972.phpt diff --git a/NEWS b/NEWS index 05bdd1b35ff7..afb45fca45ca 100644 --- a/NEWS +++ b/NEWS @@ -93,6 +93,8 @@ PHP NEWS . Fix persistent free of non-persistent connect_attr key (David Carlier). - Opcache: + . Fixed bug GH-21972 (Corrupted variable type when a typed by-value return + contains a reference wrapper). (Weilin Du) . Fixed tracing JIT crash when a VM interrupt is handled during an observed user function call. (Levi Morrison) . Fixed bug GH-22004 (Assertion failure at ext/opcache/jit/zend_jit_trace.c). diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 34906e1bfcca..c46b17101546 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4387,7 +4387,7 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ref)))) { ZEND_VM_NEXT_OPCODE(); } @@ -4417,6 +4417,9 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV } retval_ptr = retval_ref; } + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + ZEND_VM_NEXT_OPCODE(); + } } SAVE_OPLINE(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index d4b2aff1907b..f4f35ce2a8a9 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -10772,7 +10772,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ref)))) { ZEND_VM_NEXT_OPCODE(); } @@ -10802,6 +10802,9 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP } retval_ptr = retval_ref; } + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + ZEND_VM_NEXT_OPCODE(); + } } SAVE_OPLINE(); @@ -21517,7 +21520,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ref)))) { ZEND_VM_NEXT_OPCODE(); } @@ -21547,6 +21550,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN } retval_ptr = retval_ref; } + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + ZEND_VM_NEXT_OPCODE(); + } } SAVE_OPLINE(); @@ -30016,7 +30022,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ref)))) { ZEND_VM_NEXT_OPCODE(); } @@ -30046,6 +30052,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN } retval_ptr = retval_ref; } + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + ZEND_VM_NEXT_OPCODE(); + } } SAVE_OPLINE(); @@ -37830,7 +37839,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ref)))) { ZEND_VM_NEXT_OPCODE(); } @@ -37860,6 +37869,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED } retval_ptr = retval_ref; } + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + ZEND_VM_NEXT_OPCODE(); + } } SAVE_OPLINE(); @@ -50642,7 +50654,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ref)))) { ZEND_VM_NEXT_OPCODE(); } @@ -50672,6 +50684,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU } retval_ptr = retval_ref; } + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + ZEND_VM_NEXT_OPCODE(); + } } SAVE_OPLINE(); diff --git a/ext/opcache/tests/opt/gh21972.phpt b/ext/opcache/tests/opt/gh21972.phpt new file mode 100644 index 000000000000..f34f9b45ccf2 --- /dev/null +++ b/ext/opcache/tests/opt/gh21972.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-21972: Typed by-value return must not leak reference wrapper +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +string(6) "string" +string(5) "false" +string(5) "false" From a2a90538f792980e46cbbb579b3d67c040fc7976 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 16 Jun 2026 15:33:43 -0400 Subject: [PATCH 2/2] Fix MIME charset sniffing advancing by name length not value length php_libxml_sniff_charset_from_string() advanced the parse cursor by the parameter name length after collecting an unquoted parameter value (WHATWG mime-sniff step 11.9.1), instead of the value length. When a Content-Type parameter before charset had a name and value of different lengths, the cursor misaligned and the charset parameter was missed, so document loading fell back to the wrong encoding. Closes GH-22343 --- .../html/encoding/HTMLDocument_createFromFile_http_header.phpt | 2 ++ ext/libxml/mime_sniff.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_http_header.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_http_header.phpt index 5c602b87f23e..5164ac68041d 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_http_header.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_createFromFile_http_header.phpt @@ -46,6 +46,7 @@ $tests = [ "text/html; ;; ; ;; Charset=\"ISO-8859-1\"", "text/html;Charset=\"ISO-8859-1", "tex.t/h#\$%!&'*%2B-.^_`|~tml;Charset=\"ISO-8859-1\"", // Note: have to encode + as 2B because of implementation details of http_server() + "text/html; abcd=ef;charset=ISO-8859-1", ], "Valid input, but invalid encoding name" => [ "text/html;Charset=\"ISO-8859-1\\", @@ -100,6 +101,7 @@ foreach ($tests as $name => $headers) { äöü äöü äöü +äöü --- Valid input, but invalid encoding name --- ��� ��� diff --git a/ext/libxml/mime_sniff.c b/ext/libxml/mime_sniff.c index 0ca032f9b795..2840c69701fc 100644 --- a/ext/libxml/mime_sniff.c +++ b/ext/libxml/mime_sniff.c @@ -273,7 +273,7 @@ PHP_LIBXML_API zend_string *php_libxml_sniff_charset_from_string(const char *sta /* 11.9.1. Set parameterValue to the result of collecting a sequence of code points that are not ';' */ size_t parameter_value_length = collect_a_sequence_of_code_points(start, end, is_not_semicolon); parameter_value = zend_string_init(start, parameter_value_length, false); - start += parameter_name_length; + start += parameter_value_length; /* 11.9.2. Remove trailing HTTP whitespace from parameterValue */ while (ZSTR_LEN(parameter_value) > 0 && is_http_whitespace(ZSTR_VAL(parameter_value)[ZSTR_LEN(parameter_value) - 1])) {