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 <br...@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 <b...@cs.stanford.edu>. *** 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