summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Josefsson <simon@josefsson.org>2015-07-08 02:06:22 +0200
committerSimon Josefsson <simon@josefsson.org>2015-07-08 02:11:25 +0200
commit2e97c2796581c27213962c77f5a8571a598f9a2e (patch)
tree8c3a917f4ca4361e10735b9a1cf6079b7f611831
parenta8dc72ad1cc0c666a99b472e7fbcee96fe9c5c96 (diff)
downloadlibidn-2e97c279.tar.gz
libidn: stringprep_utf8_to_ucs4 now rejects invalid UTF-8. CVE-2015-2059
-rw-r--r--.gitignore8
-rw-r--r--NEWS31
-rw-r--r--lib/gl/Makefile.am10
-rw-r--r--lib/gl/m4/gnulib-cache.m43
-rw-r--r--lib/gl/m4/gnulib-comp.m45
-rw-r--r--lib/gl/unistr/u8-check.c105
-rw-r--r--lib/gltests/Makefile.am10
-rw-r--r--lib/gltests/unistr/test-u8-check.c188
-rw-r--r--lib/nfkc.c19
-rw-r--r--lib/strerror-idna.c2
-rw-r--r--lib/strerror-stringprep.c4
-rw-r--r--lib/stringprep.c5
-rw-r--r--lib/stringprep.h1
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/tst_badutf8.c50
-rw-r--r--tests/tst_stringprep.c22
16 files changed, 450 insertions, 15 deletions
diff --git a/.gitignore b/.gitignore
index bba0c12..37c5729 100644
--- a/.gitignore
+++ b/.gitignore
@@ -385,8 +385,12 @@ lib/gl/unistr.h
lib/gl/unistr/.deps/
lib/gl/unistr/.dirstamp
lib/gl/unistr/.libs/
+lib/gl/unistr/u8-check.lo
+lib/gl/unistr/u8-check.o
lib/gl/unistr/u8-mbtoucr.lo
lib/gl/unistr/u8-mbtoucr.o
+lib/gl/unistr/u8-to-u32.lo
+lib/gl/unistr/u8-to-u32.o
lib/gl/unistr/u8-uctomb-aux.lo
lib/gl/unistr/u8-uctomb-aux.o
lib/gl/unistr/u8-uctomb.lo
@@ -471,6 +475,7 @@ lib/gltests/test-thread_create
lib/gltests/test-thread_create.o
lib/gltests/test-thread_self
lib/gltests/test-thread_self.o
+lib/gltests/test-u8-check
lib/gltests/test-u8-mbtoucr
lib/gltests/test-u8-uctomb
lib/gltests/test-unistd
@@ -484,6 +489,7 @@ lib/gltests/unistd.h
lib/gltests/unistd.o
lib/gltests/unistr/.deps/
lib/gltests/unistr/.dirstamp
+lib/gltests/unistr/test-u8-check.o
lib/gltests/unistr/test-u8-mbtoucr.o
lib/gltests/unistr/test-u8-uctomb.o
lib/gltests/unistr/unistr
@@ -606,6 +612,8 @@ tests/.deps/
tests/Makefile
tests/Makefile.in
tests/libutils.a
+tests/tst_badutf8
+tests/tst_badutf8.o
tests/tst_idna
tests/tst_idna.o
tests/tst_idna2
diff --git a/NEWS b/NEWS
index 8716343..22821d6 100644
--- a/NEWS
+++ b/NEWS
@@ -2,7 +2,36 @@ Libidn NEWS -- History of user-visible changes. -*- outline -*-
Copyright (C) 2002-2015 Simon Josefsson
See the end for copying conditions.
-* Version 1.31 (unreleased) [stable]
+* Version 1.31 (unreleased) [beta]
+
+** libidn: stringprep_utf8_to_ucs4 now rejects invalid UTF-8. CVE-2015-2059
+This function has always been documented to not validate that the
+input UTF-8 string is actually valid UTF-8. Like the rest of the API,
+when you call a function that works on UTF-8 data, you have to pass it
+valid UTF-8 data. Application writers appear to have difficulties
+using interfaces designed like that, as bugs triggered by invalid
+UTF-8 has been identified in a number of projects (jabberd2, gnutls,
+wget, and curl). While we could introduce a new API to perform UTF-8
+validation, so that applications can easily implement the proper
+checks, this appear error prone because there is a risk that the check
+will be forgotten. Instead, we took the more radical approach of
+modifying the documentation and the implementation of the API. The
+intention is that all functions that accepts UTF-8 data should
+validate it before use. This will solve the problem for applications,
+without needing to change them. This change has the unfortunate
+side-effect that Surrogate codes (see section 5.5 of RFC 3454) no
+longer trigger the STRINGPREP_CONTAINS_PROHIBITED error code but
+instead will trigger the newly introduced STRINGPREP_ICONV_ERROR error
+code, as the gnulib/libunistring-based code that we use to test
+UTF-8-compliance rejects Surrogate codes. We hope that this is an
+acceptable cost to live with in order to improve application security.
+We welcome feedback on this solution, and we are marking this release
+as beta rather than stable to signal that we may reconsider this
+approach if people disagree. Reported by several people including
+Thijs Alkemade, Gustavo Grieco, Daniel Stenberg, and Nikos
+Mavrogiannopoulos.
+
+** libidn: Added STRINGPREP_ICONV_ERROR error code.
** libidn: Workaround valgrind/gcc/glibc issue.
Valgrind reported a 'Invalid read of size 4' that was caused by
diff --git a/lib/gl/Makefile.am b/lib/gl/Makefile.am
index e453192..3902005 100644
--- a/lib/gl/Makefile.am
+++ b/lib/gl/Makefile.am
@@ -21,7 +21,7 @@
# the same distribution terms as the rest of that program.
#
# Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --local-dir=lib/gl/override --lib=libgnu --source-base=lib/gl --m4-base=lib/gl/m4 --doc-base=doc --tests-base=lib/gltests --aux-dir=build-aux --with-tests --avoid=iconv-h-tests --avoid=string-tests --avoid=wchar-tests --lgpl=2 --no-conditional-dependencies --libtool --macro-prefix=lgl --no-vc-files gettext-h lib-msvc-compat lib-symbol-versions lib-symbol-visibility stdint striconv strverscmp
+# Reproduce by: gnulib-tool --import --dir=. --local-dir=lib/gl/override --lib=libgnu --source-base=lib/gl --m4-base=lib/gl/m4 --doc-base=doc --tests-base=lib/gltests --aux-dir=build-aux --with-tests --avoid=iconv-h-tests --avoid=string-tests --avoid=wchar-tests --lgpl=2 --no-conditional-dependencies --libtool --macro-prefix=lgl --no-vc-files gettext-h lib-msvc-compat lib-symbol-versions lib-symbol-visibility stdint striconv strverscmp unistr/u8-check
AUTOMAKE_OPTIONS = 1.9.6 gnits subdir-objects
@@ -524,6 +524,14 @@ EXTRA_DIST += unistr.in.h
## end gnulib module unistr/base
+## begin gnulib module unistr/u8-check
+
+if LIBUNISTRING_COMPILE_UNISTR_U8_CHECK
+libgnu_la_SOURCES += unistr/u8-check.c
+endif
+
+## end gnulib module unistr/u8-check
+
## begin gnulib module unistr/u8-mbtoucr
if LIBUNISTRING_COMPILE_UNISTR_U8_MBTOUCR
diff --git a/lib/gl/m4/gnulib-cache.m4 b/lib/gl/m4/gnulib-cache.m4
index 16a70bb..1c59f8f 100644
--- a/lib/gl/m4/gnulib-cache.m4
+++ b/lib/gl/m4/gnulib-cache.m4
@@ -27,7 +27,7 @@
# Specification in the form of a command-line invocation:
-# gnulib-tool --import --dir=. --local-dir=lib/gl/override --lib=libgnu --source-base=lib/gl --m4-base=lib/gl/m4 --doc-base=doc --tests-base=lib/gltests --aux-dir=build-aux --with-tests --avoid=iconv-h-tests --avoid=string-tests --avoid=wchar-tests --lgpl=2 --no-conditional-dependencies --libtool --macro-prefix=lgl --no-vc-files gettext-h lib-msvc-compat lib-symbol-versions lib-symbol-visibility stdint striconv strverscmp
+# gnulib-tool --import --dir=. --local-dir=lib/gl/override --lib=libgnu --source-base=lib/gl --m4-base=lib/gl/m4 --doc-base=doc --tests-base=lib/gltests --aux-dir=build-aux --with-tests --avoid=iconv-h-tests --avoid=string-tests --avoid=wchar-tests --lgpl=2 --no-conditional-dependencies --libtool --macro-prefix=lgl --no-vc-files gettext-h lib-msvc-compat lib-symbol-versions lib-symbol-visibility stdint striconv strverscmp unistr/u8-check
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([lib/gl/override])
@@ -39,6 +39,7 @@ gl_MODULES([
stdint
striconv
strverscmp
+ unistr/u8-check
])
gl_AVOID([iconv-h-tests string-tests wchar-tests])
gl_SOURCE_BASE([lib/gl])
diff --git a/lib/gl/m4/gnulib-comp.m4 b/lib/gl/m4/gnulib-comp.m4
index 83f0b0f..715104c 100644
--- a/lib/gl/m4/gnulib-comp.m4
+++ b/lib/gl/m4/gnulib-comp.m4
@@ -117,6 +117,8 @@ AC_DEFUN([lgl_EARLY],
# Code from module unistd:
# Code from module unistd-tests:
# Code from module unistr/base:
+ # Code from module unistr/u8-check:
+ # Code from module unistr/u8-check-tests:
# Code from module unistr/u8-mbtoucr:
# Code from module unistr/u8-mbtoucr-tests:
# Code from module unistr/u8-uctomb:
@@ -182,6 +184,7 @@ AC_DEFUN([lgl_INIT],
gl_SYS_TYPES_H
AC_PROG_MKDIR_P
gl_LIBUNISTRING_LIBHEADER([0.9.4], [unistr.h])
+ gl_LIBUNISTRING_MODULE([0.9], [unistr/u8-check])
gl_MODULE_INDICATOR([unistr/u8-mbtoucr])
gl_LIBUNISTRING_MODULE([0.9], [unistr/u8-mbtoucr])
gl_MODULE_INDICATOR([unistr/u8-uctomb])
@@ -411,6 +414,7 @@ AC_DEFUN([lgl_FILE_LIST], [
lib/strverscmp.c
lib/sys_types.in.h
lib/unistr.in.h
+ lib/unistr/u8-check.c
lib/unistr/u8-mbtoucr.c
lib/unistr/u8-uctomb-aux.c
lib/unistr/u8-uctomb.c
@@ -511,6 +515,7 @@ AC_DEFUN([lgl_FILE_LIST], [
tests/test-unsetenv.c
tests/test-verify.c
tests/test-verify.sh
+ tests/unistr/test-u8-check.c
tests/unistr/test-u8-mbtoucr.c
tests/unistr/test-u8-uctomb.c
tests=lib/alloca.in.h
diff --git a/lib/gl/unistr/u8-check.c b/lib/gl/unistr/u8-check.c
new file mode 100644
index 0000000..0e87f11
--- /dev/null
+++ b/lib/gl/unistr/u8-check.c
@@ -0,0 +1,105 @@
+/* Check UTF-8 string.
+ Copyright (C) 2002, 2006-2007, 2009-2015 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2002.
+
+ This program is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "unistr.h"
+
+const uint8_t *
+u8_check (const uint8_t *s, size_t n)
+{
+ const uint8_t *s_end = s + n;
+
+ while (s < s_end)
+ {
+ /* Keep in sync with unistr.h and u8-mbtouc-aux.c. */
+ uint8_t c = *s;
+
+ if (c < 0x80)
+ {
+ s++;
+ continue;
+ }
+ if (c >= 0xc2)
+ {
+ if (c < 0xe0)
+ {
+ if (s + 2 <= s_end
+ && (s[1] ^ 0x80) < 0x40)
+ {
+ s += 2;
+ continue;
+ }
+ }
+ else if (c < 0xf0)
+ {
+ if (s + 3 <= s_end
+ && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40
+ && (c >= 0xe1 || s[1] >= 0xa0)
+ && (c != 0xed || s[1] < 0xa0))
+ {
+ s += 3;
+ continue;
+ }
+ }
+ else if (c < 0xf8)
+ {
+ if (s + 4 <= s_end
+ && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40
+ && (s[3] ^ 0x80) < 0x40
+ && (c >= 0xf1 || s[1] >= 0x90)
+#if 1
+ && (c < 0xf4 || (c == 0xf4 && s[1] < 0x90))
+#endif
+ )
+ {
+ s += 4;
+ continue;
+ }
+ }
+#if 0
+ else if (c < 0xfc)
+ {
+ if (s + 5 <= s_end
+ && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40
+ && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40
+ && (c >= 0xf9 || s[1] >= 0x88))
+ {
+ s += 5;
+ continue;
+ }
+ }
+ else if (c < 0xfe)
+ {
+ if (s + 6 <= s_end
+ && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40
+ && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40
+ && (s[5] ^ 0x80) < 0x40
+ && (c >= 0xfd || s[1] >= 0x84))
+ {
+ s += 6;
+ continue;
+ }
+ }
+#endif
+ }
+ /* invalid or incomplete multibyte character */
+ return s;
+ }
+ return NULL;
+}
diff --git a/lib/gltests/Makefile.am b/lib/gltests/Makefile.am
index 4544746..d6de4bc 100644
--- a/lib/gltests/Makefile.am
+++ b/lib/gltests/Makefile.am
@@ -880,6 +880,16 @@ EXTRA_DIST += test-unistd.c
## end gnulib module unistd-tests
+## begin gnulib module unistr/u8-check-tests
+
+TESTS += test-u8-check
+check_PROGRAMS += test-u8-check
+test_u8_check_SOURCES = unistr/test-u8-check.c
+test_u8_check_LDADD = $(LDADD) $(LIBUNISTRING)
+EXTRA_DIST += unistr/test-u8-check.c macros.h
+
+## end gnulib module unistr/u8-check-tests
+
## begin gnulib module unistr/u8-mbtoucr-tests
TESTS += test-u8-mbtoucr
diff --git a/lib/gltests/unistr/test-u8-check.c b/lib/gltests/unistr/test-u8-check.c
new file mode 100644
index 0000000..fcf678c
--- /dev/null
+++ b/lib/gltests/unistr/test-u8-check.c
@@ -0,0 +1,188 @@
+/* Test of u8_check() function.
+ Copyright (C) 2010-2015 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2010. */
+
+#include <config.h>
+
+#include "unistr.h"
+
+#include "macros.h"
+
+int
+main ()
+{
+ /* Test empty string. */
+ {
+ static const uint8_t input[] = "";
+ ASSERT (u8_check (input, 0) == NULL);
+ }
+
+ /* Test valid non-empty string. */
+ {
+ static const uint8_t input[] = /* "Данило Шеган" */
+ "\320\224\320\260\320\275\320\270\320\273\320\276 \320\250\320\265\320\263\320\260\320\275";
+ ASSERT (u8_check (input, sizeof (input) - 1) == NULL);
+ }
+
+ /* Test out-of-range character with 4 bytes: U+110000. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\364\220\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test out-of-range character with 5 bytes: U+200000. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\370\210\200\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test out-of-range character with 6 bytes: U+4000000. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\374\204\200\200\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test invalid lead byte. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\376\200\200\200\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\377\200\200\200\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test overlong 2-byte character. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\301\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test overlong 3-byte character. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\340\200\277";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test overlong 4-byte character. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\360\200\277\277";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test invalid bytes in 2-byte character. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\302\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == NULL);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\302\100";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\302\300";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test invalid bytes in 3-byte character. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\342\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == NULL);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\342\100\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\342\300\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\342\200\100";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\342\200\300";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test invalid bytes in 4-byte character. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\362\200\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == NULL);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\362\100\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\362\300\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\362\200\100\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\362\200\300\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\362\200\200\100";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\362\200\200\300";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test truncated/incomplete 2-byte character. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\302";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test truncated/incomplete 3-byte character. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\342\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test truncated/incomplete 4-byte character. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\362\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test missing lead byte. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\200\200\200\200\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ /* Test surrogate codepoints. */
+ {
+ static const uint8_t input[] = "\320\224\320\260\355\240\200\355\260\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+ {
+ static const uint8_t input[] = "\320\224\320\260\355\260\200";
+ ASSERT (u8_check (input, sizeof (input) - 1) == input + 4);
+ }
+
+ return 0;
+}
diff --git a/lib/nfkc.c b/lib/nfkc.c
index fbea0c8..4992074 100644
--- a/lib/nfkc.c
+++ b/lib/nfkc.c
@@ -1002,6 +1002,8 @@ stringprep_unichar_to_utf8 (uint32_t c, char *outbuf)
return g_unichar_to_utf8 (c, outbuf);
}
+#include <unistr.h>
+
/**
* stringprep_utf8_to_ucs4:
* @str: a UTF-8 encoded string
@@ -1010,9 +1012,10 @@ stringprep_unichar_to_utf8 (uint32_t c, char *outbuf)
* @items_written: location to store the number of characters in the
* result, or %NULL.
*
- * Convert a string from UTF-8 to a 32-bit fixed width
- * representation as UCS-4, assuming valid UTF-8 input.
- * This function does no error checking on the input.
+ * Convert a string from UTF-8 to a 32-bit fixed width representation
+ * as UCS-4. The function now performs error checking to verify that
+ * the input is valid UTF-8 (before it was documented to not do error
+ * checking).
*
* Return value: a pointer to a newly allocated UCS-4 string.
* This value must be deallocated by the caller.
@@ -1020,6 +1023,16 @@ stringprep_unichar_to_utf8 (uint32_t c, char *outbuf)
uint32_t *
stringprep_utf8_to_ucs4 (const char *str, ssize_t len, size_t * items_written)
{
+ size_t n;
+
+ if (len < 0)
+ n = strlen (str);
+ else
+ n = len;
+
+ if (u8_check ((const uint8_t *) str, n))
+ return NULL;
+
return g_utf8_to_ucs4_fast (str, (glong) len, (glong *) items_written);
}
diff --git a/lib/strerror-idna.c b/lib/strerror-idna.c
index 0163c7e..f1e94ec 100644
--- a/lib/strerror-idna.c
+++ b/lib/strerror-idna.c
@@ -115,7 +115,7 @@ idna_strerror (Idna_rc rc)
break;
case IDNA_ICONV_ERROR:
- p = _("System iconv failed");
+ p = _("Could not convert string in locale encoding");
break;
case IDNA_MALLOC_ERROR:
diff --git a/lib/strerror-stringprep.c b/lib/strerror-stringprep.c
index f3a8c9c..f53fbbd 100644
--- a/lib/strerror-stringprep.c
+++ b/lib/strerror-stringprep.c
@@ -65,6 +65,7 @@
* This usually indicate a problem in the calling application.
* STRINGPREP_UNKNOWN_PROFILE: The supplied profile name was not
* known to the library.
+ * STRINGPREP_ICONV_ERROR: Could not convert string in locale encoding.
* STRINGPREP_NFKC_FAILED: The Unicode NFKC operation failed. This
* usually indicate an internal error in the library.
* STRINGPREP_MALLOC_ERROR: The malloc() was out of memory. This is
@@ -121,6 +122,9 @@ stringprep_strerror (Stringprep_rc rc)
case STRINGPREP_UNKNOWN_PROFILE:
p = _("Unknown profile");
break;
+ case STRINGPREP_ICONV_ERROR:
+ p = _("Could not convert string in locale encoding.");
+ break;
case STRINGPREP_NFKC_FAILED:
p = _("Unicode normalization failed (internal error)");
diff --git a/lib/stringprep.c b/lib/stringprep.c
index fc17c5c..8ff28e6 100644
--- a/lib/stringprep.c
+++ b/lib/stringprep.c
@@ -380,6 +380,8 @@ stringprep (char *in,
free (ucs4);
ucs4 = stringprep_utf8_to_ucs4 (in, -1, &ucs4len);
+ if (ucs4 == NULL)
+ return STRINGPREP_ICONV_ERROR;
maxucs4len = ucs4len + adducs4len;
newp = realloc (ucs4, maxucs4len * sizeof (uint32_t));
if (!newp)
@@ -402,7 +404,7 @@ stringprep (char *in,
utf8 = stringprep_ucs4_to_utf8 (ucs4, ucs4len, 0, 0);
free (ucs4);
if (!utf8)
- return STRINGPREP_MALLOC_ERROR;
+ return STRINGPREP_ICONV_ERROR;
if (strlen (utf8) >= maxlen)
{
@@ -590,6 +592,7 @@ stringprep_profile (const char *in,
* This usually indicate a problem in the calling application.
* @STRINGPREP_UNKNOWN_PROFILE: The supplied profile name was not
* known to the library.
+ * @STRINGPREP_ICONV_ERROR: Could not convert string in locale encoding.
* @STRINGPREP_NFKC_FAILED: The Unicode NFKC operation failed. This
* usually indicate an internal error in the library.
* @STRINGPREP_MALLOC_ERROR: The malloc() was out of memory. This is
diff --git a/lib/stringprep.h b/lib/stringprep.h
index d322a3a..309d01e 100644
--- a/lib/stringprep.h
+++ b/lib/stringprep.h
@@ -68,6 +68,7 @@ extern "C"
STRINGPREP_PROFILE_ERROR = 101,
STRINGPREP_FLAG_ERROR = 102,
STRINGPREP_UNKNOWN_PROFILE = 103,
+ STRINGPREP_ICONV_ERROR = 104,
/* Internal errors. */
STRINGPREP_NFKC_FAILED = 200,
STRINGPREP_MALLOC_ERROR = 201
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1e4efa7..0f890fe 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -27,7 +27,7 @@ libutils_a_SOURCES = utils.h utils.c
ctests = tst_stringprep tst_punycode tst_idna tst_idna2 tst_idna3 \
tst_idna4 tst_nfkc tst_pr29 tst_strerror tst_toutf8 \
- tst_symbols
+ tst_symbols tst_badutf8
if TLD
ctests += tst_tld
endif
diff --git a/tests/tst_badutf8.c b/tests/tst_badutf8.c
new file mode 100644
index 0000000..2feb099
--- /dev/null
+++ b/tests/tst_badutf8.c
@@ -0,0 +1,50 @@
+/* tst_badutf8.c --- Self tests for malformed UTF-8 regressions.
+ * Copyright (C) 2015 Simon Josefsson
+ *
+ * This file is part of GNU Libidn.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <idna.h>
+#include <idn-free.h>
+
+#include "utils.h"
+
+void
+doit (void)
+{
+ char *badutf8 = strdup ("\x7e\x64\x61\x72\x10\x2f\x2f\xf9\x2b\x71"
+ "\x60\x79\x7b\x2e\x63\x75\x2b\x61\x65\x72"
+ "\x75\x65\x56\x66\x7f\x62\xc5\x76\xe5\x00");
+ char *s = NULL;
+ int rc;
+
+ rc = idna_to_ascii_8z (badutf8, &s, 0);
+ free (badutf8);
+ if (rc != IDNA_ICONV_ERROR)
+ fail ("rc %d\n", rc);
+
+ idn_free (s);
+}
diff --git a/tests/tst_stringprep.c b/tests/tst_stringprep.c
index 1856d35..c352dd9 100644
--- a/tests/tst_stringprep.c
+++ b/tests/tst_stringprep.c
@@ -100,7 +100,8 @@ const struct stringprep strprep[] = {
"\xF4\x8F\xBF\xBF", NULL, "Nameprep", 0,
STRINGPREP_CONTAINS_PROHIBITED},
{"Surrogate code U+DF42",
- "\xED\xBD\x82", NULL, "Nameprep", 0, STRINGPREP_CONTAINS_PROHIBITED},
+ "\xED\xBD\x82", NULL, "Nameprep", 0, STRINGPREP_ICONV_ERROR
+ /* was STRINGPREP_CONTAINS_PROHIBITED */},
{"Non-plain text character U+FFFD",
"\xEF\xBF\xBD", NULL, "Nameprep", 0, STRINGPREP_CONTAINS_PROHIBITED},
{"Ideographic description character U+2FF5",
@@ -234,15 +235,22 @@ doit (void)
hexprint (strprep[i].in, strlen (strprep[i].in));
binprint (strprep[i].in, strlen (strprep[i].in));
}
-
{
uint32_t *l;
- char *x;
+ char *x = NULL;
l = stringprep_utf8_to_ucs4 (strprep[i].in, -1, NULL);
- x = stringprep_ucs4_to_utf8 (l, -1, NULL, NULL);
+ if (l)
+ x = stringprep_ucs4_to_utf8 (l, -1, NULL, NULL);
free (l);
-
- if (strcmp (strprep[i].in, x) != 0)
+ if (i == 29)
+ /* Ignoring known bad UTF-8 in entry 29 */
+ continue;
+ else if (l == NULL)
+ {
+ fail ("bad UTF-8 in entry %ld\n", i);
+ continue;
+ }
+ else if (strcmp (strprep[i].in, x) != 0)
{
fail ("bad UTF-8 in entry %ld\n", i);
if (debug)
@@ -254,10 +262,12 @@ doit (void)
escapeprint (x, strlen (x));
hexprint (x, strlen (x));
}
+ continue;
}
free (x);
}
+
rc = stringprep_profile (strprep[i].in, &p,
strprep[i].profile ?
strprep[i].profile :