bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: %.1s format with vasnprintf reads more than one byte from argument


From: Bruno Haible
Subject: Re: %.1s format with vasnprintf reads more than one byte from argument
Date: Tue, 24 Feb 2009 04:17:26 +0100
User-agent: KMail/1.9.9

Hi Ben.

> But running valgrind on test-vasnprintf when USE_SNPRINTF is not
> selected, when the appended patch to test-vasnprintf.c is
> applied, makes it clear that vasnprintf() will read beyond the
> specified precision:
> 
>     ==3968== Conditional jump or move depends on uninitialised value(s)
>     ==3968==    at 0x40239D7: strlen (mc_replace_strmem.c:242)
>     ==3968==    by 0x8049065: vasnprintf (vasnprintf.c:4044)
>     ==3968==    by 0x8048655: my_asnprintf (test-vasnprintf.c:47)
>     ==3968==    by 0x80488F6: main (test-vasnprintf.c:131)
> 
> The culprit is pretty clearly this code in lib/vasnprintf.c:
> 
>                   case 's':
> [...]
>                       tmp_length = strlen (a.arg[dp->arg_index].a.a_string);
>                     break;

Well spotted. It could cause SIGSEGV in some cases.

How did you find this? I thought that valgrind only runs on modern
platforms, which all have snprintf.

> The obvious solution would be to use strnlen in place of strlen
> here, but I don't know whether you would object to the additional
> dependency.  Maybe you want to introduce a "local_strnlen"
> function instead.
> 
> Something similar would be needed for the wide-character case
> (presumably a "local_wcsnlen").

There is also a more complicated case: when a %ls directive occurs
in a char* format string, or a %s directive in a wchar_t* format string.
In these two cases, the only robust fix that I see is to implement the
entire handling of this directive ourselves. I'm applying this:


2009-02-23  Bruno Haible  <address@hidden>

        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 <address@hidden>.

*** lib/vasnprintf.c.orig       2009-02-24 04:07:58.000000000 +0100
--- lib/vasnprintf.c    2009-02-24 04:06:45.000000000 +0100
***************
*** 117,145 ****
  # 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
--- 117,122 ----
***************
*** 152,157 ****
--- 129,135 ----
  #  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,167 ****
--- 140,146 ----
  #  define DIRECTIVES char_directives
  #  define PRINTF_PARSE printf_parse
  #  define DCHAR_CPY memcpy
+ #  define DCHAR_SET memset
  # endif
  #endif
  #if WIDE_CHAR_VERSION
***************
*** 215,220 ****
--- 194,257 ----
  #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,2071 ****
--- 2103,2625 ----
                  }
              }
  #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,4047 ****
  # 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);
  #  endif
                        }
                      else
  # endif
!                       tmp_length = strlen (a.arg[dp->arg_index].a.a_string);
                      break;
  
                    case 'p':
--- 4586,4649 ----
  # if HAVE_WCHAR_T
                      if (type == TYPE_WIDE_STRING)
                        {
! #  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
!                       {
! # 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':
*** m4/vasnprintf.m4.orig       2009-02-24 04:07:58.000000000 +0100
--- m4/vasnprintf.m4    2009-02-24 04:07:07.000000000 +0100
***************
*** 1,4 ****
! # vasnprintf.m4 serial 26
  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,
--- 1,4 ----
! # 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,64 ****
    AC_REQUIRE([AC_TYPE_LONG_LONG_INT])
    AC_REQUIRE([gt_TYPE_WCHAR_T])
    AC_REQUIRE([gt_TYPE_WINT_T])
!   AC_CHECK_FUNCS([snprintf wcslen])
    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>])
--- 58,64 ----
    AC_REQUIRE([AC_TYPE_LONG_LONG_INT])
    AC_REQUIRE([gt_TYPE_WCHAR_T])
    AC_REQUIRE([gt_TYPE_WINT_T])
!   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>])
*** tests/test-snprintf-posix.h.orig    2009-02-24 04:07:58.000000000 +0100
--- tests/test-snprintf-posix.h 2009-02-24 03:52:15.000000000 +0100
***************
*** 3064,3067 ****
--- 3064,3113 ----
      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
  }
*** tests/test-sprintf-posix.h.orig     2009-02-24 04:07:58.000000000 +0100
--- tests/test-sprintf-posix.h  2009-02-24 03:52:15.000000000 +0100
***************
*** 3050,3053 ****
--- 3050,3099 ----
      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
  }
*** tests/test-vasnprintf-posix.c.orig  2009-02-24 04:07:58.000000000 +0100
--- tests/test-vasnprintf-posix.c       2009-02-24 03:52:15.000000000 +0100
***************
*** 3599,3604 ****
--- 3599,3654 ----
      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 *
*** tests/test-vasprintf-posix.c.orig   2009-02-24 04:07:58.000000000 +0100
--- tests/test-vasprintf-posix.c        2009-02-24 03:52:15.000000000 +0100
***************
*** 3579,3584 ****
--- 3579,3634 ----
      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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]