Skip to content

Commit f8f1d27

Browse files
committed
Fix #48147 - implement manual handling of //IGNORE for broken libc
1 parent 0c92538 commit f8f1d27

File tree

3 files changed

+92
-4
lines changed

3 files changed

+92
-4
lines changed

ext/iconv/config.m4

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ if test "$PHP_ICONV" != "no"; then
3535
PHP_ICONV_H_PATH="$PHP_ICONV_PREFIX/include/giconv.h"
3636
else
3737
PHP_ICONV_H_PATH="$PHP_ICONV_PREFIX/include/iconv.h"
38-
fi
38+
fi
3939

4040
AC_MSG_CHECKING([if iconv is glibc's])
4141
AC_TRY_LINK([#include <gnu/libc-version.h>],[gnu_get_libc_version();],
@@ -53,8 +53,8 @@ if test "$PHP_ICONV" != "no"; then
5353
AC_TRY_RUN([
5454
#include <$PHP_ICONV_H_PATH>
5555
int main() {
56-
printf("%d", _libiconv_version);
57-
return 0;
56+
printf("%d", _libiconv_version);
57+
return 0;
5858
}
5959
],[
6060
AC_MSG_RESULT(yes)
@@ -138,7 +138,7 @@ int main() {
138138
if (cd == (iconv_t)(-1)) {
139139
if (errno == EINVAL) {
140140
return 0;
141-
} else {
141+
} else {
142142
return 1;
143143
}
144144
}
@@ -159,6 +159,37 @@ int main() {
159159
AC_DEFINE([ICONV_SUPPORTS_ERRNO],0,[Whether iconv supports error no or not])
160160
])
161161

162+
AC_MSG_CHECKING([if iconv supports //IGNORE])
163+
AC_TRY_RUN([
164+
#include <$PHP_ICONV_H_PATH>
165+
#include <stdlib.h>
166+
167+
int main() {
168+
iconv_t cd = iconv_open( "UTF-8//IGNORE", "UTF-8" );
169+
char *in_p = "\xC3\xC3\xC3\xB8";
170+
size_t in_left = 4, out_left = 4096;
171+
char *out = malloc(out_left);
172+
char *out_p = out;
173+
size_t result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left);
174+
if(result == (size_t)-1) {
175+
return 1;
176+
}
177+
return 0;
178+
}
179+
],[
180+
AC_MSG_RESULT(yes)
181+
PHP_DEFINE([ICONV_BROKEN_IGNORE],0,[ext/iconv])
182+
AC_DEFINE([ICONV_BROKEN_IGNORE],0,[Whether iconv supports IGNORE])
183+
],[
184+
AC_MSG_RESULT(no)
185+
PHP_DEFINE([ICONV_BROKEN_IGNORE],1,[ext/iconv])
186+
AC_DEFINE([ICONV_BROKEN_IGNORE],1,[Whether iconv supports IGNORE])
187+
],[
188+
AC_MSG_RESULT(no, cross-compiling)
189+
PHP_DEFINE([ICONV_SUPPORTS_ERRNO],0,[ext/iconv])
190+
AC_DEFINE([ICONV_SUPPORTS_ERRNO],0,[Whether iconv supports IGNORE])
191+
])
192+
162193
AC_MSG_CHECKING([if your cpp allows macro usage in include lines])
163194
AC_TRY_COMPILE([
164195
#define FOO <$PHP_ICONV_H_PATH>

ext/iconv/iconv.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,24 @@ static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd
535535
}
536536
/* }}} */
537537

538+
/* {{{ */
539+
#if ICONV_BROKEN_IGNORE
540+
static int _php_check_ignore(const char *charset)
541+
{
542+
size_t clen = strlen(charset);
543+
if (clen >= 9 && strcmp("//IGNORE", charset+clen-8) == 0) {
544+
return 1;
545+
}
546+
if (clen >= 19 && strcmp("//IGNORE//TRANSLIT", charset+clen-18) == 0) {
547+
return 1;
548+
}
549+
return 0;
550+
}
551+
#else
552+
#define _php_check_ignore(x) (0)
553+
#endif
554+
/* }}} */
555+
538556
/* {{{ php_iconv_string()
539557
*/
540558
PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len, zend_string **out, const char *out_charset, const char *in_charset)
@@ -613,6 +631,7 @@ PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len,
613631
size_t bsz, result = 0;
614632
php_iconv_err_t retval = PHP_ICONV_ERR_SUCCESS;
615633
zend_string *out_buf;
634+
int ignore_ilseq = _php_check_ignore(out_charset);
616635

617636
*out = NULL;
618637

@@ -636,6 +655,17 @@ PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len,
636655
result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left);
637656
out_size = bsz - out_left;
638657
if (result == (size_t)(-1)) {
658+
if (ignore_ilseq && errno == EILSEQ) {
659+
if (in_left <= 1) {
660+
result = 0;
661+
} else {
662+
errno = 0;
663+
in_p++;
664+
in_left--;
665+
continue;
666+
}
667+
}
668+
639669
if (errno == E2BIG && in_left > 0) {
640670
/* converted string is longer than out buffer */
641671
bsz += in_len;

ext/iconv/tests/bug48147.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Bug #48147 (iconv with //IGNORE cuts the string)
3+
--SKIPIF--
4+
<?php extension_loaded('iconv') or die('skip iconv extension is not available'); ?>
5+
--FILE--
6+
<?php
7+
$text = "aa\xC3\xC3\xC3\xB8aa";
8+
var_dump(iconv("UTF-8", "UTF-8", $text));
9+
var_dump(urlencode(iconv("UTF-8", "UTF-8//IGNORE", $text)));
10+
// only invalid
11+
var_dump(urlencode(iconv("UTF-8", "UTF-8//IGNORE", "\xC3")));
12+
// start invalid
13+
var_dump(urlencode(iconv("UTF-8", "UTF-8//IGNORE", "\xC3\xC3\xC3\xB8aa")));
14+
// finish invalid
15+
var_dump(urlencode(iconv("UTF-8", "UTF-8//IGNORE", "aa\xC3\xC3\xC3")));
16+
?>
17+
--EXPECTF--
18+
Notice: iconv(): Detected an illegal character in input string in %s on line %d
19+
bool(false)
20+
string(10) "aa%C3%B8aa"
21+
22+
Notice: iconv(): Detected an incomplete multibyte character in input string in %s on line %d
23+
string(0) ""
24+
string(8) "%C3%B8aa"
25+
26+
Notice: iconv(): Detected an incomplete multibyte character in input string in %s on line %d
27+
string(0) ""

0 commit comments

Comments
 (0)