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