From d29c866d442eeb0d2c585c1adccce3a7d376d14a Mon Sep 17 00:00:00 2001 From: Yuya Hamada Date: Wed, 10 Dec 2025 21:16:44 +0900 Subject: [PATCH 1/6] Fix GH-20674 mb_decode_mimeheader does not handle separator `?= =?` is skipped if long term, so skip space character. --- ext/mbstring/mbstring.c | 2 +- ext/mbstring/tests/gh20674.phpt | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 ext/mbstring/tests/gh20674.phpt diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 1d5c27a2a3815..be173495c12b9 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -6395,7 +6395,7 @@ static zend_string* mb_mime_header_decode(zend_string *input, const mbfl_encodin p = temp; /* Decoding of MIME encoded word was successful; * Try to collapse a run of whitespace */ - if (p < e && (*p == '\n' || *p == '\r')) { + if (p < e && (*p == '\n' || *p == '\r' || *p == ' ')) { do { p++; } while (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' ')); diff --git a/ext/mbstring/tests/gh20674.phpt b/ext/mbstring/tests/gh20674.phpt new file mode 100644 index 0000000000000..40f437740c20f --- /dev/null +++ b/ext/mbstring/tests/gh20674.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-20674 (mb_decode_mimeheader does not handle separator) +--EXTENSIONS-- +mbstring +--FILE-- + +--EXPECTF-- +string(11) "The PHP 8.5" From c2e0d8d6b0c3a38a5318c661e2f9e662a5d5101b Mon Sep 17 00:00:00 2001 From: Yuya Hamada Date: Thu, 11 Dec 2025 10:05:08 +0900 Subject: [PATCH 2/6] Add tab for backward compatible --- ext/mbstring/mbstring.c | 2 +- ext/mbstring/tests/gh20674.phpt | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index be173495c12b9..b9ffafa1f6e2a 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -6395,7 +6395,7 @@ static zend_string* mb_mime_header_decode(zend_string *input, const mbfl_encodin p = temp; /* Decoding of MIME encoded word was successful; * Try to collapse a run of whitespace */ - if (p < e && (*p == '\n' || *p == '\r' || *p == ' ')) { + if (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' ')) { do { p++; } while (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' ')); diff --git a/ext/mbstring/tests/gh20674.phpt b/ext/mbstring/tests/gh20674.phpt index 40f437740c20f..1218ab55753f9 100644 --- a/ext/mbstring/tests/gh20674.phpt +++ b/ext/mbstring/tests/gh20674.phpt @@ -7,6 +7,19 @@ mbstring $subject = '=?us-ascii?Q?The_PH?= =?us-ascii?Q?P_8.5?='; var_dump(mb_decode_mimeheader($subject)); + +// mb_decode_mimeheader's backward compatible for TAB(\t) +$subject = "=?us-ascii?Q?The_PH?=\t=?us-ascii?Q?P_8.5?="; +var_dump(mb_decode_mimeheader($subject)); + +$subject = "=?us-ascii?Q?The_PH?=\t =?us-ascii?Q?P_8.5?="; +var_dump(mb_decode_mimeheader($subject)); + +$subject = "=?us-ascii?Q?The_PH?= \t =?us-ascii?Q?P_8.5?="; +var_dump(mb_decode_mimeheader($subject)); ?> --EXPECTF-- string(11) "The PHP 8.5" +string(11) "The PHP 8.5" +string(11) "The PHP 8.5" +string(11) "The PHP 8.5" From 1e8b7474622112a8f772f3a7fc89ca3e2006ea5c Mon Sep 17 00:00:00 2001 From: Yuya Hamada Date: Fri, 12 Dec 2025 00:37:36 +0900 Subject: [PATCH 3/6] Add test case from RFC2047 and fix last pattern See: https://www.ietf.org/rfc/rfc2047#section-8 And fix (=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=) as (a b). --- ext/mbstring/mbstring.c | 2 ++ ext/mbstring/tests/gh20674.phpt | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index b9ffafa1f6e2a..5839945d47572 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -6402,6 +6402,8 @@ static zend_string* mb_mime_header_decode(zend_string *input, const mbfl_encodin /* We will only actually output a space if this is not immediately followed * by another valid encoded word */ space_pending = true; + } else { + space_pending = false; } continue; } diff --git a/ext/mbstring/tests/gh20674.phpt b/ext/mbstring/tests/gh20674.phpt index 1218ab55753f9..5246d113811d8 100644 --- a/ext/mbstring/tests/gh20674.phpt +++ b/ext/mbstring/tests/gh20674.phpt @@ -17,9 +17,24 @@ var_dump(mb_decode_mimeheader($subject)); $subject = "=?us-ascii?Q?The_PH?= \t =?us-ascii?Q?P_8.5?="; var_dump(mb_decode_mimeheader($subject)); + +// from RFC 2047 https://www.ietf.org/rfc/rfc2047 +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a_b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= b)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= + =?ISO-8859-1?Q?b?=)")); ?> --EXPECTF-- string(11) "The PHP 8.5" string(11) "The PHP 8.5" string(11) "The PHP 8.5" string(11) "The PHP 8.5" +string(5) "(a b)" +string(5) "(a b)" +string(4) "(ab)" +string(5) "(a b)" +string(4) "(ab)" +string(4) "(ab)" From fdd4e848d7f2d6d035b7f829066f724f031b6bc3 Mon Sep 17 00:00:00 2001 From: Yuya Hamada Date: Fri, 12 Dec 2025 11:04:04 +0900 Subject: [PATCH 4/6] Fix duplicate test case --- ext/mbstring/tests/gh20674.phpt | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/ext/mbstring/tests/gh20674.phpt b/ext/mbstring/tests/gh20674.phpt index 5246d113811d8..c1d13e3b7fad9 100644 --- a/ext/mbstring/tests/gh20674.phpt +++ b/ext/mbstring/tests/gh20674.phpt @@ -19,13 +19,7 @@ $subject = "=?us-ascii?Q?The_PH?= \t =?us-ascii?Q?P_8.5?="; var_dump(mb_decode_mimeheader($subject)); // from RFC 2047 https://www.ietf.org/rfc/rfc2047 -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)")); -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a_b?=)")); -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= b)")); -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= - =?ISO-8859-1?Q?b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?_b?=)")); ?> --EXPECTF-- string(11) "The PHP 8.5" @@ -33,8 +27,3 @@ string(11) "The PHP 8.5" string(11) "The PHP 8.5" string(11) "The PHP 8.5" string(5) "(a b)" -string(5) "(a b)" -string(4) "(ab)" -string(5) "(a b)" -string(4) "(ab)" -string(4) "(ab)" From be2b26433bdac95694d77daf73be5eac4902a9ff Mon Sep 17 00:00:00 2001 From: Yuya Hamada Date: Fri, 12 Dec 2025 11:52:20 +0900 Subject: [PATCH 5/6] Revert "Fix duplicate test case" This reverts commit fdd4e848d7f2d6d035b7f829066f724f031b6bc3. --- ext/mbstring/tests/gh20674.phpt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ext/mbstring/tests/gh20674.phpt b/ext/mbstring/tests/gh20674.phpt index c1d13e3b7fad9..5246d113811d8 100644 --- a/ext/mbstring/tests/gh20674.phpt +++ b/ext/mbstring/tests/gh20674.phpt @@ -19,7 +19,13 @@ $subject = "=?us-ascii?Q?The_PH?= \t =?us-ascii?Q?P_8.5?="; var_dump(mb_decode_mimeheader($subject)); // from RFC 2047 https://www.ietf.org/rfc/rfc2047 -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?_b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a_b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= b)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= + =?ISO-8859-1?Q?b?=)")); ?> --EXPECTF-- string(11) "The PHP 8.5" @@ -27,3 +33,8 @@ string(11) "The PHP 8.5" string(11) "The PHP 8.5" string(11) "The PHP 8.5" string(5) "(a b)" +string(5) "(a b)" +string(4) "(ab)" +string(5) "(a b)" +string(4) "(ab)" +string(4) "(ab)" From 9e12326848ecc0f13a174f853c4e127da496a4bd Mon Sep 17 00:00:00 2001 From: Yuya Hamada Date: Fri, 12 Dec 2025 11:55:10 +0900 Subject: [PATCH 6/6] Sort RFC 2047 example test case --- ext/mbstring/tests/gh20674.phpt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/mbstring/tests/gh20674.phpt b/ext/mbstring/tests/gh20674.phpt index 5246d113811d8..2fb8206037dee 100644 --- a/ext/mbstring/tests/gh20674.phpt +++ b/ext/mbstring/tests/gh20674.phpt @@ -18,11 +18,11 @@ var_dump(mb_decode_mimeheader($subject)); $subject = "=?us-ascii?Q?The_PH?= \t =?us-ascii?Q?P_8.5?="; var_dump(mb_decode_mimeheader($subject)); -// from RFC 2047 https://www.ietf.org/rfc/rfc2047 -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)")); -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a_b?=)")); -var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); +// from RFC 2047 https://www.ietf.org/rfc/rfc2047#section-8 +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?=)")); var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= b)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a_b?=)")); +var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); var_dump(mb_decode_mimeheader("(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)")); @@ -32,9 +32,9 @@ string(11) "The PHP 8.5" string(11) "The PHP 8.5" string(11) "The PHP 8.5" string(11) "The PHP 8.5" +string(3) "(a)" string(5) "(a b)" string(5) "(a b)" string(4) "(ab)" -string(5) "(a b)" string(4) "(ab)" string(4) "(ab)"