Skip to content

Commit 473ec53

Browse files
committed
Fix #48147 - implement manual handling of //IGNORE for broken libc
Conflicts: ext/iconv/iconv.c
1 parent 8026001 commit 473ec53

File tree

4 files changed

+95
-4
lines changed

4 files changed

+95
-4
lines changed

NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ PHP NEWS
66
. Fixed bug #69566 (Conditional jump or move depends on uninitialised value
77
in extension trait). (jbboehr at gmail dot com)
88

9+
- Iconv:
10+
. Fixed bug #48147 (iconv with //IGNORE cuts the string). (Stas)
11+
912
- Opcache
1013
. Fixed bug #69549 (Memory leak with opcache.optimization_level=0xFFFFFFFF).
1114
(Laruence, Dmitry)

ext/iconv/config.m4

+35-4
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

+30
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,24 @@ static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd
464464
}
465465
/* }}} */
466466

467+
/* {{{ */
468+
#if ICONV_BROKEN_IGNORE
469+
static int _php_check_ignore(const char *charset)
470+
{
471+
size_t clen = strlen(charset);
472+
if (clen >= 9 && strcmp("//IGNORE", charset+clen-8) == 0) {
473+
return 1;
474+
}
475+
if (clen >= 19 && strcmp("//IGNORE//TRANSLIT", charset+clen-18) == 0) {
476+
return 1;
477+
}
478+
return 0;
479+
}
480+
#else
481+
#define _php_check_ignore(x) (0)
482+
#endif
483+
/* }}} */
484+
467485
/* {{{ php_iconv_string()
468486
*/
469487
PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len,
@@ -545,6 +563,7 @@ PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len,
545563
char *out_p, *out_buf, *tmp_buf;
546564
size_t bsz, result = 0;
547565
php_iconv_err_t retval = PHP_ICONV_ERR_SUCCESS;
566+
int ignore_ilseq = _php_check_ignore(out_charset);
548567

549568
*out = NULL;
550569
*out_len = 0;
@@ -569,6 +588,17 @@ PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len,
569588
result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left);
570589
out_size = bsz - out_left;
571590
if (result == (size_t)(-1)) {
591+
if (ignore_ilseq && errno == EILSEQ) {
592+
if (in_left <= 1) {
593+
result = 0;
594+
} else {
595+
errno = 0;
596+
in_p++;
597+
in_left--;
598+
continue;
599+
}
600+
}
601+
572602
if (errno == E2BIG && in_left > 0) {
573603
/* converted string is longer than out buffer */
574604
bsz += in_len;

ext/iconv/tests/bug48147.phpt

+27
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)