On Sat, Mar 28, 2015 at 12:51 PM, Nikos Mavrogiannopoulos
<n...@gnutls.org> wrote:
> Hello Simon,
>  Robert reported some invalid memory access in gnutls, and one I traced
> it back to libidn. A reproducer is attached. The reproducer uses strings
> on the heap because valgrind doesn't seem to detect such accesses on the
> stack.
> ==623== Invalid read of size 1
> ==623==    at 0x4E38E7F: g_utf8_to_ucs4_fast (nfkc.c:399)
> ==623==    by 0x4E38E7F: stringprep_utf8_to_ucs4 (nfkc.c:1023)
> ==623==    by 0x4E3A7DE: idna_to_ascii_8z (idna.c:578)
> ==623==    by 0x4005FD: main (in /home/nmav/cvs/gnutls/lib/a.out)
> ==623==  Address 0x541105f is 1 bytes after a block of size 30 alloc'd
> ==623==    at 0x4C28C20: malloc (vg_replace_malloc.c:296)
> ==623==    by 0x50E99D9: strdup (strdup.c:42)
> ==623==    by 0x4005E0: main (in /home/nmav/cvs/gnutls/lib/a.out)

The attached patches handle the reported issue. However, all functions
which use g_utf8_next_char() including g_utf8_strlen() are affected.

regards,
Nikos
From 1eb41ae34b462dda54abec756234651a2e1bc0e1 Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <n...@gnutls.org>
Date: Sat, 28 Mar 2015 13:24:23 +0100
Subject: [PATCH 1/2] g_utf8_to_ucs4_fast: prevent access past the end of
 string

---
 lib/nfkc.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/lib/nfkc.c b/lib/nfkc.c
index fbea0c8..b5679de 100644
--- a/lib/nfkc.c
+++ b/lib/nfkc.c
@@ -389,25 +389,36 @@ g_utf8_to_ucs4_fast (const gchar * str, glong len, glong * items_written)
   gunichar *result;
   gsize n_chars, i;
   const gchar *p;
+  glong left = len, skip;
 
   g_return_val_if_fail (str != NULL, NULL);
 
+  /* left holds the length in bytes */
+  if (left < 0) left = strlen(str);
+
   p = str;
   n_chars = 0;
   if (len < 0)
     {
       while (*p)
 	{
-	  p = g_utf8_next_char (p);
+	  skip = g_utf8_skip[*(const guchar *)(p)];
+	  left -= skip;
+	  p += skip;
 	  ++n_chars;
+	  if (left < 0)
+	    return NULL;
 	}
     }
   else
     {
       while (p < str + len && *p)
 	{
-	  p = g_utf8_next_char (p);
+	  skip = g_utf8_skip[*(const guchar *)(p)];
+	  p += skip;
 	  ++n_chars;
+	  if (left < 0)
+	    return NULL;
 	}
     }
 
-- 
2.1.4

From 02f3d1f52b8c279f826ccda2d2cccb6ff7ec65cb Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <n...@gnutls.org>
Date: Sat, 28 Mar 2015 13:27:47 +0100
Subject: [PATCH 2/2] Added a check for invalid encodings

---
 tests/Makefile.am   |  2 +-
 tests/tst_invalid.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 1 deletion(-)
 create mode 100644 tests/tst_invalid.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0f491ec..acb50b3 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_invalid
 if TLD
 ctests += tst_tld
 endif
diff --git a/tests/tst_invalid.c b/tests/tst_invalid.c
new file mode 100644
index 0000000..32285a5
--- /dev/null
+++ b/tests/tst_invalid.c
@@ -0,0 +1,52 @@
+/* tst_idna.c --- Self tests for idna_to_ascii().
+ * Copyright (C) 2015 Nikos Mavrogiannopoulos
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <idna.h>
+
+int main()
+{
+	char *s = NULL, *s2 = NULL;
+	int rc;
+	char *x1 = 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 *x2 = strdup("\x76\x72\x6f\x63\x65\x72\x74\x2e\x79\x65\x5e\xdc\xf6\xd0\x00");
+
+
+	rc = idna_to_ascii_8z(x1, &s, 0);
+	if (rc == IDNA_SUCCESS) {
+		/* should have failed */
+		exit(1);
+	}
+
+	rc = idna_to_ascii_8z(x2, &s2, 0);
+	if (rc == IDNA_SUCCESS) {
+		/* should have failed */
+		exit(1);
+	}
+	free(x1);
+	free(x2);
+	exit(0);
+}
-- 
2.1.4

_______________________________________________
Help-libidn mailing list
Help-libidn@gnu.org
https://lists.gnu.org/mailman/listinfo/help-libidn

Reply via email to