Commit 38628e89a54 for php.net
commit 38628e89a545c06e1425aa639b1d624baacf938a
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Thu Mar 26 18:05:28 2026 -0400
Fix GH-17399: iconv memory leak on bailout
Wrap bailable sections in php_iconv_string(), _php_iconv_substr(),
_php_iconv_mime_encode(), and _php_iconv_mime_decode() with zend_try/zend_catch
to ensure iconv handles allocated via system malloc are closed if a Zend OOM
bailout fires during smart_str or zend_string operations.
Fixes GH-17399
Closes GH-21541
diff --git a/NEWS b/NEWS
index dc0e430775f..0e042bb864c 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,9 @@ PHP NEWS
. Fixed bug GH-19983 (GC assertion failure with fibers, generators and
destructors). (iliaal)
+- Iconv:
+ . Fixed bug GH-17399 (iconv memory leak on bailout). (iliaal)
+
- SPL:
. Fixed bug GH-21499 (RecursiveArrayIterator getChildren UAF after parent
free). (Girgias)
diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c
index ba74eddd012..117a9e948f6 100644
--- a/ext/iconv/iconv.c
+++ b/ext/iconv/iconv.c
@@ -459,59 +459,65 @@ PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len,
out_left = in_len + 32; /* Avoid realloc() most cases */
out_size = 0;
bsz = out_left;
- out_buf = zend_string_alloc(bsz, 0);
- out_p = ZSTR_VAL(out_buf);
-
- while (in_left > 0) {
- result = iconv(cd, (ICONV_CONST char **) &in_p, &in_left, (char **) &out_p, &out_left);
- out_size = bsz - out_left;
- if (result == (size_t)(-1)) {
- if (ignore_ilseq && errno == EILSEQ) {
- if (in_left <= 1) {
- result = 0;
- } else {
- errno = 0;
- in_p++;
- in_left--;
- continue;
+
+ zend_try {
+ out_buf = zend_string_alloc(bsz, 0);
+ out_p = ZSTR_VAL(out_buf);
+
+ while (in_left > 0) {
+ result = iconv(cd, (ICONV_CONST char **) &in_p, &in_left, (char **) &out_p, &out_left);
+ out_size = bsz - out_left;
+ if (result == (size_t)(-1)) {
+ if (ignore_ilseq && errno == EILSEQ) {
+ if (in_left <= 1) {
+ result = 0;
+ } else {
+ errno = 0;
+ in_p++;
+ in_left--;
+ continue;
+ }
}
- }
- if (errno == E2BIG && in_left > 0) {
- /* converted string is longer than out buffer */
- bsz += in_len;
+ if (errno == E2BIG && in_left > 0) {
+ /* converted string is longer than out buffer */
+ bsz += in_len;
- out_buf = zend_string_extend(out_buf, bsz, 0);
- out_p = ZSTR_VAL(out_buf);
- out_p += out_size;
- out_left = bsz - out_size;
- continue;
+ out_buf = zend_string_extend(out_buf, bsz, 0);
+ out_p = ZSTR_VAL(out_buf);
+ out_p += out_size;
+ out_left = bsz - out_size;
+ continue;
+ }
}
+ break;
}
- break;
- }
- if (result != (size_t)(-1)) {
- /* flush the shift-out sequences */
- for (;;) {
- result = iconv(cd, NULL, NULL, (char **) &out_p, &out_left);
- out_size = bsz - out_left;
+ if (result != (size_t)(-1)) {
+ /* flush the shift-out sequences */
+ for (;;) {
+ result = iconv(cd, NULL, NULL, (char **) &out_p, &out_left);
+ out_size = bsz - out_left;
- if (result != (size_t)(-1)) {
- break;
- }
+ if (result != (size_t)(-1)) {
+ break;
+ }
- if (errno == E2BIG) {
- bsz += 16;
- out_buf = zend_string_extend(out_buf, bsz, 0);
- out_p = ZSTR_VAL(out_buf);
- out_p += out_size;
- out_left = bsz - out_size;
- } else {
- break;
+ if (errno == E2BIG) {
+ bsz += 16;
+ out_buf = zend_string_extend(out_buf, bsz, 0);
+ out_p = ZSTR_VAL(out_buf);
+ out_p += out_size;
+ out_left = bsz - out_size;
+ } else {
+ break;
+ }
}
}
- }
+ } zend_catch {
+ iconv_close(cd);
+ zend_bailout();
+ } zend_end_try();
iconv_close(cd);
@@ -684,58 +690,63 @@ static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
errno = 0;
more = nbytes > 0 && len > 0;
- for (in_p = str, in_left = nbytes, cnt = 0; more; ++cnt) {
- out_p = buf;
- out_left = sizeof(buf);
+ bool bailout = false;
+ zend_try {
+ for (in_p = str, in_left = nbytes, cnt = 0; more; ++cnt) {
+ out_p = buf;
+ out_left = sizeof(buf);
- more = in_left > 0 && len > 0;
+ more = in_left > 0 && len > 0;
- iconv(cd1, more ? (ICONV_CONST char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
- if (out_left == sizeof(buf)) {
- break;
- }
+ iconv(cd1, more ? (ICONV_CONST char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
+ if (out_left == sizeof(buf)) {
+ break;
+ }
- if ((zend_long)cnt >= offset) {
- if (cd2 == (iconv_t)NULL) {
- cd2 = iconv_open(enc, GENERIC_SUPERSET_NAME);
+ if ((zend_long)cnt >= offset) {
+ if (cd2 == (iconv_t)NULL) {
+ cd2 = iconv_open(enc, GENERIC_SUPERSET_NAME);
- if (cd2 == (iconv_t)(-1)) {
- cd2 = (iconv_t)NULL;
- if (errno == EINVAL) {
- err = PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- err = PHP_ICONV_ERR_CONVERTER;
+ if (cd2 == (iconv_t)(-1)) {
+ cd2 = (iconv_t)NULL;
+ if (errno == EINVAL) {
+ err = PHP_ICONV_ERR_WRONG_CHARSET;
+ } else {
+ err = PHP_ICONV_ERR_CONVERTER;
+ }
+ break;
}
+ }
+
+ if (_php_iconv_appendl(pretval, buf, sizeof(buf), cd2) != PHP_ICONV_ERR_SUCCESS) {
break;
}
+ --len;
}
- if (_php_iconv_appendl(pretval, buf, sizeof(buf), cd2) != PHP_ICONV_ERR_SUCCESS) {
- break;
- }
- --len;
}
- }
-
- switch (errno) {
- case EINVAL:
- err = PHP_ICONV_ERR_ILLEGAL_CHAR;
- break;
+ switch (errno) {
+ case EINVAL:
+ err = PHP_ICONV_ERR_ILLEGAL_CHAR;
+ break;
- case EILSEQ:
- err = PHP_ICONV_ERR_ILLEGAL_SEQ;
- break;
+ case EILSEQ:
+ err = PHP_ICONV_ERR_ILLEGAL_SEQ;
+ break;
- case E2BIG:
- break;
- }
- if (err == PHP_ICONV_ERR_SUCCESS) {
- if (cd2 != (iconv_t)NULL) {
- _php_iconv_appendl(pretval, NULL, 0, cd2);
+ case E2BIG:
+ break;
}
- smart_str_0(pretval);
- }
+ if (err == PHP_ICONV_ERR_SUCCESS) {
+ if (cd2 != (iconv_t)NULL) {
+ _php_iconv_appendl(pretval, NULL, 0, cd2);
+ }
+ smart_str_0(pretval);
+ }
+ } zend_catch {
+ bailout = true;
+ } zend_end_try();
if (cd1 != (iconv_t)NULL) {
iconv_close(cd1);
@@ -744,6 +755,9 @@ static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
if (cd2 != (iconv_t)NULL) {
iconv_close(cd2);
}
+ if (bailout) {
+ zend_bailout();
+ }
return err;
}
@@ -904,6 +918,7 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
{
php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
+ bool bailout = false;
size_t char_cnt = 0;
size_t out_charset_len;
size_t lfchars_len;
@@ -962,215 +977,219 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
goto out;
}
- buf = safe_emalloc(1, max_line_len, 5);
+ zend_try {
+ buf = safe_emalloc(1, max_line_len, 5);
- char_cnt = max_line_len;
+ char_cnt = max_line_len;
- _php_iconv_appendl(pretval, fname, fname_nbytes, cd_pl);
- char_cnt -= fname_nbytes;
- smart_str_appendl(pretval, ": ", sizeof(": ") - 1);
- char_cnt -= 2;
+ _php_iconv_appendl(pretval, fname, fname_nbytes, cd_pl);
+ char_cnt -= fname_nbytes;
+ smart_str_appendl(pretval, ": ", sizeof(": ") - 1);
+ char_cnt -= 2;
- in_p = fval;
- in_left = fval_nbytes;
+ in_p = fval;
+ in_left = fval_nbytes;
- do {
- size_t prev_in_left;
- size_t out_size;
- size_t encoded_word_min_len = sizeof("=\?\?X\?\?=")-1 + out_charset_len + (enc_scheme == PHP_ICONV_ENC_SCHEME_BASE64 ? 4 : 3);
+ do {
+ size_t prev_in_left;
+ size_t out_size;
+ size_t encoded_word_min_len = sizeof("=\?\?X\?\?=")-1 + out_charset_len + (enc_scheme == PHP_ICONV_ENC_SCHEME_BASE64 ? 4 : 3);
- if (char_cnt < encoded_word_min_len + lfchars_len + 1) {
- /* lfchars must be encoded in ASCII here*/
- smart_str_appendl(pretval, lfchars, lfchars_len);
- smart_str_appendc(pretval, ' ');
- char_cnt = max_line_len - 1;
- }
+ if (char_cnt < encoded_word_min_len + lfchars_len + 1) {
+ /* lfchars must be encoded in ASCII here*/
+ smart_str_appendl(pretval, lfchars, lfchars_len);
+ smart_str_appendc(pretval, ' ');
+ char_cnt = max_line_len - 1;
+ }
- smart_str_appendl(pretval, "=?", sizeof("=?") - 1);
- char_cnt -= 2;
- smart_str_appendl(pretval, out_charset, out_charset_len);
- char_cnt -= out_charset_len;
- smart_str_appendc(pretval, '?');
- char_cnt --;
+ smart_str_appendl(pretval, "=?", sizeof("=?") - 1);
+ char_cnt -= 2;
+ smart_str_appendl(pretval, out_charset, out_charset_len);
+ char_cnt -= out_charset_len;
+ smart_str_appendc(pretval, '?');
+ char_cnt --;
- switch (enc_scheme) {
- case PHP_ICONV_ENC_SCHEME_BASE64: {
- size_t ini_in_left;
- const char *ini_in_p;
- size_t out_reserved = 4;
+ switch (enc_scheme) {
+ case PHP_ICONV_ENC_SCHEME_BASE64: {
+ size_t ini_in_left;
+ const char *ini_in_p;
+ size_t out_reserved = 4;
- smart_str_appendc(pretval, 'B');
- char_cnt--;
- smart_str_appendc(pretval, '?');
- char_cnt--;
+ smart_str_appendc(pretval, 'B');
+ char_cnt--;
+ smart_str_appendc(pretval, '?');
+ char_cnt--;
- prev_in_left = ini_in_left = in_left;
- ini_in_p = in_p;
+ prev_in_left = ini_in_left = in_left;
+ ini_in_p = in_p;
- out_size = (char_cnt - 2) / 4 * 3;
+ out_size = (char_cnt - 2) / 4 * 3;
- for (;;) {
- out_p = buf;
+ for (;;) {
+ out_p = buf;
- if (out_size <= out_reserved) {
- err = PHP_ICONV_ERR_TOO_BIG;
- goto out;
- }
+ if (out_size <= out_reserved) {
+ err = PHP_ICONV_ERR_TOO_BIG;
+ goto out;
+ }
- out_left = out_size - out_reserved;
+ out_left = out_size - out_reserved;
- if (iconv(cd, (ICONV_CONST char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
- switch (errno) {
- case EINVAL:
- err = PHP_ICONV_ERR_ILLEGAL_CHAR;
- goto out;
+ if (iconv(cd, (ICONV_CONST char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
+ switch (errno) {
+ case EINVAL:
+ err = PHP_ICONV_ERR_ILLEGAL_CHAR;
+ goto out;
- case EILSEQ:
- err = PHP_ICONV_ERR_ILLEGAL_SEQ;
- goto out;
+ case EILSEQ:
+ err = PHP_ICONV_ERR_ILLEGAL_SEQ;
+ goto out;
+
+ case E2BIG:
+ if (prev_in_left == in_left) {
+ err = PHP_ICONV_ERR_TOO_BIG;
+ goto out;
+ }
+ break;
- case E2BIG:
- if (prev_in_left == in_left) {
- err = PHP_ICONV_ERR_TOO_BIG;
+ default:
+ err = PHP_ICONV_ERR_UNKNOWN;
goto out;
- }
- break;
+ }
+ }
- default:
+ out_left += out_reserved;
+
+ if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
+ if (errno != E2BIG) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
+ }
+ } else {
+ break;
}
- }
-
- out_left += out_reserved;
- if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
- if (errno != E2BIG) {
+ if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
- } else {
- break;
- }
- if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
- err = PHP_ICONV_ERR_UNKNOWN;
- goto out;
+ out_reserved += 4;
+ in_left = ini_in_left;
+ in_p = ini_in_p;
}
- out_reserved += 4;
- in_left = ini_in_left;
- in_p = ini_in_p;
- }
+ prev_in_left = in_left;
- prev_in_left = in_left;
+ encoded = php_base64_encode((unsigned char *) buf, (out_size - out_left));
- encoded = php_base64_encode((unsigned char *) buf, (out_size - out_left));
+ if (char_cnt < ZSTR_LEN(encoded)) {
+ /* something went wrong! */
+ err = PHP_ICONV_ERR_UNKNOWN;
+ goto out;
+ }
- if (char_cnt < ZSTR_LEN(encoded)) {
- /* something went wrong! */
- err = PHP_ICONV_ERR_UNKNOWN;
- goto out;
- }
+ smart_str_appendl(pretval, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
+ char_cnt -= ZSTR_LEN(encoded);
+ smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
+ char_cnt -= 2;
- smart_str_appendl(pretval, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
- char_cnt -= ZSTR_LEN(encoded);
- smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
- char_cnt -= 2;
+ zend_string_release_ex(encoded, 0);
+ encoded = NULL;
+ } break; /* case PHP_ICONV_ENC_SCHEME_BASE64: */
- zend_string_release_ex(encoded, 0);
- encoded = NULL;
- } break; /* case PHP_ICONV_ENC_SCHEME_BASE64: */
+ case PHP_ICONV_ENC_SCHEME_QPRINT: {
+ size_t ini_in_left;
+ const char *ini_in_p;
+ const unsigned char *p;
+ size_t nbytes_required;
- case PHP_ICONV_ENC_SCHEME_QPRINT: {
- size_t ini_in_left;
- const char *ini_in_p;
- const unsigned char *p;
- size_t nbytes_required;
+ smart_str_appendc(pretval, 'Q');
+ char_cnt--;
+ smart_str_appendc(pretval, '?');
+ char_cnt--;
- smart_str_appendc(pretval, 'Q');
- char_cnt--;
- smart_str_appendc(pretval, '?');
- char_cnt--;
+ prev_in_left = ini_in_left = in_left;
+ ini_in_p = in_p;
- prev_in_left = ini_in_left = in_left;
- ini_in_p = in_p;
+ for (out_size = (char_cnt - 2); out_size > 0;) {
- for (out_size = (char_cnt - 2); out_size > 0;) {
+ nbytes_required = 0;
- nbytes_required = 0;
+ out_p = buf;
+ out_left = out_size;
- out_p = buf;
- out_left = out_size;
+ if (iconv(cd, (ICONV_CONST char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
+ switch (errno) {
+ case EINVAL:
+ err = PHP_ICONV_ERR_ILLEGAL_CHAR;
+ goto out;
- if (iconv(cd, (ICONV_CONST char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
- switch (errno) {
- case EINVAL:
- err = PHP_ICONV_ERR_ILLEGAL_CHAR;
- goto out;
+ case EILSEQ:
+ err = PHP_ICONV_ERR_ILLEGAL_SEQ;
+ goto out;
- case EILSEQ:
- err = PHP_ICONV_ERR_ILLEGAL_SEQ;
- goto out;
+ case E2BIG:
+ if (prev_in_left == in_left) {
+ err = PHP_ICONV_ERR_UNKNOWN;
+ goto out;
+ }
+ break;
- case E2BIG:
- if (prev_in_left == in_left) {
+ default:
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
- }
- break;
-
- default:
+ }
+ }
+ if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
+ if (errno != E2BIG) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
+ }
}
- }
- if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
- if (errno != E2BIG) {
- err = PHP_ICONV_ERR_UNKNOWN;
- goto out;
+
+ for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
+ nbytes_required += qp_table[*p];
}
- }
- for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
- nbytes_required += qp_table[*p];
- }
+ if (nbytes_required <= char_cnt - 2) {
+ break;
+ }
- if (nbytes_required <= char_cnt - 2) {
- break;
+ out_size -= ((nbytes_required - (char_cnt - 2)) + 2) / 3;
+ in_left = ini_in_left;
+ in_p = ini_in_p;
}
- out_size -= ((nbytes_required - (char_cnt - 2)) + 2) / 3;
- in_left = ini_in_left;
- in_p = ini_in_p;
- }
-
- for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
- if (qp_table[*p] == 1) {
- smart_str_appendc(pretval, *(char *)p);
- char_cnt--;
- } else {
- static const char qp_digits[] = "0123456789ABCDEF";
- smart_str_appendc(pretval, '=');
- smart_str_appendc(pretval, qp_digits[(*p >> 4) & 0x0f]);
- smart_str_appendc(pretval, qp_digits[(*p & 0x0f)]);
- char_cnt -= 3;
+ for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
+ if (qp_table[*p] == 1) {
+ smart_str_appendc(pretval, *(char *)p);
+ char_cnt--;
+ } else {
+ static const char qp_digits[] = "0123456789ABCDEF";
+ smart_str_appendc(pretval, '=');
+ smart_str_appendc(pretval, qp_digits[(*p >> 4) & 0x0f]);
+ smart_str_appendc(pretval, qp_digits[(*p & 0x0f)]);
+ char_cnt -= 3;
+ }
}
- }
- smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
- char_cnt -= 2;
+ smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
+ char_cnt -= 2;
- if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
- err = PHP_ICONV_ERR_UNKNOWN;
- goto out;
- }
+ if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
+ err = PHP_ICONV_ERR_UNKNOWN;
+ goto out;
+ }
- } break; /* case PHP_ICONV_ENC_SCHEME_QPRINT: */
- }
- } while (in_left > 0);
+ } break; /* case PHP_ICONV_ENC_SCHEME_QPRINT: */
+ }
+ } while (in_left > 0);
- smart_str_0(pretval);
+ smart_str_0(pretval);
+ } zend_catch {
+ bailout = true;
+ } zend_end_try();
out:
if (cd != (iconv_t)(-1)) {
@@ -1185,6 +1204,9 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
if (buf != NULL) {
efree(buf);
}
+ if (bailout) {
+ zend_bailout();
+ }
return err;
}
/* }}} */
@@ -1193,6 +1215,7 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *str, size_t str_nbytes, const char *enc, const char **next_pos, int mode)
{
php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
+ bool bailout = false;
iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
@@ -1224,203 +1247,226 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
}
p1 = str;
- for (str_left = str_nbytes; str_left > 0; str_left--, p1++) {
- int eos = 0;
-
- switch (scan_stat) {
- case 0: /* expecting any character */
- switch (*p1) {
- case '\r': /* part of an EOL sequence? */
- scan_stat = 7;
- break;
+ zend_try {
+ for (str_left = str_nbytes; str_left > 0; str_left--, p1++) {
+ int eos = 0;
+
+ switch (scan_stat) {
+ case 0: /* expecting any character */
+ switch (*p1) {
+ case '\r': /* part of an EOL sequence? */
+ scan_stat = 7;
+ break;
- case '\n':
- scan_stat = 8;
- break;
+ case '\n':
+ scan_stat = 8;
+ break;
- case '=': /* first letter of an encoded chunk */
- encoded_word = p1;
- scan_stat = 1;
- break;
+ case '=': /* first letter of an encoded chunk */
+ encoded_word = p1;
+ scan_stat = 1;
+ break;
- case ' ': case '\t': /* a chunk of whitespaces */
- spaces = p1;
- scan_stat = 11;
- break;
+ case ' ': case '\t': /* a chunk of whitespaces */
+ spaces = p1;
+ scan_stat = 11;
+ break;
- default: /* first letter of a non-encoded word */
- err = _php_iconv_appendc(pretval, *p1, cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- if (mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR) {
- err = PHP_ICONV_ERR_SUCCESS;
- } else {
- goto out;
+ default: /* first letter of a non-encoded word */
+ err = _php_iconv_appendc(pretval, *p1, cd_pl);
+ if (err != PHP_ICONV_ERR_SUCCESS) {
+ if (mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR) {
+ err = PHP_ICONV_ERR_SUCCESS;
+ } else {
+ goto out;
+ }
}
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- }
- break;
- }
- break;
-
- case 1: /* expecting a delimiter */
- if (*p1 != '?') {
- if (*p1 == '\r' || *p1 == '\n') {
- --p1;
- }
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
+ encoded_word = NULL;
+ if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
+ scan_stat = 12;
+ }
+ break;
}
break;
- }
- csname = p1 + 1;
- scan_stat = 2;
- break;
-
- case 2: /* expecting a charset name */
- switch (*p1) {
- case '?': /* normal delimiter: encoding scheme follows */
- scan_stat = 3;
- break;
-
- case '*': /* new style delimiter: locale id follows */
- scan_stat = 10;
- break;
- case '\r': case '\n': /* not an encoded-word */
- --p1;
- _php_iconv_appendc(pretval, '=', cd_pl);
- _php_iconv_appendc(pretval, '?', cd_pl);
- err = _php_iconv_appendl(pretval, csname, (size_t)((p1 + 1) - csname), cd_pl);
+ case 1: /* expecting a delimiter */
+ if (*p1 != '?') {
+ if (*p1 == '\r' || *p1 == '\n') {
+ --p1;
+ }
+ err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
if (err != PHP_ICONV_ERR_SUCCESS) {
goto out;
}
- csname = NULL;
+ encoded_word = NULL;
if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
scan_stat = 12;
- }
- else {
+ } else {
scan_stat = 0;
}
- continue;
- }
- if (scan_stat != 2) {
- char tmpbuf[80];
-
- if (csname == NULL) {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
+ break;
}
+ csname = p1 + 1;
+ scan_stat = 2;
+ break;
- csname_len = (size_t)(p1 - csname);
+ case 2: /* expecting a charset name */
+ switch (*p1) {
+ case '?': /* normal delimiter: encoding scheme follows */
+ scan_stat = 3;
+ break;
- if (csname_len > sizeof(tmpbuf) - 1) {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
+ case '*': /* new style delimiter: locale id follows */
+ scan_stat = 10;
+ break;
+
+ case '\r': case '\n': /* not an encoded-word */
+ --p1;
+ _php_iconv_appendc(pretval, '=', cd_pl);
+ _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;
}
- encoded_word = NULL;
+ csname = NULL;
if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
scan_stat = 12;
- } else {
+ }
+ else {
scan_stat = 0;
}
- break;
- } else {
+ continue;
+ }
+ if (scan_stat != 2) {
+ char tmpbuf[80];
+
+ if (csname == NULL) {
err = PHP_ICONV_ERR_MALFORMED;
goto out;
}
- }
-
- memcpy(tmpbuf, csname, csname_len);
- tmpbuf[csname_len] = '\0';
-
- if (cd != (iconv_t)(-1)) {
- iconv_close(cd);
- }
- cd = iconv_open(enc, tmpbuf);
+ csname_len = (size_t)(p1 - csname);
- if (cd == (iconv_t)(-1)) {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* Bad character set, but the user wants us to
- * press on. In this case, we'll just insert the
- * undecoded encoded word, since there isn't really
- * a more sensible behaviour available; the only
- * other options are to swallow the encoded word
- * entirely or decode it with an arbitrarily chosen
- * single byte encoding, both of which seem to have
- * a higher WTF factor than leaving it undecoded.
- *
- * Given this approach, we need to skip ahead to
- * the end of the encoded word. */
- int qmarks = 2;
- while (qmarks > 0 && str_left > 1) {
- if (*(++p1) == '?') {
- --qmarks;
+ if (csname_len > sizeof(tmpbuf) - 1) {
+ 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;
+ }
+ encoded_word = NULL;
+ if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
+ scan_stat = 12;
+ } else {
+ scan_stat = 0;
}
- --str_left;
+ break;
+ } else {
+ err = PHP_ICONV_ERR_MALFORMED;
+ goto out;
}
+ }
+
+ memcpy(tmpbuf, csname, csname_len);
+ tmpbuf[csname_len] = '\0';
+
+ if (cd != (iconv_t)(-1)) {
+ iconv_close(cd);
+ }
+
+ cd = iconv_open(enc, tmpbuf);
- /* Look ahead to check for the terminating = that
- * should be there as well; if it's there, we'll
- * also include that. If it's not, there isn't much
- * we can do at this point. */
- if (*(p1 + 1) == '=') {
- ++p1;
- if (str_left > 1) {
+ if (cd == (iconv_t)(-1)) {
+ if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
+ /* Bad character set, but the user wants us to
+ * press on. In this case, we'll just insert the
+ * undecoded encoded word, since there isn't really
+ * a more sensible behaviour available; the only
+ * other options are to swallow the encoded word
+ * entirely or decode it with an arbitrarily chosen
+ * single byte encoding, both of which seem to have
+ * a higher WTF factor than leaving it undecoded.
+ *
+ * Given this approach, we need to skip ahead to
+ * the end of the encoded word. */
+ int qmarks = 2;
+ while (qmarks > 0 && str_left > 1) {
+ if (*(++p1) == '?') {
+ --qmarks;
+ }
--str_left;
}
- }
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
+ /* Look ahead to check for the terminating = that
+ * should be there as well; if it's there, we'll
+ * also include that. If it's not, there isn't much
+ * we can do at this point. */
+ if (*(p1 + 1) == '=') {
+ ++p1;
+ if (str_left > 1) {
+ --str_left;
+ }
+ }
- /* Let's go back and see if there are further
- * encoded words or bare content, and hope they
- * might actually have a valid character set. */
- scan_stat = 12;
- break;
- } else {
- if (errno == EINVAL) {
- err = PHP_ICONV_ERR_WRONG_CHARSET;
+ err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
+ if (err != PHP_ICONV_ERR_SUCCESS) {
+ goto out;
+ }
+
+ /* Let's go back and see if there are further
+ * encoded words or bare content, and hope they
+ * might actually have a valid character set. */
+ scan_stat = 12;
+ break;
} else {
- err = PHP_ICONV_ERR_CONVERTER;
+ if (errno == EINVAL) {
+ err = PHP_ICONV_ERR_WRONG_CHARSET;
+ } else {
+ err = PHP_ICONV_ERR_CONVERTER;
+ }
+ goto out;
}
- goto out;
}
}
- }
- break;
+ break;
- case 3: /* expecting a encoding scheme specifier */
- switch (*p1) {
- case 'b':
- case 'B':
- enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
- scan_stat = 4;
- break;
+ case 3: /* expecting a encoding scheme specifier */
+ switch (*p1) {
+ case 'b':
+ case 'B':
+ enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
+ scan_stat = 4;
+ break;
- case 'q':
- case 'Q':
- enc_scheme = PHP_ICONV_ENC_SCHEME_QPRINT;
- scan_stat = 4;
- break;
+ case 'q':
+ case 'Q':
+ enc_scheme = PHP_ICONV_ENC_SCHEME_QPRINT;
+ scan_stat = 4;
+ break;
- default:
+ default:
+ 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;
+ }
+ encoded_word = NULL;
+ if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
+ scan_stat = 12;
+ } else {
+ scan_stat = 0;
+ }
+ break;
+ } else {
+ err = PHP_ICONV_ERR_MALFORMED;
+ goto out;
+ }
+ }
+ break;
+
+ case 4: /* expecting a delimiter */
+ if (*p1 != '?') {
if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
+ /* 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;
@@ -1436,299 +1482,281 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
err = PHP_ICONV_ERR_MALFORMED;
goto out;
}
- }
- break;
-
- case 4: /* expecting a delimiter */
- if (*p1 != '?') {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* 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;
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
- break;
- } else {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
}
- }
- encoded_text = p1 + 1;
- scan_stat = 5;
- break;
-
- case 5: /* expecting an encoded portion */
- if (*p1 == '?') {
- encoded_text_len = (size_t)(p1 - encoded_text);
- scan_stat = 6;
- }
- break;
+ encoded_text = p1 + 1;
+ scan_stat = 5;
+ break;
- case 7: /* expecting a "\n" character */
- if (*p1 == '\n') {
- scan_stat = 8;
- } else {
- /* bare CR */
- _php_iconv_appendc(pretval, '\r', cd_pl);
- _php_iconv_appendc(pretval, *p1, cd_pl);
- scan_stat = 0;
- }
- break;
+ case 5: /* expecting an encoded portion */
+ if (*p1 == '?') {
+ encoded_text_len = (size_t)(p1 - encoded_text);
+ scan_stat = 6;
+ }
+ break;
- case 8: /* checking whether the following line is part of a
- folded header */
- if (*p1 != ' ' && *p1 != '\t') {
- --p1;
- str_left = 1; /* quit_loop */
+ case 7: /* expecting a "\n" character */
+ if (*p1 == '\n') {
+ scan_stat = 8;
+ } else {
+ /* bare CR */
+ _php_iconv_appendc(pretval, '\r', cd_pl);
+ _php_iconv_appendc(pretval, *p1, cd_pl);
+ scan_stat = 0;
+ }
break;
- }
- if (encoded_word == NULL) {
- _php_iconv_appendc(pretval, ' ', cd_pl);
- }
- spaces = NULL;
- scan_stat = 11;
- break;
- case 6: /* expecting a End-Of-Chunk character "=" */
- if (*p1 != '=') {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* 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;
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
+ case 8: /* checking whether the following line is part of a
+ folded header */
+ if (*p1 != ' ' && *p1 != '\t') {
+ --p1;
+ str_left = 1; /* quit_loop */
break;
- } else {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
}
- }
- scan_stat = 9;
- if (str_left == 1) {
- eos = 1;
- } else {
+ if (encoded_word == NULL) {
+ _php_iconv_appendc(pretval, ' ', cd_pl);
+ }
+ spaces = NULL;
+ scan_stat = 11;
break;
- }
- /* TODO might want to rearrange logic so this is more obvious */
- ZEND_FALLTHROUGH;
- case 9: /* choice point, seeing what to do next.*/
- switch (*p1) {
- default:
- /* Handle non-RFC-compliant formats
- *
- * RFC2047 requires the character that comes right
- * after an encoded word (chunk) to be a whitespace,
- * while there are lots of broken implementations that
- * generate such malformed headers that don't fulfill
- * that requirement.
- */
- if (!eos) {
+ case 6: /* expecting a End-Of-Chunk character "=" */
+ if (*p1 != '=') {
+ if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
+ /* 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;
+ }
+ encoded_word = NULL;
if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- /* 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;
- }
scan_stat = 12;
- break;
+ } else {
+ scan_stat = 0;
}
+ break;
+ } else {
+ err = PHP_ICONV_ERR_MALFORMED;
+ goto out;
}
- ZEND_FALLTHROUGH;
-
- case '\r': case '\n': case ' ': case '\t': {
- zend_string *decoded_text;
-
- switch (enc_scheme) {
- case PHP_ICONV_ENC_SCHEME_BASE64:
- decoded_text = php_base64_decode((unsigned char*)encoded_text, encoded_text_len);
- break;
-
- case PHP_ICONV_ENC_SCHEME_QPRINT:
- decoded_text = php_quot_print_decode((unsigned char*)encoded_text, encoded_text_len, 1);
- break;
- default:
- decoded_text = NULL;
- break;
- }
+ }
+ scan_stat = 9;
+ if (str_left == 1) {
+ eos = 1;
+ } else {
+ break;
+ }
+ /* TODO might want to rearrange logic so this is more obvious */
+ ZEND_FALLTHROUGH;
- if (decoded_text == NULL) {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* 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;
- }
- encoded_word = NULL;
+ case 9: /* choice point, seeing what to do next.*/
+ switch (*p1) {
+ default:
+ /* Handle non-RFC-compliant formats
+ *
+ * RFC2047 requires the character that comes right
+ * after an encoded word (chunk) to be a whitespace,
+ * while there are lots of broken implementations that
+ * generate such malformed headers that don't fulfill
+ * that requirement.
+ */
+ if (!eos) {
if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
+ /* 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;
+ }
scan_stat = 12;
- } else {
- scan_stat = 0;
+ break;
}
- break;
- } else {
- err = PHP_ICONV_ERR_UNKNOWN;
- goto out;
}
- }
+ ZEND_FALLTHROUGH;
- err = _php_iconv_appendl(pretval, ZSTR_VAL(decoded_text), ZSTR_LEN(decoded_text), cd);
- if (err == PHP_ICONV_ERR_SUCCESS) {
- err = _php_iconv_appendl(pretval, NULL, 0, cd);
- }
- zend_string_release_ex(decoded_text, 0);
+ case '\r': case '\n': case ' ': case '\t': {
+ zend_string *decoded_text;
- if (err != PHP_ICONV_ERR_SUCCESS) {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* pass the entire chunk through the converter */
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)(p1 - encoded_word), cd_pl);
- encoded_word = NULL;
- if (err != PHP_ICONV_ERR_SUCCESS) {
+ switch (enc_scheme) {
+ case PHP_ICONV_ENC_SCHEME_BASE64:
+ decoded_text = php_base64_decode((unsigned char*)encoded_text, encoded_text_len);
+ break;
+
+ case PHP_ICONV_ENC_SCHEME_QPRINT:
+ decoded_text = php_quot_print_decode((unsigned char*)encoded_text, encoded_text_len, 1);
+ break;
+ default:
+ decoded_text = NULL;
+ break;
+ }
+
+ if (decoded_text == NULL) {
+ if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
+ /* 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;
+ }
+ encoded_word = NULL;
+ if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
+ scan_stat = 12;
+ } else {
+ scan_stat = 0;
+ }
break;
+ } else {
+ err = PHP_ICONV_ERR_UNKNOWN;
+ goto out;
}
- } else {
- goto out;
}
- }
- if (eos) { /* reached end-of-string. done. */
- scan_stat = 0;
- break;
- }
+ err = _php_iconv_appendl(pretval, ZSTR_VAL(decoded_text), ZSTR_LEN(decoded_text), cd);
+ if (err == PHP_ICONV_ERR_SUCCESS) {
+ err = _php_iconv_appendl(pretval, NULL, 0, cd);
+ }
+ zend_string_release_ex(decoded_text, 0);
- switch (*p1) {
- case '\r': /* part of an EOL sequence? */
- scan_stat = 7;
- break;
+ if (err != PHP_ICONV_ERR_SUCCESS) {
+ if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
+ /* pass the entire chunk through the converter */
+ err = _php_iconv_appendl(pretval, encoded_word, (size_t)(p1 - encoded_word), cd_pl);
+ encoded_word = NULL;
+ if (err != PHP_ICONV_ERR_SUCCESS) {
+ break;
+ }
+ } else {
+ goto out;
+ }
+ }
- case '\n':
- scan_stat = 8;
+ if (eos) { /* reached end-of-string. done. */
+ scan_stat = 0;
break;
+ }
- case '=': /* first letter of an encoded chunk */
- scan_stat = 1;
- break;
+ switch (*p1) {
+ case '\r': /* part of an EOL sequence? */
+ scan_stat = 7;
+ break;
- case ' ': case '\t': /* medial whitespaces */
- spaces = p1;
- scan_stat = 11;
- break;
+ case '\n':
+ scan_stat = 8;
+ break;
- default: /* first letter of a non-encoded word */
- _php_iconv_appendc(pretval, *p1, cd_pl);
- scan_stat = 12;
- break;
- }
- } break;
- }
- break;
+ case '=': /* first letter of an encoded chunk */
+ scan_stat = 1;
+ break;
- case 10: /* expects a language specifier. dismiss it for now */
- if (*p1 == '?') {
- scan_stat = 3;
- }
- break;
+ case ' ': case '\t': /* medial whitespaces */
+ spaces = p1;
+ scan_stat = 11;
+ break;
- case 11: /* expecting a chunk of whitespaces */
- switch (*p1) {
- case '\r': /* part of an EOL sequence? */
- scan_stat = 7;
- break;
+ default: /* first letter of a non-encoded word */
+ _php_iconv_appendc(pretval, *p1, cd_pl);
+ scan_stat = 12;
+ break;
+ }
+ } break;
+ }
+ break;
- case '\n':
- scan_stat = 8;
- break;
+ case 10: /* expects a language specifier. dismiss it for now */
+ if (*p1 == '?') {
+ scan_stat = 3;
+ }
+ break;
- case '=': /* first letter of an encoded chunk */
- if (spaces != NULL && encoded_word == NULL) {
- _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
- spaces = NULL;
- }
- encoded_word = p1;
- scan_stat = 1;
- break;
+ case 11: /* expecting a chunk of whitespaces */
+ switch (*p1) {
+ case '\r': /* part of an EOL sequence? */
+ scan_stat = 7;
+ break;
- case ' ': case '\t':
- break;
+ case '\n':
+ scan_stat = 8;
+ break;
- default: /* first letter of a non-encoded word */
- if (spaces != NULL) {
- _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
- spaces = NULL;
- }
- _php_iconv_appendc(pretval, *p1, cd_pl);
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
- break;
- }
- break;
+ case '=': /* first letter of an encoded chunk */
+ if (spaces != NULL && encoded_word == NULL) {
+ _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
+ spaces = NULL;
+ }
+ encoded_word = p1;
+ scan_stat = 1;
+ break;
- case 12: /* expecting a non-encoded word */
- switch (*p1) {
- case '\r': /* part of an EOL sequence? */
- scan_stat = 7;
- break;
+ case ' ': case '\t':
+ break;
- case '\n':
- scan_stat = 8;
- break;
+ default: /* first letter of a non-encoded word */
+ if (spaces != NULL) {
+ _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
+ spaces = NULL;
+ }
+ _php_iconv_appendc(pretval, *p1, cd_pl);
+ encoded_word = NULL;
+ if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
+ scan_stat = 12;
+ } else {
+ scan_stat = 0;
+ }
+ break;
+ }
+ break;
- case ' ': case '\t':
- spaces = p1;
- scan_stat = 11;
- break;
+ case 12: /* expecting a non-encoded word */
+ switch (*p1) {
+ case '\r': /* part of an EOL sequence? */
+ scan_stat = 7;
+ break;
- case '=': /* first letter of an encoded chunk */
- if (!(mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- encoded_word = p1;
- scan_stat = 1;
+ case '\n':
+ scan_stat = 8;
break;
- }
- ZEND_FALLTHROUGH;
- default:
- _php_iconv_appendc(pretval, *p1, cd_pl);
- break;
- }
- break;
+ case ' ': case '\t':
+ spaces = p1;
+ scan_stat = 11;
+ break;
+
+ case '=': /* first letter of an encoded chunk */
+ if (!(mode & PHP_ICONV_MIME_DECODE_STRICT)) {
+ encoded_word = p1;
+ scan_stat = 1;
+ break;
+ }
+ ZEND_FALLTHROUGH;
+
+ default:
+ _php_iconv_appendc(pretval, *p1, cd_pl);
+ break;
+ }
+ break;
+ }
}
- }
- switch (scan_stat) {
- case 0: case 8: case 11: case 12:
- break;
- default:
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- if (scan_stat == 1) {
- _php_iconv_appendc(pretval, '=', cd_pl);
+ switch (scan_stat) {
+ case 0: case 8: case 11: case 12:
+ break;
+ default:
+ if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
+ if (scan_stat == 1) {
+ _php_iconv_appendc(pretval, '=', cd_pl);
+ }
+ err = PHP_ICONV_ERR_SUCCESS;
+ } else {
+ err = PHP_ICONV_ERR_MALFORMED;
+ goto out;
}
- err = PHP_ICONV_ERR_SUCCESS;
- } else {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
- }
- }
+ }
- if (next_pos != NULL) {
- *next_pos = p1;
- }
+ if (next_pos != NULL) {
+ *next_pos = p1;
+ }
+
+ smart_str_0(pretval);
+ } zend_catch {
+ bailout = true;
+ } zend_end_try();
- smart_str_0(pretval);
out:
if (cd != (iconv_t)(-1)) {
iconv_close(cd);
@@ -1736,6 +1764,9 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
if (cd_pl != (iconv_t)(-1)) {
iconv_close(cd_pl);
}
+ if (bailout) {
+ zend_bailout();
+ }
return err;
}
/* }}} */
diff --git a/ext/iconv/tests/gh17399.phpt b/ext/iconv/tests/gh17399.phpt
new file mode 100644
index 00000000000..b93fc638e84
--- /dev/null
+++ b/ext/iconv/tests/gh17399.phpt
@@ -0,0 +1,13 @@
+--TEST--
+GH-17399 (iconv memory leak with large line-length in iconv_mime_encode)
+--EXTENSIONS--
+iconv
+--FILE--
+<?php
+$options = [
+ 'line-length' => PHP_INT_MAX,
+];
+iconv_mime_encode('Subject', 'test', $options);
+?>
+--EXPECTF--
+Fatal error: Allowed memory size of %d bytes exhausted %s in %s on line %d
diff --git a/ext/iconv/tests/gh17399_iconv.phpt b/ext/iconv/tests/gh17399_iconv.phpt
new file mode 100644
index 00000000000..2cdd0e176b2
--- /dev/null
+++ b/ext/iconv/tests/gh17399_iconv.phpt
@@ -0,0 +1,13 @@
+--TEST--
+GH-17399 (iconv() leak on bailout)
+--EXTENSIONS--
+iconv
+--INI--
+memory_limit=2M
+--FILE--
+<?php
+$str = str_repeat('a', 1024 * 1024);
+iconv('UTF-8', 'ISO-8859-1', $str);
+?>
+--EXPECTF--
+Fatal error: Allowed memory size of %d bytes exhausted %s in %s on line %d
diff --git a/ext/iconv/tests/gh17399_mime_decode.phpt b/ext/iconv/tests/gh17399_mime_decode.phpt
new file mode 100644
index 00000000000..95a37d364f3
--- /dev/null
+++ b/ext/iconv/tests/gh17399_mime_decode.phpt
@@ -0,0 +1,13 @@
+--TEST--
+GH-17399 (iconv_mime_decode() leak on bailout)
+--EXTENSIONS--
+iconv
+--INI--
+memory_limit=2M
+--FILE--
+<?php
+$str = str_repeat('a', 1024 * 1024);
+iconv_mime_decode($str, 0, 'UTF-8');
+?>
+--EXPECTF--
+Fatal error: Allowed memory size of %d bytes exhausted %s in %s on line %d
diff --git a/ext/iconv/tests/gh17399_substr.phpt b/ext/iconv/tests/gh17399_substr.phpt
new file mode 100644
index 00000000000..35d791db59f
--- /dev/null
+++ b/ext/iconv/tests/gh17399_substr.phpt
@@ -0,0 +1,13 @@
+--TEST--
+GH-17399 (iconv_substr() leak on bailout)
+--EXTENSIONS--
+iconv
+--INI--
+memory_limit=2M
+--FILE--
+<?php
+$str = str_repeat('a', 1024 * 1024);
+iconv_substr($str, 0, 1024 * 1024, 'UTF-8');
+?>
+--EXPECTF--
+Fatal error: Allowed memory size of %d bytes exhausted %s in %s on line %d