Guard against vsnprintf implementations that mishandle a size=0 argument.
authorBruno Haible <bruno@clisp.org>
Sun, 29 Apr 2007 15:40:30 +0000 (15:40 +0000)
committerBruno Haible <bruno@clisp.org>
Sun, 29 Apr 2007 15:40:30 +0000 (15:40 +0000)
ChangeLog
m4/printf.m4
m4/snprintf-posix.m4
m4/vsnprintf-posix.m4
modules/snprintf-posix-tests
modules/vsnprintf-posix-tests
tests/test-snprintf.c
tests/test-vsnprintf.c

index 2211258aca9f3a331136cf83b754d2a6b481d7e3..225d8e36aed4195e55c657868fd54574a66d3387 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2007-04-29  Bruno Haible  <bruno@clisp.org>
+
+       * m4/printf.m4 (gl_VSNPRINTF_ZEROSIZE_C99): New macro.
+       * m4/snprintf-posix.m4 (gl_FUNC_SNPRINTF_POSIX): Invoke
+       gl_VSNPRINTF_ZEROSIZE_C99. Test gl_cv_func_vsnprintf_zerosize_c99.
+       * m4/vsnprintf-posix.m4 (gl_FUNC_VSNPRINTF_POSIX): Likewise.
+       * modules/snprintf-posix-tests (Files): Add tests/test-snprintf.c.
+       (configure.ac): Define CHECK_SNPRINTF_POSIX.
+       (TESTS, check_PROGRAMS): Add test-snprintf.
+       * modules/vsnprintf-posix-tests (Files): Add tests/test-vsnprintf.c.
+       (configure.ac): Define CHECK_VSNPRINTF_POSIX.
+       (TESTS, check_PROGRAMS): Add test-vsnprintf.
+       * tests/test-snprintf.c (main) [!CHECK_SNPRINTF_POSIX]: Disable
+       assertions that fail on HP-UX, OSF/1, or IRIX.
+       * tests/test-vsnprintf.c (main) [!CHECK_VSNPRINTF_POSIX]: Likewise.
+
 2007-04-29  Bruno Haible  <bruno@clisp.org>
 
        * MODULES.html.sh (posix_functions): Remove 'contents'.
index fdabf1b354454c1f40e1b21743c4e4b2dfe79d17..3387d84675a2e14017d88a061cc942a2812bf908 100644 (file)
@@ -1,4 +1,4 @@
-# printf.m4 serial 6
+# printf.m4 serial 7
 dnl Copyright (C) 2003, 2007 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -361,6 +361,16 @@ changequote([,])dnl
 dnl Test whether the return value of the snprintf function is the number
 dnl of bytes (excluding the terminating NUL) that would have been produced
 dnl if the buffer had been large enough. (ISO C99, POSIX:2001)
+dnl For example, this test program fails on IRIX 6.5:
+dnl     ---------------------------------------------------------------------
+dnl     #include <stdio.h>
+dnl     int main()
+dnl     {
+dnl       static char buf[8];
+dnl       int retval = snprintf (buf, 3, "%d", 12345);
+dnl       return retval >= 0 && retval < 3;
+dnl     }
+dnl     ---------------------------------------------------------------------
 dnl Result is gl_cv_func_snprintf_retval_c99.
 
 AC_DEFUN([gl_SNPRINTF_RETVAL_C99],
@@ -475,6 +485,104 @@ changequote([,])dnl
     ])
 ])
 
+dnl Test whether the vsnprintf function, when passed a zero size, produces no
+dnl output. (ISO C99, POSIX:2001)
+dnl For example, snprintf nevertheless writes a NUL byte in this case
+dnl on OSF/1 5.1:
+dnl     ---------------------------------------------------------------------
+dnl     #include <stdio.h>
+dnl     int main()
+dnl     {
+dnl       static char buf[8] = "DEADBEEF";
+dnl       snprintf (buf, 0, "%d", 12345);
+dnl       return buf[0] != 'D';
+dnl     }
+dnl     ---------------------------------------------------------------------
+dnl And vsnprintf writes any output without bounds in this case, behaving like
+dnl vsprintf, on HP-UX 11 and OSF/1 5.1:
+dnl     ---------------------------------------------------------------------
+dnl     #include <stdarg.h>
+dnl     #include <stdio.h>
+dnl     static int my_snprintf (char *buf, int size, const char *format, ...)
+dnl     {
+dnl       va_list args;
+dnl       int ret;
+dnl       va_start (args, format);
+dnl       ret = vsnprintf (buf, size, format, args);
+dnl       va_end (args);
+dnl       return ret;
+dnl     }
+dnl     int main()
+dnl     {
+dnl       static char buf[8] = "DEADBEEF";
+dnl       my_snprintf (buf, 0, "%d", 12345);
+dnl       return buf[0] != 'D';
+dnl     }
+dnl     ---------------------------------------------------------------------
+dnl Result is gl_cv_func_vsnprintf_zerosize_c99.
+
+AC_DEFUN([gl_VSNPRINTF_ZEROSIZE_C99],
+[
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CACHE_CHECK([whether vsnprintf respects a zero size as in C99],
+    [gl_cv_func_vsnprintf_zerosize_c99], 
+    [
+      AC_TRY_RUN([
+#include <stdarg.h>
+#include <stdio.h>
+static int my_snprintf (char *buf, int size, const char *format, ...)
+{
+  va_list args;
+  int ret;
+  va_start (args, format);
+  ret = vsnprintf (buf, size, format, args);
+  va_end (args);
+  return ret;
+}
+int main()
+{
+  static char buf[8] = "DEADBEEF";
+  my_snprintf (buf, 0, "%d", 12345);
+  return buf[0] != 'D';
+}],
+      [gl_cv_func_vsnprintf_zerosize_c99=yes],
+      [gl_cv_func_vsnprintf_zerosize_c99=no],
+      [
+changequote(,)dnl
+       case "$host_os" in
+                               # Guess yes on glibc systems.
+         *-gnu*)               gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+                               # Guess yes on FreeBSD >= 5.
+         freebsd[1-4]*)        gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+         freebsd* | kfreebsd*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+                               # Guess yes on MacOS X >= 10.3.
+         darwin[1-6].*)        gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+         darwin*)              gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+                               # Guess yes on Solaris >= 2.6.
+         solaris2.[0-5]*)      gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+         solaris*)             gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+                               # Guess yes on AIX >= 4.
+         aix[1-3]*)            gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+         aix*)                 gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+                               # Guess yes on IRIX >= 6.5.
+         irix6.5)              gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+                               # Guess yes on NetBSD >= 3.
+         netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*)
+                               gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+         netbsd*)              gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+                               # Guess yes on BeOS.
+         beos*)                gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+                               # Guess yes on mingw.
+         mingw* | pw*)         gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+                               # If we don't know, assume the worst.
+         *)                    gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+       esac
+changequote([,])dnl
+      ])
+    ])
+])
+
 dnl The results of these tests on various platforms are:
 dnl
 dnl 1 = gl_PRINTF_SIZES_C99
@@ -486,6 +594,7 @@ dnl 6 = gl_SNPRINTF_PRESENCE
 dnl 7 = gl_SNPRINTF_TRUNCATION_C99
 dnl 8 = gl_SNPRINTF_RETVAL_C99
 dnl 9 = gl_SNPRINTF_DIRECTIVE_N
+dnl 10 = gl_VSNPRINTF_ZEROSIZE_C99
 dnl
 dnl 1 = checking whether printf supports size specifiers as in C99...
 dnl 2 = checking whether printf supports the 'a' and 'A' directives...
@@ -496,28 +605,29 @@ dnl 6 = checking for snprintf...
 dnl 7 = checking whether snprintf truncates the result as in C99...
 dnl 8 = checking whether snprintf returns a byte count as in C99...
 dnl 9 = checking whether snprintf fully supports the 'n' directive...
+dnl 10 = checking whether vsnprintf respects a zero size as in C99...
 dnl
 dnl . = yes, # = no.
 dnl
-dnl                                        1  2  3  4  5  6  7  8  9
-dnl   glibc 2.5                            .  .  .  .  .  .  .  .  .
-dnl   glibc 2.3.6                          .  #  .  .  .  .  .  .  .
-dnl   FreeBSD 5.4, 6.1                     .  ?  .  .  .  .  .  .  .
-dnl   MacOS X 10.3.9                       .  #  .  .  .  .  .  .  .
-dnl   OpenBSD 3.9, 4.0                     .  #  ?  .  .  .  .  .  ?
-dnl   Cygwin 2007                          .  #  #  .  .  .  .  .  .
-dnl   Cygwin 2006                          #  #  #  .  .  .  .  .  .
-dnl   Solaris 10                           .  #  .  .  .  .  .  .  .
-dnl   Solaris 2.6 ... 9                    #  #  #  .  .  .  .  .  .
-dnl   Solaris 2.5.1                        #  #  #  .  .  #  #  #  #
-dnl   AIX 5.2                              .  #  .  .  .  .  .  .  .
-dnl   AIX 4.3.2, 5.1                       #  #  #  .  .  .  .  .  .
-dnl   HP-UX 11.31                          .  #  .  .  .  .  .  #  #
-dnl   HP-UX 10.20, 11.00, 11.11, 11.23     #  #  #  .  .  .  .  #  #
-dnl   IRIX 6.5                             #  #  #  .  .  .  .  #  .
-dnl   OSF/1 5.1                            #  #  #  .  .  .  .  #  .
-dnl   OSF/1 4.0d                           #  #  #  .  .  #  #  #  #
-dnl   NetBSD 4.0                           .  ?  ?  .  .  .  .  .  ?
-dnl   NetBSD 3.0                           .  #  #  .  #  .  .  .  .
-dnl   BeOS                                 #  #  #  .  #  .  .  .  .
-dnl   mingw                                #  #  #  .  #  .  #  #  #
+dnl                                        1  2  3  4  5  6  7  8  9 10
+dnl   glibc 2.5                            .  .  .  .  .  .  .  .  .  .
+dnl   glibc 2.3.6                          .  #  .  .  .  .  .  .  .  .
+dnl   FreeBSD 5.4, 6.1                     .  ?  .  .  .  .  .  .  .  .
+dnl   MacOS X 10.3.9                       .  #  .  .  .  .  .  .  .  .
+dnl   OpenBSD 3.9, 4.0                     .  #  ?  .  .  .  .  .  ?  ?
+dnl   Cygwin 2007                          .  #  #  .  .  .  .  .  .  ?
+dnl   Cygwin 2006                          #  #  #  .  .  .  .  .  .  ?
+dnl   Solaris 10                           .  #  .  .  .  .  .  .  .  .
+dnl   Solaris 2.6 ... 9                    #  #  #  .  .  .  .  .  .  .
+dnl   Solaris 2.5.1                        #  #  #  .  .  #  #  #  #  #
+dnl   AIX 5.2                              .  #  .  .  .  .  .  .  .  .
+dnl   AIX 4.3.2, 5.1                       #  #  #  .  .  .  .  .  .  .
+dnl   HP-UX 11.31                          .  #  .  .  .  .  .  #  #  .
+dnl   HP-UX 10.20, 11.00, 11.11, 11.23     #  #  #  .  .  .  .  #  #  #
+dnl   IRIX 6.5                             #  #  #  .  .  .  .  #  .  .
+dnl   OSF/1 5.1                            #  #  #  .  .  .  .  #  .  #
+dnl   OSF/1 4.0d                           #  #  #  .  .  #  #  #  #  #
+dnl   NetBSD 4.0                           .  ?  ?  .  .  .  .  .  ?  ?
+dnl   NetBSD 3.0                           .  #  #  .  #  .  .  .  .  .
+dnl   BeOS                                 #  #  #  .  #  .  .  .  .  .
+dnl   mingw                                #  #  #  .  #  .  #  #  #  .
index 890605d2ae32ecded970f94acabd55776470b065..9e9ef1dfe8c387d47e4d801a5eea9cb9ce7a8d2b 100644 (file)
@@ -1,4 +1,4 @@
-# snprintf-posix.m4 serial 3
+# snprintf-posix.m4 serial 4
 dnl Copyright (C) 2007 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -18,6 +18,7 @@ AC_DEFUN([gl_FUNC_SNPRINTF_POSIX],
     gl_SNPRINTF_TRUNCATION_C99
     gl_SNPRINTF_RETVAL_C99
     gl_SNPRINTF_DIRECTIVE_N
+    gl_VSNPRINTF_ZEROSIZE_C99
     case "$gl_cv_func_printf_sizes_c99" in
       *yes)
         case "$gl_cv_func_printf_directive_a" in
@@ -34,9 +35,13 @@ AC_DEFUN([gl_FUNC_SNPRINTF_POSIX],
                               *yes)
                                 case "$gl_cv_func_snprintf_directive_n" in
                                   *yes)
-                                    # snprintf exists and is already POSIX
-                                    # compliant.
-                                    gl_cv_func_snprintf_posix=yes
+                                    case "$gl_cv_func_vsnprintf_zerosize_c99" in
+                                      *yes)
+                                        # snprintf exists and is already POSIX
+                                        # compliant.
+                                        gl_cv_func_snprintf_posix=yes
+                                        ;;
+                                    esac
                                     ;;
                                 esac
                                 ;;
index 2ea482bb1f5d75b5b5af3fbe8cdb17288f6bbabc..f2cb58232babea0f2eb584060b08e1654eb253ed 100644 (file)
@@ -1,4 +1,4 @@
-# vsnprintf-posix.m4 serial 3
+# vsnprintf-posix.m4 serial 4
 dnl Copyright (C) 2007 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -19,6 +19,7 @@ AC_DEFUN([gl_FUNC_VSNPRINTF_POSIX],
     gl_SNPRINTF_TRUNCATION_C99
     gl_SNPRINTF_RETVAL_C99
     gl_SNPRINTF_DIRECTIVE_N
+    gl_VSNPRINTF_ZEROSIZE_C99
     case "$gl_cv_func_printf_sizes_c99" in
       *yes)
         case "$gl_cv_func_printf_directive_a" in
@@ -35,9 +36,13 @@ AC_DEFUN([gl_FUNC_VSNPRINTF_POSIX],
                               *yes)
                                 case "$gl_cv_func_snprintf_directive_n" in
                                   *yes)
-                                    # vsnprintf exists and is already POSIX
-                                    # compliant.
-                                    gl_cv_func_vsnprintf_posix=yes
+                                    case "$gl_cv_func_vsnprintf_zerosize_c99" in
+                                      *yes)
+                                        # vsnprintf exists and is already POSIX
+                                        # compliant.
+                                        gl_cv_func_vsnprintf_posix=yes
+                                        ;;
+                                    esac
                                     ;;
                                 esac
                                 ;;
index 11bca695dbf70143e98f7734411d79aa9d9cfdba..913828a54c6010d36daf4fd5eb0931651897076f 100644 (file)
@@ -1,14 +1,17 @@
 Files:
 tests/test-snprintf-posix.c
 tests/test-snprintf-posix.h
+tests/test-snprintf.c
 
 Depends-on:
 stdint
 
 configure.ac:
+AC_DEFINE([CHECK_SNPRINTF_POSIX], 1,
+  [Define to 1 for strict checking in test-snprintf.c.])
 
 Makefile.am:
-TESTS += test-snprintf-posix
-check_PROGRAMS += test-snprintf-posix
+TESTS += test-snprintf-posix test-snprintf
+check_PROGRAMS += test-snprintf-posix test-snprintf
 EXTRA_DIST += test-snprintf-posix.h
 
index 9f7fdff2f9af6c29d0e1dead74d416cf36487b94..446ffd5090b98d8478394f0a3728fc6038035017 100644 (file)
@@ -1,14 +1,17 @@
 Files:
 tests/test-vsnprintf-posix.c
 tests/test-snprintf-posix.h
+tests/test-vsnprintf.c
 
 Depends-on:
 stdint
 
 configure.ac:
+AC_DEFINE([CHECK_VSNPRINTF_POSIX], 1,
+  [Define to 1 for strict checking in test-vsnprintf.c.])
 
 Makefile.am:
-TESTS += test-vsnprintf-posix
-check_PROGRAMS += test-vsnprintf-posix
+TESTS += test-vsnprintf-posix test-vsnprintf
+check_PROGRAMS += test-vsnprintf-posix test-vsnprintf
 EXTRA_DIST += test-snprintf-posix.h
 
index c1235ef03c6e711477c316ae613363c76df77186..543e02704013d70afb5042712c1c18ce74231dc4 100644 (file)
@@ -50,13 +50,18 @@ main (int argc, char *argv[])
       retval = snprintf (buf, size, "%d", 12345);
       if (size < 6)
        {
+#if CHECK_SNPRINTF_POSIX
          ASSERT (retval < 0 || retval >= size);
+#endif
          if (size > 0)
            {
              ASSERT (memcmp (buf, "12345", size - 1) == 0);
              ASSERT (buf[size - 1] == '\0' || buf[size - 1] == '0' + size);
            }
-         ASSERT (memcmp (buf + size, "DEADBEEF" + size, 8 - size) == 0);
+#if !CHECK_SNPRINTF_POSIX
+         if (size > 0)
+#endif
+           ASSERT (memcmp (buf + size, "DEADBEEF" + size, 8 - size) == 0);
        }
       else
        {
index a8ed6ec7c75523fc8dd24a0b1b3fda0cc9e9cfdd..e57e982e50d8c33c5e35f2899fffff883b6bdbe3 100644 (file)
@@ -63,13 +63,18 @@ main (int argc, char *argv[])
       retval = my_snprintf (buf, size, "%d", 12345);
       if (size < 6)
        {
+#if CHECK_VSNPRINTF_POSIX
          ASSERT (retval < 0 || retval >= size);
+#endif
          if (size > 0)
            {
              ASSERT (memcmp (buf, "12345", size - 1) == 0);
              ASSERT (buf[size - 1] == '\0' || buf[size - 1] == '0' + size);
            }
-         ASSERT (memcmp (buf + size, "DEADBEEF" + size, 8 - size) == 0);
+#if !CHECK_VSNPRINTF_POSIX
+         if (size > 0)
+#endif
+           ASSERT (memcmp (buf + size, "DEADBEEF" + size, 8 - size) == 0);
        }
       else
        {