From 570e68886c41c2e765e6218cb317d9a9a447a041 Mon Sep 17 00:00:00 2001
From: Simon Josefsson <simon@josefsson.org>
Date: Mon, 10 Aug 2015 08:23:30 +0000
Subject: idn: Use getline instead of fgets with fixed-size buffer.

Fixes out-of-bounds read, reported by Hanno Böck.
---
Index: libidn-1.10/gl/Makefile.am
===================================================================
--- libidn-1.10.orig/gl/Makefile.am
+++ libidn-1.10/gl/Makefile.am
@@ -58,6 +58,24 @@ EXTRA_DIST += $(top_srcdir)/build-aux/ge
 
 ## end   gnulib module gendocs
 
+## begin gnulib module getdelim
+
+
+EXTRA_DIST += getdelim.c
+
+EXTRA_libgnu_la_SOURCES += getdelim.c
+
+## end   gnulib module getdelim
+
+## begin gnulib module getline
+
+
+EXTRA_DIST += getline.c
+
+EXTRA_libgnu_la_SOURCES += getline.c
+
+## end   gnulib module getline
+
 ## begin gnulib module getopt
 
 BUILT_SOURCES += $(GETOPT_H)
Index: libidn-1.10/gl/getdelim.c
===================================================================
--- /dev/null
+++ libidn-1.10/gl/getdelim.c
@@ -0,0 +1,135 @@
+/* getdelim.c --- Implementation of replacement getdelim function.
+   Copyright (C) 1994, 1996-1998, 2001, 2003, 2005-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, 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/>.  */
+
+/* Ported from glibc by Simon Josefsson. */
+
+/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
+   optimizes away the lineptr == NULL || n == NULL || fp == NULL tests below.  */
+#define _GL_ARG_NONNULL(params)
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef SSIZE_MAX
+# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
+#endif
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+# define getc_maybe_unlocked(fp)        getc(fp)
+#elif !HAVE_FLOCKFILE || !HAVE_FUNLOCKFILE || !HAVE_DECL_GETC_UNLOCKED
+# undef flockfile
+# undef funlockfile
+# define flockfile(x) ((void) 0)
+# define funlockfile(x) ((void) 0)
+# define getc_maybe_unlocked(fp)        getc(fp)
+#else
+# define getc_maybe_unlocked(fp)        getc_unlocked(fp)
+#endif
+
+/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and
+   NUL-terminate it).  *LINEPTR is a pointer returned from malloc (or
+   NULL), pointing to *N characters of space.  It is realloc'ed as
+   necessary.  Returns the number of characters read (not including
+   the null terminator), or -1 on error or EOF.  */
+
+ssize_t
+getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
+{
+  ssize_t result;
+  size_t cur_len = 0;
+
+  if (lineptr == NULL || n == NULL || fp == NULL)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  flockfile (fp);
+
+  if (*lineptr == NULL || *n == 0)
+    {
+      char *new_lineptr;
+      *n = 120;
+      new_lineptr = (char *) realloc (*lineptr, *n);
+      if (new_lineptr == NULL)
+        {
+          result = -1;
+          goto unlock_return;
+        }
+      *lineptr = new_lineptr;
+    }
+
+  for (;;)
+    {
+      int i;
+
+      i = getc_maybe_unlocked (fp);
+      if (i == EOF)
+        {
+          result = -1;
+          break;
+        }
+
+      /* Make enough space for len+1 (for final NUL) bytes.  */
+      if (cur_len + 1 >= *n)
+        {
+          size_t needed_max =
+            SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
+          size_t needed = 2 * *n + 1;   /* Be generous. */
+          char *new_lineptr;
+
+          if (needed_max < needed)
+            needed = needed_max;
+          if (cur_len + 1 >= needed)
+            {
+              result = -1;
+              errno = EOVERFLOW;
+              goto unlock_return;
+            }
+
+          new_lineptr = (char *) realloc (*lineptr, needed);
+          if (new_lineptr == NULL)
+            {
+              result = -1;
+              goto unlock_return;
+            }
+
+          *lineptr = new_lineptr;
+          *n = needed;
+        }
+
+      (*lineptr)[cur_len] = i;
+      cur_len++;
+
+      if (i == delimiter)
+        break;
+    }
+  (*lineptr)[cur_len] = '\0';
+  result = cur_len ? cur_len : result;
+
+ unlock_return:
+  funlockfile (fp); /* doesn't set errno */
+
+  return result;
+}
Index: libidn-1.10/gl/getline.c
===================================================================
--- /dev/null
+++ libidn-1.10/gl/getline.c
@@ -0,0 +1,27 @@
+/* getline.c --- Implementation of replacement getline function.
+   Copyright (C) 2005-2007, 2009-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, 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 Simon Josefsson. */
+
+#include <config.h>
+
+#include <stdio.h>
+
+ssize_t
+getline (char **lineptr, size_t *n, FILE *stream)
+{
+  return getdelim (lineptr, n, '\n', stream);
+}
Index: libidn-1.10/src/idn.c
===================================================================
--- libidn-1.10.orig/src/idn.c
+++ libidn-1.10/src/idn.c
@@ -136,7 +136,8 @@ int
 main (int argc, char *argv[])
 {
   struct gengetopt_args_info args_info;
-  char readbuf[BUFSIZ];
+  char *line = NULL;
+  size_t linelen = 0;
   char *p, *r;
   uint32_t *q;
   unsigned cmdn = 0;
@@ -196,11 +197,8 @@ main (int argc, char *argv[])
   do
     {
       if (cmdn < args_info.inputs_num)
-	{
-	  strncpy (readbuf, args_info.inputs[cmdn++], BUFSIZ - 1);
-	  readbuf[BUFSIZ - 1] = '\0';
-	}
-      else if (fgets (readbuf, BUFSIZ, stdin) == NULL)
+	line = strdup (args_info.inputs[cmdn++]);
+      else if (getline (&line, &linelen, stdin) == -1)
 	{
 	  if (feof (stdin))
 	    break;
@@ -208,12 +206,12 @@ main (int argc, char *argv[])
 	  error (EXIT_FAILURE, errno, _("Input error"));
 	}
 
-      if (readbuf[strlen (readbuf) - 1] == '\n')
-	readbuf[strlen (readbuf) - 1] = '\0';
+      if (line[strlen (line) - 1] == '\n')
+	line[strlen (line) - 1] = '\0';
 
       if (args_info.stringprep_given)
 	{
-	  p = stringprep_locale_to_utf8 (readbuf);
+	  p = stringprep_locale_to_utf8 (line);
 	  if (!p)
 	    error (EXIT_FAILURE, 0, _("Could not convert from %s to UTF-8."),
 		   stringprep_locale_charset ());
@@ -273,9 +271,10 @@ main (int argc, char *argv[])
 
       if (args_info.punycode_encode_given)
 	{
+	  char encbuf[BUFSIZ];
 	  size_t len, len2;
 
-	  p = stringprep_locale_to_utf8 (readbuf);
+	  p = stringprep_locale_to_utf8 (line);
 	  if (!p)
 	    error (EXIT_FAILURE, 0, _("Could not convert from %s to UTF-8."),
 		   stringprep_locale_charset ());
@@ -295,15 +294,15 @@ main (int argc, char *argv[])
 	    }
 
 	  len2 = BUFSIZ - 1;
-	  rc = punycode_encode (len, q, NULL, &len2, readbuf);
+	  rc = punycode_encode (len, q, NULL, &len2, encbuf);
 	  free (q);
 	  if (rc != PUNYCODE_SUCCESS)
 	    error (EXIT_FAILURE, 0, _("punycode_encode: %s"),
 		   punycode_strerror (rc));
 
-	  readbuf[len2] = '\0';
+	  encbuf[len2] = '\0';
 
-	  p = stringprep_utf8_to_locale (readbuf);
+	  p = stringprep_utf8_to_locale (encbuf);
 	  if (!p)
 	    error (EXIT_FAILURE, 0, _("Could not convert from UTF-8 to %s."),
 		   stringprep_locale_charset ());
@@ -322,7 +321,7 @@ main (int argc, char *argv[])
 	  if (!q)
 	    error (EXIT_FAILURE, ENOMEM, "malloc");
 
-	  rc = punycode_decode (strlen (readbuf), readbuf, &len, q, NULL);
+	  rc = punycode_decode (strlen (line), line, &len, q, NULL);
 	  if (rc != PUNYCODE_SUCCESS)
 	    {
 	      free (q);
@@ -358,7 +357,7 @@ main (int argc, char *argv[])
 
       if (args_info.idna_to_ascii_given)
 	{
-	  p = stringprep_locale_to_utf8 (readbuf);
+	  p = stringprep_locale_to_utf8 (line);
 	  if (!p)
 	    error (EXIT_FAILURE, 0, _("Could not convert from %s to UTF-8."),
 		   stringprep_locale_charset ());
@@ -435,7 +434,7 @@ main (int argc, char *argv[])
 
       if (args_info.idna_to_unicode_given)
 	{
-	  p = stringprep_locale_to_utf8 (readbuf);
+	  p = stringprep_locale_to_utf8 (line);
 	  if (!p)
 	    error (EXIT_FAILURE, 0, _("Could not convert from %s to UTF-8."),
 		   stringprep_locale_charset ());
@@ -516,7 +515,7 @@ main (int argc, char *argv[])
 
       if (args_info.nfkc_given)
 	{
-	  p = stringprep_locale_to_utf8 (readbuf);
+	  p = stringprep_locale_to_utf8 (line);
 	  if (!p)
 	    error (EXIT_FAILURE, 0, _("Could not convert from %s to UTF-8."),
 		   stringprep_locale_charset ());
@@ -578,5 +577,7 @@ main (int argc, char *argv[])
   while (!feof (stdin) && !ferror (stdin) && (args_info.inputs_num == 0 ||
 					      cmdn < args_info.inputs_num));
 
+  free (line);
+
   return EXIT_SUCCESS;
 }
