changeset 11207:4daf474e9033

vasnprintf: Fix invalid read past end of memory block.
author Bruno Haible <bruno@clisp.org>
date Tue, 24 Feb 2009 04:10:02 +0100
parents c7e84b56dbc3
children 5d1816f79525
files ChangeLog lib/vasnprintf.c m4/vasnprintf.m4 tests/test-snprintf-posix.h tests/test-sprintf-posix.h tests/test-vasnprintf-posix.c tests/test-vasprintf-posix.c
diffstat 7 files changed, 841 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Feb 22 15:05:45 2009 +0100
+++ b/ChangeLog	Tue Feb 24 04:10:02 2009 +0100
@@ -1,3 +1,19 @@
+2009-02-23  Bruno Haible  <bruno@clisp.org>
+
+	Fix invalid read past end of memory block.
+	* lib/vasnprintf.c (DCHAR_SET): Define.
+	(local_wcslen): Define only when needed.
+	(local_strnlen, local_wcsnlen): New functions.
+	(VASNPRINTF) [!USE_SNPRINTF && HAVE_WCHAR_T]: Implement the %s and %ls
+	directives that involve a conversion ourselves.
+	* m4/vasnprintf.m4 (gl_PREREQ_VASNPRINTF): Also check for strnlen,
+	wcsnlen, mbrtowc, wcrtomb.
+	* tests/test-vasnprintf-posix.c (test_function): Add tests for %.*s.
+	* tests/test-vasprintf-posix.c (test_function): Likewise.
+	* tests/test-snprintf-posix.h (test_function): Likewise.
+	* tests/test-sprintf-posix.h (test_function): Likewise.
+	Reported by Ben Pfaff <blp@cs.stanford.edu>.
+
 2009-02-22  Bruno Haible  <bruno@clisp.org>
 
 	Implement new clarified decomposition of Hangul syllables.
--- a/lib/vasnprintf.c	Sun Feb 22 15:05:45 2009 +0100
+++ b/lib/vasnprintf.c	Tue Feb 24 04:10:02 2009 +0100
@@ -117,29 +117,6 @@
 # include "fpucw.h"
 #endif
 
-#if HAVE_WCHAR_T
-# if HAVE_WCSLEN
-#  define local_wcslen wcslen
-# else
-   /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid
-      a dependency towards this library, here is a local substitute.
-      Define this substitute only once, even if this file is included
-      twice in the same compilation unit.  */
-#  ifndef local_wcslen_defined
-#   define local_wcslen_defined 1
-static size_t
-local_wcslen (const wchar_t *s)
-{
-  const wchar_t *ptr;
-
-  for (ptr = s; *ptr != (wchar_t) 0; ptr++)
-    ;
-  return ptr - s;
-}
-#  endif
-# endif
-#endif
-
 /* Default parameters.  */
 #ifndef VASNPRINTF
 # if WIDE_CHAR_VERSION
@@ -152,6 +129,7 @@
 #  define DIRECTIVES wchar_t_directives
 #  define PRINTF_PARSE wprintf_parse
 #  define DCHAR_CPY wmemcpy
+#  define DCHAR_SET wmemset
 # else
 #  define VASNPRINTF vasnprintf
 #  define FCHAR_T char
@@ -162,6 +140,7 @@
 #  define DIRECTIVES char_directives
 #  define PRINTF_PARSE printf_parse
 #  define DCHAR_CPY memcpy
+#  define DCHAR_SET memset
 # endif
 #endif
 #if WIDE_CHAR_VERSION
@@ -215,6 +194,64 @@
 #undef remainder
 #define remainder rem
 
+#if !USE_SNPRINTF && !WIDE_CHAR_VERSION
+# if (HAVE_STRNLEN && !defined _AIX)
+#  define local_strnlen strnlen
+# else
+#  ifndef local_strnlen_defined
+#   define local_strnlen_defined 1
+static size_t
+local_strnlen (const char *string, size_t maxlen)
+{
+  const char *end = memchr (string, '\0', maxlen);
+  return end ? (size_t) (end - string) : maxlen;
+}
+#  endif
+# endif
+#endif
+
+#if !USE_SNPRINTF && HAVE_WCHAR_T && (WIDE_CHAR_VERSION || DCHAR_IS_TCHAR)
+# if HAVE_WCSLEN
+#  define local_wcslen wcslen
+# else
+   /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid
+      a dependency towards this library, here is a local substitute.
+      Define this substitute only once, even if this file is included
+      twice in the same compilation unit.  */
+#  ifndef local_wcslen_defined
+#   define local_wcslen_defined 1
+static size_t
+local_wcslen (const wchar_t *s)
+{
+  const wchar_t *ptr;
+
+  for (ptr = s; *ptr != (wchar_t) 0; ptr++)
+    ;
+  return ptr - s;
+}
+#  endif
+# endif
+#endif
+
+#if !USE_SNPRINTF && HAVE_WCHAR_T && WIDE_CHAR_VERSION
+# if HAVE_WCSNLEN
+#  define local_wcsnlen wcsnlen
+# else
+#  ifndef local_wcsnlen_defined
+#   define local_wcsnlen_defined 1
+static size_t
+local_wcsnlen (const wchar_t *s, size_t maxlen)
+{
+  const wchar_t *ptr;
+
+  for (ptr = s; maxlen > 0 && *ptr != (wchar_t) 0; ptr++, maxlen--)
+    ;
+  return ptr - s;
+}
+#  endif
+# endif
+#endif
+
 #if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL
 /* Determine the decimal-point character according to the current locale.  */
 # ifndef decimal_point_char_defined
@@ -2066,6 +2103,523 @@
 		  }
 	      }
 #endif
+#if !USE_SNPRINTF && HAVE_WCHAR_T
+	    else if (dp->conversion == 's'
+# if WIDE_CHAR_VERSION
+		     && a.arg[dp->arg_index].type != TYPE_WIDE_STRING
+# else
+		     && a.arg[dp->arg_index].type == TYPE_WIDE_STRING
+# endif
+		    )
+	      {
+		/* The normal handling of the 's' directive below requires
+		   allocating a temporary buffer.  The determination of its
+		   length (tmp_length), in the case when a precision is
+		   specified, below requires a conversion between a char[]
+		   string and a wchar_t[] wide string.  It could be done, but
+		   we have no guarantee that the implementation of sprintf will
+		   use the exactly same algorithm.  Without this guarantee, it
+		   is possible to have buffer overrun bugs.  In order to avoid
+		   such bugs, we implement the entire processing of the 's'
+		   directive ourselves.  */
+		int flags = dp->flags;
+		int has_width;
+		size_t width;
+		int has_precision;
+		size_t precision;
+
+		has_width = 0;
+		width = 0;
+		if (dp->width_start != dp->width_end)
+		  {
+		    if (dp->width_arg_index != ARG_NONE)
+		      {
+			int arg;
+
+			if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+			  abort ();
+			arg = a.arg[dp->width_arg_index].a.a_int;
+			if (arg < 0)
+			  {
+			    /* "A negative field width is taken as a '-' flag
+			        followed by a positive field width."  */
+			    flags |= FLAG_LEFT;
+			    width = (unsigned int) (-arg);
+			  }
+			else
+			  width = arg;
+		      }
+		    else
+		      {
+			const FCHAR_T *digitp = dp->width_start;
+
+			do
+			  width = xsum (xtimes (width, 10), *digitp++ - '0');
+			while (digitp != dp->width_end);
+		      }
+		    has_width = 1;
+		  }
+
+		has_precision = 0;
+		precision = 6;
+		if (dp->precision_start != dp->precision_end)
+		  {
+		    if (dp->precision_arg_index != ARG_NONE)
+		      {
+			int arg;
+
+			if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
+			  abort ();
+			arg = a.arg[dp->precision_arg_index].a.a_int;
+			/* "A negative precision is taken as if the precision
+			    were omitted."  */
+			if (arg >= 0)
+			  {
+			    precision = arg;
+			    has_precision = 1;
+			  }
+		      }
+		    else
+		      {
+			const FCHAR_T *digitp = dp->precision_start + 1;
+
+			precision = 0;
+			while (digitp != dp->precision_end)
+			  precision = xsum (xtimes (precision, 10), *digitp++ - '0');
+			has_precision = 1;
+		      }
+		  }
+
+# if WIDE_CHAR_VERSION
+		/* %s in vasnwprintf.  See the specification of fwprintf.  */
+		{
+		  const char *arg = a.arg[dp->arg_index].a.a_string;
+		  const char *arg_end;
+		  size_t characters;
+
+		  if (has_precision)
+		    {
+		      /* Use only as many bytes as needed to produce PRECISION
+			 wide characters, from the left.  */
+#  if HAVE_MBRTOWC
+		      mbstate_t state;
+		      memset (&state, '\0', sizeof (mbstate_t));
+#  endif
+		      arg_end = arg;
+		      characters = 0;
+		      for (; precision > 0; precision--)
+			{
+			  int count;
+#  if HAVE_MBRTOWC
+			  count = mbrlen (arg_end, MB_CUR_MAX, &state);
+#  else
+			  count = mblen (arg_end, MB_CUR_MAX);
+#  endif
+			  if (count == 0)
+			    /* Found the terminating NUL.  */
+			    break;
+			  if (count < 0)
+			    {
+			      /* Invalid or incomplete multibyte character.  */
+			      if (!(result == resultbuf || result == NULL))
+				free (result);
+			      if (buf_malloced != NULL)
+				free (buf_malloced);
+			      CLEANUP ();
+			      errno = EILSEQ;
+			      return NULL;
+			    }
+			  arg_end += count;
+			  characters++;
+			}
+		    }
+		  else if (has_width)
+		    {
+		      /* Use the entire string, and count the number of wide
+			 characters.  */
+#  if HAVE_MBRTOWC
+		      mbstate_t state;
+		      memset (&state, '\0', sizeof (mbstate_t));
+#  endif
+		      arg_end = arg;
+		      characters = 0;
+		      for (;;)
+			{
+			  int count;
+#  if HAVE_MBRTOWC
+			  count = mbrlen (arg_end, MB_CUR_MAX, &state);
+#  else
+			  count = mblen (arg_end, MB_CUR_MAX);
+#  endif
+			  if (count == 0)
+			    /* Found the terminating NUL.  */
+			    break;
+			  if (count < 0)
+			    {
+			      /* Invalid or incomplete multibyte character.  */
+			      if (!(result == resultbuf || result == NULL))
+				free (result);
+			      if (buf_malloced != NULL)
+				free (buf_malloced);
+			      CLEANUP ();
+			      errno = EILSEQ;
+			      return NULL;
+			    }
+			  arg_end += count;
+			  characters++;
+			}
+		    }
+		  else
+		    {
+		      /* Use the entire string.  */
+		      arg_end = arg + strlen (arg);
+		      /* The number of characters doesn't matter.  */
+		      characters = 0;
+		    }
+
+		  if (has_width && width > characters
+		      && !(dp->flags & FLAG_LEFT))
+		    {
+		      size_t n = width - characters;
+		      ENSURE_ALLOCATION (xsum (length, n));
+		      DCHAR_SET (result + length, ' ', n);
+		      length += n;
+		    }
+
+		  if (has_precision || has_width)
+		    {
+		      /* We know the number of wide characters in advance.  */
+		      size_t remaining;
+#  if HAVE_MBRTOWC
+		      mbstate_t state;
+		      memset (&state, '\0', sizeof (mbstate_t));
+#  endif
+		      ENSURE_ALLOCATION (xsum (length, characters));
+		      for (remaining = characters; remaining > 0; remaining--)
+			{
+			  wchar_t wc;
+			  int count;
+#  if HAVE_MBRTOWC
+			  count = mbrtowc (&wc, arg, arg_end - arg, &state);
+#  else
+			  count = mbtowc (&wc, arg, arg_end - arg);
+#  endif
+			  if (count <= 0)
+			    /* mbrtowc not consistent with mbrlen, or mbtowc
+			       not consistent with mblen.  */
+			    abort ();
+			  result[length++] = wc;
+			  arg += count;
+			}
+		      if (!(arg == arg_end))
+			abort ();
+		    }
+		  else
+		    {
+#  if HAVE_MBRTOWC
+		      mbstate_t state;
+		      memset (&state, '\0', sizeof (mbstate_t));
+#  endif
+		      while (arg < arg_end)
+			{
+			  wchar_t wc;
+			  int count;
+#  if HAVE_MBRTOWC
+			  count = mbrtowc (&wc, arg, arg_end - arg, &state);
+#  else
+			  count = mbtowc (&wc, arg, arg_end - arg);
+#  endif
+			  if (count <= 0)
+			    /* mbrtowc not consistent with mbrlen, or mbtowc
+			       not consistent with mblen.  */
+			    abort ();
+			  ENSURE_ALLOCATION (xsum (length, 1));
+			  result[length++] = wc;
+			  arg += count;
+			}
+		    }
+
+		  if (has_width && width > characters
+		      && (dp->flags & FLAG_LEFT))
+		    {
+		      size_t n = width - characters;
+		      ENSURE_ALLOCATION (xsum (length, n));
+		      DCHAR_SET (result + length, ' ', n);
+		      length += n;
+		    }
+		}
+# else
+		/* %ls in vasnprintf.  See the specification of fprintf.  */
+		{
+		  const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string;
+		  const wchar_t *arg_end;
+		  size_t characters;
+#  if !DCHAR_IS_TCHAR
+		  /* This code assumes that TCHAR_T is 'char'.  */
+		  typedef int TCHAR_T_verify[2 * (sizeof (TCHAR_T) == 1) - 1];
+		  TCHAR_T *tmpsrc;
+		  DCHAR_T *tmpdst;
+		  size_t tmpdst_len;
+#  endif
+		  size_t w;
+
+		  if (has_precision)
+		    {
+		      /* Use only as many wide characters as needed to produce
+			 at most PRECISION bytes, from the left.  */
+#  if HAVE_WCRTOMB
+		      mbstate_t state;
+		      memset (&state, '\0', sizeof (mbstate_t));
+#  endif
+		      arg_end = arg;
+		      characters = 0;
+		      while (precision > 0)
+			{
+			  char buf[64]; /* Assume MB_CUR_MAX <= 64.  */
+			  int count;
+
+			  if (*arg_end == 0)
+			    /* Found the terminating null wide character.  */
+			    break;
+#  if HAVE_WCRTOMB
+			  count = wcrtomb (buf, *arg_end, &state);
+#  else
+			  count = wctomb (buf, *arg_end);
+#  endif
+			  if (count < 0)
+			    {
+			      /* Cannot convert.  */
+			      if (!(result == resultbuf || result == NULL))
+				free (result);
+			      if (buf_malloced != NULL)
+				free (buf_malloced);
+			      CLEANUP ();
+			      errno = EILSEQ;
+			      return NULL;
+			    }
+			  if (precision < count)
+			    break;
+			  arg_end++;
+			  characters += count;
+			  precision -= count;
+			}
+		    }
+#  if DCHAR_IS_TCHAR
+		  else if (has_width)
+#  else
+		  else
+#  endif
+		    {
+		      /* Use the entire string, and count the number of
+			 bytes.  */
+#  if HAVE_WCRTOMB
+		      mbstate_t state;
+		      memset (&state, '\0', sizeof (mbstate_t));
+#  endif
+		      arg_end = arg;
+		      characters = 0;
+		      for (;;)
+			{
+			  char buf[64]; /* Assume MB_CUR_MAX <= 64.  */
+			  int count;
+
+			  if (*arg_end == 0)
+			    /* Found the terminating null wide character.  */
+			    break;
+#  if HAVE_WCRTOMB
+			  count = wcrtomb (buf, *arg_end, &state);
+#  else
+			  count = wctomb (buf, *arg_end);
+#  endif
+			  if (count < 0)
+			    {
+			      /* Cannot convert.  */
+			      if (!(result == resultbuf || result == NULL))
+				free (result);
+			      if (buf_malloced != NULL)
+				free (buf_malloced);
+			      CLEANUP ();
+			      errno = EILSEQ;
+			      return NULL;
+			    }
+			  arg_end++;
+			  characters += count;
+			}
+		    }
+#  if DCHAR_IS_TCHAR
+		  else
+		    {
+		      /* Use the entire string.  */
+		      arg_end = arg + local_wcslen (arg);
+		      /* The number of bytes doesn't matter.  */
+		      characters = 0;
+		    }
+#  endif
+
+#  if !DCHAR_IS_TCHAR
+		  /* Convert the string into a piece of temporary memory.  */
+		  tmpsrc = (TCHAR_T *) malloc (characters * sizeof (TCHAR_T));
+		  if (tmpsrc == NULL)
+		    goto out_of_memory;
+		  {
+		    TCHAR_T *tmpptr = tmpsrc;
+		    size_t remaining;
+#   if HAVE_WCRTOMB
+		    mbstate_t state;
+		    memset (&state, '\0', sizeof (mbstate_t));
+#   endif
+		    for (remaining = characters; remaining > 0; )
+		      {
+			char buf[64]; /* Assume MB_CUR_MAX <= 64.  */
+			int count;
+
+			if (*arg == 0)
+			  abort ();
+#   if HAVE_WCRTOMB
+			count = wcrtomb (buf, *arg, &state);
+#   else
+			count = wctomb (buf, *arg);
+#   endif
+			if (count <= 0)
+			  /* Inconsistency.  */
+			  abort ();
+			memcpy (tmpptr, buf, count);
+			tmpptr += count;
+			arg++;
+			remaining -= count;
+		      }
+		    if (!(arg == arg_end))
+		      abort ();
+		  }
+
+		  /* Convert from TCHAR_T[] to DCHAR_T[].  */
+		  tmpdst = NULL;
+		  tmpdst_len = 0;
+		  if (DCHAR_CONV_FROM_ENCODING (locale_charset (),
+						iconveh_question_mark,
+						tmpsrc, characters,
+						NULL,
+						&tmpdst, &tmpdst_len)
+		      < 0)
+		    {
+		      int saved_errno = errno;
+		      free (tmpsrc);
+		      if (!(result == resultbuf || result == NULL))
+			free (result);
+		      if (buf_malloced != NULL)
+			free (buf_malloced);
+		      CLEANUP ();
+		      errno = saved_errno;
+		      return NULL;
+		    }
+		  free (tmpsrc);
+#  endif
+
+		  if (has_width)
+		    {
+#  if ENABLE_UNISTDIO
+		      /* Outside POSIX, it's preferrable to compare the width
+			 against the number of _characters_ of the converted
+			 value.  */
+		      w = DCHAR_MBSNLEN (result + length, characters);
+#  else
+		      /* The width is compared against the number of _bytes_
+			 of the converted value, says POSIX.  */
+		      w = characters;
+#  endif
+		    }
+		  else
+		    /* w doesn't matter.  */
+		    w = 0;
+
+		  if (has_width && width > w
+		      && !(dp->flags & FLAG_LEFT))
+		    {
+		      size_t n = width - w;
+		      ENSURE_ALLOCATION (xsum (length, n));
+		      DCHAR_SET (result + length, ' ', n);
+		      length += n;
+		    }
+
+#  if DCHAR_IS_TCHAR
+		  if (has_precision || has_width)
+		    {
+		      /* We know the number of bytes in advance.  */
+		      size_t remaining;
+#   if HAVE_WCRTOMB
+		      mbstate_t state;
+		      memset (&state, '\0', sizeof (mbstate_t));
+#   endif
+		      ENSURE_ALLOCATION (xsum (length, characters));
+		      for (remaining = characters; remaining > 0; )
+			{
+			  char buf[64]; /* Assume MB_CUR_MAX <= 64.  */
+			  int count;
+
+			  if (*arg == 0)
+			    abort ();
+#   if HAVE_WCRTOMB
+			  count = wcrtomb (buf, *arg, &state);
+#   else
+			  count = wctomb (buf, *arg);
+#   endif
+			  if (count <= 0)
+			    /* Inconsistency.  */
+			    abort ();
+			  memcpy (result + length, buf, count);
+			  length += count;
+			  arg++;
+			  remaining -= count;
+			}
+		      if (!(arg == arg_end))
+			abort ();
+		    }
+		  else
+		    {
+#   if HAVE_WCRTOMB
+		      mbstate_t state;
+		      memset (&state, '\0', sizeof (mbstate_t));
+#   endif
+		      while (arg < arg_end)
+			{
+			  char buf[64]; /* Assume MB_CUR_MAX <= 64.  */
+			  int count;
+
+			  if (*arg == 0)
+			    abort ();
+#   if HAVE_WCRTOMB
+			  count = wcrtomb (buf, *arg, &state);
+#   else
+			  count = wctomb (buf, *arg);
+#   endif
+			  if (count <= 0)
+			    /* Inconsistency.  */
+			    abort ();
+			  ENSURE_ALLOCATION (xsum (length, count));
+			  memcpy (result + length, buf, count);
+			  length += count;
+			  arg++;
+			}
+		    }
+#  else
+		  ENSURE_ALLOCATION (xsum (length, tmpdst_len));
+		  DCHAR_CPY (result + length, tmpdst, tmpdst_len);
+		  free (tmpdst);
+		  length += tmpdst_len;
+#  endif
+
+		  if (has_width && width > w
+		      && (dp->flags & FLAG_LEFT))
+		    {
+		      size_t n = width - w;
+		      ENSURE_ALLOCATION (xsum (length, n));
+		      DCHAR_SET (result + length, ' ', n);
+		      length += n;
+		    }
+		}
+	      }
+# endif
+#endif
 #if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL
 	    else if ((dp->conversion == 'a' || dp->conversion == 'A')
 # if !(NEED_PRINTF_DIRECTIVE_A || (NEED_PRINTF_LONG_DOUBLE && NEED_PRINTF_DOUBLE))
@@ -4032,16 +4586,64 @@
 # if HAVE_WCHAR_T
 		      if (type == TYPE_WIDE_STRING)
 			{
-			  tmp_length =
-			    local_wcslen (a.arg[dp->arg_index].a.a_wide_string);
-
-#  if !WIDE_CHAR_VERSION
-			  tmp_length = xtimes (tmp_length, MB_CUR_MAX);
+#  if WIDE_CHAR_VERSION
+			  /* ISO C says about %ls in fwprintf:
+			       "If the precision is not specified or is greater
+				than the size of the array, the array shall
+				contain a null wide character."
+			     So if there is a precision, we must not use
+			     wcslen.  */
+			  const wchar_t *arg =
+			    a.arg[dp->arg_index].a.a_wide_string;
+
+			  if (has_precision)
+			    tmp_length = local_wcsnlen (arg, precision);
+			  else
+			    tmp_length = local_wcslen (arg);
+#  else
+			  /* ISO C says about %ls in fprintf:
+			       "If a precision is specified, no more than that
+				many bytes are written (including shift
+				sequences, if any), and the array shall contain
+				a null wide character if, to equal the
+				multibyte character sequence length given by
+				the precision, the function would need to
+				access a wide character one past the end of the
+				array."
+			     So if there is a precision, we must not use
+			     wcslen.  */
+			  /* This case has already been handled above.  */
+			  abort ();
 #  endif
 			}
 		      else
 # endif
-			tmp_length = strlen (a.arg[dp->arg_index].a.a_string);
+			{
+# if WIDE_CHAR_VERSION
+			  /* ISO C says about %s in fwprintf:
+			       "If the precision is not specified or is greater
+				than the size of the converted array, the
+				converted array shall contain a null wide
+				character."
+			     So if there is a precision, we must not use
+			     strlen.  */
+			  /* This case has already been handled above.  */
+			  abort ();
+# else
+			  /* ISO C says about %s in fprintf:
+			       "If the precision is not specified or greater
+				than the size of the array, the array shall
+				contain a null character."
+			     So if there is a precision, we must not use
+			     strlen.  */
+			  const char *arg = a.arg[dp->arg_index].a.a_string;
+
+			  if (has_precision)
+			    tmp_length = local_strnlen (arg, precision);
+			  else
+			    tmp_length = strlen (arg);
+# endif
+			}
 		      break;
 
 		    case 'p':
--- a/m4/vasnprintf.m4	Sun Feb 22 15:05:45 2009 +0100
+++ b/m4/vasnprintf.m4	Tue Feb 24 04:10:02 2009 +0100
@@ -1,4 +1,4 @@
-# vasnprintf.m4 serial 26
+# vasnprintf.m4 serial 27
 dnl Copyright (C) 2002-2004, 2006-2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -58,7 +58,7 @@
   AC_REQUIRE([AC_TYPE_LONG_LONG_INT])
   AC_REQUIRE([gt_TYPE_WCHAR_T])
   AC_REQUIRE([gt_TYPE_WINT_T])
-  AC_CHECK_FUNCS([snprintf wcslen])
+  AC_CHECK_FUNCS([snprintf strnlen wcslen wcsnlen mbrtowc wcrtomb])
   dnl Use the _snprintf function only if it is declared (because on NetBSD it
   dnl is defined as a weak alias of snprintf; we prefer to use the latter).
   AC_CHECK_DECLS([_snprintf], , , [#include <stdio.h>])
--- a/tests/test-snprintf-posix.h	Sun Feb 22 15:05:45 2009 +0100
+++ b/tests/test-snprintf-posix.h	Tue Feb 24 04:10:02 2009 +0100
@@ -3064,4 +3064,50 @@
     ASSERT (strcmp (result + 4000, " 99") == 0);
     ASSERT (retval == strlen (result));
   }
+
+  /* Test the support of the %s format directive.  */
+
+  /* To verify that these tests succeed, it is necessary to run them under
+     a tool that checks against invalid memory accesses, such as ElectricFence
+     or "valgrind --tool=memcheck".  */
+  {
+    size_t i;
+
+    for (i = 1; i <= 8; i++)
+      {
+	char *block;
+	char result[5000];
+	int retval;
+
+	block = (char *) malloc (i);
+	memcpy (block, "abcdefgh", i);
+	retval = my_snprintf (result, sizeof (result), "%.*s", (int) i, block);
+	ASSERT (memcmp (result, block, i) == 0);
+	ASSERT (result[i] == '\0');
+	ASSERT (retval == strlen (result));
+	free (block);
+      }
+  }
+#if HAVE_WCHAR_T
+  {
+    size_t i;
+
+    for (i = 1; i <= 8; i++)
+      {
+	wchar_t *block;
+	size_t j;
+	char result[5000];
+	int retval;
+
+	block = (wchar_t *) malloc (i * sizeof (wchar_t));
+	for (j = 0; j < i; j++)
+	  block[j] = "abcdefgh"[j];
+	retval = my_snprintf (result, sizeof (result), "%.*ls", (int) i, block);
+	ASSERT (memcmp (result, "abcdefgh", i) == 0);
+	ASSERT (result[i] == '\0');
+	ASSERT (retval == strlen (result));
+	free (block);
+      }
+  }
+#endif
 }
--- a/tests/test-sprintf-posix.h	Sun Feb 22 15:05:45 2009 +0100
+++ b/tests/test-sprintf-posix.h	Tue Feb 24 04:10:02 2009 +0100
@@ -3050,4 +3050,50 @@
     ASSERT (strcmp (result + 4000, " 99") == 0);
     ASSERT (retval == strlen (result));
   }
+
+  /* Test the support of the %s format directive.  */
+
+  /* To verify that these tests succeed, it is necessary to run them under
+     a tool that checks against invalid memory accesses, such as ElectricFence
+     or "valgrind --tool=memcheck".  */
+  {
+    size_t i;
+
+    for (i = 1; i <= 8; i++)
+      {
+	char *block;
+	char result[5000];
+	int retval;
+
+	block = (char *) malloc (i);
+	memcpy (block, "abcdefgh", i);
+	retval = my_sprintf (result, "%.*s", (int) i, block);
+	ASSERT (memcmp (result, block, i) == 0);
+	ASSERT (result[i] == '\0');
+	ASSERT (retval == strlen (result));
+	free (block);
+      }
+  }
+#if HAVE_WCHAR_T
+  {
+    size_t i;
+
+    for (i = 1; i <= 8; i++)
+      {
+	wchar_t *block;
+	size_t j;
+	char result[5000];
+	int retval;
+
+	block = (wchar_t *) malloc (i * sizeof (wchar_t));
+	for (j = 0; j < i; j++)
+	  block[j] = "abcdefgh"[j];
+	retval = my_sprintf (result, "%.*ls", (int) i, block);
+	ASSERT (memcmp (result, "abcdefgh", i) == 0);
+	ASSERT (result[i] == '\0');
+	ASSERT (retval == strlen (result));
+	free (block);
+      }
+  }
+#endif
 }
--- a/tests/test-vasnprintf-posix.c	Sun Feb 22 15:05:45 2009 +0100
+++ b/tests/test-vasnprintf-posix.c	Tue Feb 24 04:10:02 2009 +0100
@@ -3599,6 +3599,56 @@
     ASSERT (length == strlen (result));
     free (result);
   }
+
+  /* Test the support of the %s format directive.  */
+
+  /* To verify that these tests succeed, it is necessary to run them under
+     a tool that checks against invalid memory accesses, such as ElectricFence
+     or "valgrind --tool=memcheck".  */
+  {
+    size_t i;
+
+    for (i = 1; i <= 8; i++)
+      {
+	char *block;
+	size_t length;
+	char *result;
+
+	block = (char *) malloc (i);
+	memcpy (block, "abcdefgh", i);
+	result = my_asnprintf (NULL, &length, "%.*s", (int) i, block);
+	ASSERT (result != NULL);
+	ASSERT (memcmp (result, block, i) == 0);
+	ASSERT (result[i] == '\0');
+	ASSERT (length == strlen (result));
+	free (result);
+	free (block);
+      }
+  }
+#if HAVE_WCHAR_T
+  {
+    size_t i;
+
+    for (i = 1; i <= 8; i++)
+      {
+	wchar_t *block;
+	size_t j;
+	size_t length;
+	char *result;
+
+	block = (wchar_t *) malloc (i * sizeof (wchar_t));
+	for (j = 0; j < i; j++)
+	  block[j] = "abcdefgh"[j];
+	result = my_asnprintf (NULL, &length, "%.*ls", (int) i, block);
+	ASSERT (result != NULL);
+	ASSERT (memcmp (result, "abcdefgh", i) == 0);
+	ASSERT (result[i] == '\0');
+	ASSERT (length == strlen (result));
+	free (result);
+	free (block);
+      }
+  }
+#endif
 }
 
 static char *
--- a/tests/test-vasprintf-posix.c	Sun Feb 22 15:05:45 2009 +0100
+++ b/tests/test-vasprintf-posix.c	Tue Feb 24 04:10:02 2009 +0100
@@ -3579,6 +3579,56 @@
     ASSERT (retval == strlen (result));
     free (result);
   }
+
+  /* Test the support of the %s format directive.  */
+
+  /* To verify that these tests succeed, it is necessary to run them under
+     a tool that checks against invalid memory accesses, such as ElectricFence
+     or "valgrind --tool=memcheck".  */
+  {
+    size_t i;
+
+    for (i = 1; i <= 8; i++)
+      {
+	char *block;
+	char *result;
+	int retval;
+
+	block = (char *) malloc (i);
+	memcpy (block, "abcdefgh", i);
+	retval = my_asprintf (&result, "%.*s", (int) i, block);
+	ASSERT (result != NULL);
+	ASSERT (memcmp (result, block, i) == 0);
+	ASSERT (result[i] == '\0');
+	ASSERT (retval == strlen (result));
+	free (result);
+	free (block);
+      }
+  }
+#if HAVE_WCHAR_T
+  {
+    size_t i;
+
+    for (i = 1; i <= 8; i++)
+      {
+	wchar_t *block;
+	size_t j;
+	char *result;
+	int retval;
+
+	block = (wchar_t *) malloc (i * sizeof (wchar_t));
+	for (j = 0; j < i; j++)
+	  block[j] = "abcdefgh"[j];
+	retval = my_asprintf (&result, "%.*ls", (int) i, block);
+	ASSERT (result != NULL);
+	ASSERT (memcmp (result, "abcdefgh", i) == 0);
+	ASSERT (result[i] == '\0');
+	ASSERT (retval == strlen (result));
+	free (result);
+	free (block);
+      }
+  }
+#endif
 }
 
 static int