Commit 1f4b1699b72 for php.net

commit 1f4b1699b72834d2420933ff28f4cb1b03c28e82
Author: David Carlier <devnexen@gmail.com>
Date:   Tue Apr 7 20:16:38 2026 +0100

    Fix GH-21664: iconv_mime_decode/iconv_mime_encode bailout corrupts EG(bailout).

    close GH-21666

diff --git a/NEWS b/NEWS
index 45d5a171d6a..4abaa254acb 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,8 @@ PHP                                                                        NEWS

 - Iconv:
   . Fixed bug GH-17399 (iconv memory leak on bailout). (iliaal)
+  . Fixed bug GH-21664 (iconv_mime_encode()/iconv_mime_decode()
+    crash with corrupt bailout state. (David Carlier)

 - Opcache:
   . Fixed bug GH-21158 (JIT: Assertion jit->ra[var].flags & (1<<0) failed in
diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c
index 117a9e948f6..29998c37d1a 100644
--- a/ext/iconv/iconv.c
+++ b/ext/iconv/iconv.c
@@ -1030,7 +1030,7 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn

 						if (out_size <= out_reserved) {
 							err = PHP_ICONV_ERR_TOO_BIG;
-							goto out;
+							goto out_try;
 						}

 						out_left = out_size - out_reserved;
@@ -1039,22 +1039,22 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
 							switch (errno) {
 								case EINVAL:
 									err = PHP_ICONV_ERR_ILLEGAL_CHAR;
-									goto out;
+									goto out_try;

 								case EILSEQ:
 									err = PHP_ICONV_ERR_ILLEGAL_SEQ;
-									goto out;
+									goto out_try;

 								case E2BIG:
 									if (prev_in_left == in_left) {
 										err = PHP_ICONV_ERR_TOO_BIG;
-										goto out;
+										goto out_try;
 									}
 									break;

 								default:
 									err = PHP_ICONV_ERR_UNKNOWN;
-									goto out;
+									goto out_try;
 							}
 						}

@@ -1063,7 +1063,7 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
 						if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
 							if (errno != E2BIG) {
 								err = PHP_ICONV_ERR_UNKNOWN;
-								goto out;
+								goto out_try;
 							}
 						} else {
 							break;
@@ -1071,7 +1071,7 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn

 						if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
 							err = PHP_ICONV_ERR_UNKNOWN;
-							goto out;
+							goto out_try;
 						}

 						out_reserved += 4;
@@ -1086,7 +1086,7 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
 					if (char_cnt < ZSTR_LEN(encoded)) {
 						/* something went wrong! */
 						err = PHP_ICONV_ERR_UNKNOWN;
-						goto out;
+						goto out_try;
 					}

 					smart_str_appendl(pretval, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
@@ -1123,28 +1123,28 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
 							switch (errno) {
 								case EINVAL:
 									err = PHP_ICONV_ERR_ILLEGAL_CHAR;
-									goto out;
+									goto out_try;

 								case EILSEQ:
 									err = PHP_ICONV_ERR_ILLEGAL_SEQ;
-									goto out;
+									goto out_try;

 								case E2BIG:
 									if (prev_in_left == in_left) {
 										err = PHP_ICONV_ERR_UNKNOWN;
-										goto out;
+										goto out_try;
 									}
 									break;

 								default:
 									err = PHP_ICONV_ERR_UNKNOWN;
-									goto out;
+									goto out_try;
 							}
 						}
 						if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
 							if (errno != E2BIG) {
 								err = PHP_ICONV_ERR_UNKNOWN;
-								goto out;
+								goto out_try;
 							}
 						}

@@ -1179,7 +1179,7 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn

 					if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
 						err = PHP_ICONV_ERR_UNKNOWN;
-						goto out;
+						goto out_try;
 					}

 				} break; /* case PHP_ICONV_ENC_SCHEME_QPRINT: */
@@ -1187,6 +1187,8 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
 		} while (in_left > 0);

 		smart_str_0(pretval);
+
+out_try: ;
 	} zend_catch {
 		bailout = true;
 	} zend_end_try();
@@ -1278,7 +1280,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 								if (mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR) {
 									err = PHP_ICONV_ERR_SUCCESS;
 								} else {
-									goto out;
+									goto out_try;
 								}
 							}
 							encoded_word = NULL;
@@ -1296,7 +1298,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 						}
 						err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
 						if (err != PHP_ICONV_ERR_SUCCESS) {
-							goto out;
+							goto out_try;
 						}
 						encoded_word = NULL;
 						if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
@@ -1326,7 +1328,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 							_php_iconv_appendc(pretval, '?', cd_pl);
 							err = _php_iconv_appendl(pretval, csname, (size_t)((p1 + 1) - csname), cd_pl);
 							if (err != PHP_ICONV_ERR_SUCCESS) {
-								goto out;
+								goto out_try;
 							}
 							csname = NULL;
 							if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
@@ -1342,7 +1344,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st

 						if (csname == NULL) {
 							err = PHP_ICONV_ERR_MALFORMED;
-							goto out;
+							goto out_try;
 						}

 						csname_len = (size_t)(p1 - csname);
@@ -1351,7 +1353,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 							if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
 								err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
 								if (err != PHP_ICONV_ERR_SUCCESS) {
-									goto out;
+									goto out_try;
 								}
 								encoded_word = NULL;
 								if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
@@ -1362,7 +1364,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 								break;
 							} else {
 								err = PHP_ICONV_ERR_MALFORMED;
-								goto out;
+								goto out_try;
 							}
 						}

@@ -1409,7 +1411,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st

 								err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
 								if (err != PHP_ICONV_ERR_SUCCESS) {
-									goto out;
+									goto out_try;
 								}

 								/* Let's go back and see if there are further
@@ -1423,7 +1425,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 								} else {
 									err = PHP_ICONV_ERR_CONVERTER;
 								}
-								goto out;
+								goto out_try;
 							}
 						}
 					}
@@ -1447,7 +1449,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 							if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
 								err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
 								if (err != PHP_ICONV_ERR_SUCCESS) {
-									goto out;
+									goto out_try;
 								}
 								encoded_word = NULL;
 								if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
@@ -1458,7 +1460,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 								break;
 							} else {
 								err = PHP_ICONV_ERR_MALFORMED;
-								goto out;
+								goto out_try;
 							}
 					}
 					break;
@@ -1469,7 +1471,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 							/* pass the entire chunk through the converter */
 							err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
 							if (err != PHP_ICONV_ERR_SUCCESS) {
-								goto out;
+								goto out_try;
 							}
 							encoded_word = NULL;
 							if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
@@ -1480,7 +1482,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 							break;
 						} else {
 							err = PHP_ICONV_ERR_MALFORMED;
-							goto out;
+							goto out_try;
 						}
 					}
 					encoded_text = p1 + 1;
@@ -1525,7 +1527,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 							/* pass the entire chunk through the converter */
 							err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
 							if (err != PHP_ICONV_ERR_SUCCESS) {
-								goto out;
+								goto out_try;
 							}
 							encoded_word = NULL;
 							if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
@@ -1536,7 +1538,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 							break;
 						} else {
 							err = PHP_ICONV_ERR_MALFORMED;
-							goto out;
+							goto out_try;
 						}
 					}
 					scan_stat = 9;
@@ -1564,7 +1566,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 									/* pass the entire chunk through the converter */
 									err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
 									if (err != PHP_ICONV_ERR_SUCCESS) {
-										goto out;
+										goto out_try;
 									}
 									scan_stat = 12;
 									break;
@@ -1593,7 +1595,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 									/* pass the entire chunk through the converter */
 									err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
 									if (err != PHP_ICONV_ERR_SUCCESS) {
-										goto out;
+										goto out_try;
 									}
 									encoded_word = NULL;
 									if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
@@ -1604,7 +1606,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 									break;
 								} else {
 									err = PHP_ICONV_ERR_UNKNOWN;
-									goto out;
+									goto out_try;
 								}
 							}

@@ -1623,7 +1625,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 										break;
 									}
 								} else {
-									goto out;
+									goto out_try;
 								}
 							}

@@ -1744,7 +1746,7 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 					err = PHP_ICONV_ERR_SUCCESS;
 				} else {
 					err = PHP_ICONV_ERR_MALFORMED;
-					goto out;
+					goto out_try;
 				}
 		}

@@ -1753,6 +1755,8 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
 		}

 		smart_str_0(pretval);
+
+out_try: ;
 	} zend_catch {
 		bailout = true;
 	} zend_end_try();
diff --git a/ext/iconv/tests/gh21664.phpt b/ext/iconv/tests/gh21664.phpt
new file mode 100644
index 00000000000..92c66049c8d
--- /dev/null
+++ b/ext/iconv/tests/gh21664.phpt
@@ -0,0 +1,21 @@
+--TEST--
+GH-21664 (iconv_mime_decode/iconv_mime_encode bailout corruption)
+--EXTENSIONS--
+iconv
+--FILE--
+<?php
+$r = iconv_mime_decode("=?utf-8?Q?" . chr(0xA1) . "?= .");
+var_dump($r);
+
+$r = iconv_mime_encode("Subject", "\x80", ["input-charset" => "UTF-8", "output-charset" => "UTF-8"]);
+var_dump($r);
+
+echo "Done\n";
+?>
+--EXPECTF--
+Notice: iconv_mime_decode(): Detected an illegal character in input string in %s on line %d
+bool(false)
+
+Notice: iconv_mime_encode(): Detected an illegal character in input string in %s on line %d
+bool(false)
+Done