[Top][All Lists]
[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
- %.1s format with vasnprintf reads more than one byte from argument, Ben Pfaff, 2009/02/23
- Re: %.1s format with vasnprintf reads more than one byte from argument,
Bruno Haible <=
- Re: %.1s format with vasnprintf reads more than one byte from argument, Ben Pfaff, 2009/02/23
- Re: %.1s format with vasnprintf reads more than one byte from argument, Eric Blake, 2009/02/25
- Re: %.1s format with vasnprintf reads more than one byte from argument, Eric Blake, 2009/02/26
- Re: %.1s format with vasnprintf reads more than one byte from argument, Bruno Haible, 2009/02/26
- Re: %.1s format with vasnprintf reads more than one byte from argument, Eric Blake, 2009/02/26