bug-gnulib
[Top][All Lists]
Advanced

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

yet another snprintf bug


From: Bruno Haible
Subject: yet another snprintf bug
Date: Sun, 29 Apr 2007 17:46:42 +0200
User-agent: KMail/1.5.4

[v]snprintf (buf, 0, format, ...) is expected to write nothing into 'buf'
according to C99 and POSIX. On Tru64, this nevertheless writes one NUL byte.
And on HP-UX 11.00...11.23, with vsnprintf, this even overwrites as much of
'buf' as it can... This works around it.

2007-04-29  Bruno Haible  <address@hidden>

        * 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.

*** m4/printf.m4        12 Apr 2007 23:13:32 -0000      1.20
--- m4/printf.m4        29 Apr 2007 15:34:08 -0000
***************
*** 1,4 ****
! # printf.m4 serial 6
  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,
--- 1,4 ----
! # 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,366 ****
--- 361,376 ----
  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,480 ****
--- 485,588 ----
      ])
  ])
  
+ 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,491 ****
--- 594,600 ----
  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,523 ****
  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
  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                                #  #  #  .  #  .  #  #  #
--- 605,633 ----
  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 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                                #  #  #  .  #  .  #  #  #  .
*** m4/snprintf-posix.m4        11 Apr 2007 23:46:07 -0000      1.4
--- m4/snprintf-posix.m4        29 Apr 2007 15:34:08 -0000
***************
*** 1,4 ****
! # snprintf-posix.m4 serial 3
  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,
--- 1,4 ----
! # 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,23 ****
--- 18,24 ----
      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,42 ****
                                *yes)
                                  case "$gl_cv_func_snprintf_directive_n" in
                                    *yes)
!                                     # snprintf exists and is already POSIX
!                                     # compliant.
!                                     gl_cv_func_snprintf_posix=yes
                                      ;;
                                  esac
                                  ;;
--- 35,47 ----
                                *yes)
                                  case "$gl_cv_func_snprintf_directive_n" in
                                    *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
                                  ;;
*** m4/vsnprintf-posix.m4       11 Apr 2007 23:46:07 -0000      1.4
--- m4/vsnprintf-posix.m4       29 Apr 2007 15:34:08 -0000
***************
*** 1,4 ****
! # vsnprintf-posix.m4 serial 3
  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,
--- 1,4 ----
! # 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,24 ****
--- 19,25 ----
      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,43 ****
                                *yes)
                                  case "$gl_cv_func_snprintf_directive_n" in
                                    *yes)
!                                     # vsnprintf exists and is already POSIX
!                                     # compliant.
!                                     gl_cv_func_vsnprintf_posix=yes
                                      ;;
                                  esac
                                  ;;
--- 36,48 ----
                                *yes)
                                  case "$gl_cv_func_snprintf_directive_n" in
                                    *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
                                  ;;
*** modules/snprintf-posix-tests        6 Apr 2007 14:36:56 -0000       1.3
--- modules/snprintf-posix-tests        29 Apr 2007 15:34:08 -0000
***************
*** 1,14 ****
  Files:
  tests/test-snprintf-posix.c
  tests/test-snprintf-posix.h
  
  Depends-on:
  stdint
  
  configure.ac:
  
  Makefile.am:
! TESTS += test-snprintf-posix
! check_PROGRAMS += test-snprintf-posix
  EXTRA_DIST += test-snprintf-posix.h
  
--- 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 test-snprintf
! check_PROGRAMS += test-snprintf-posix test-snprintf
  EXTRA_DIST += test-snprintf-posix.h
  
*** modules/vsnprintf-posix-tests       6 Apr 2007 14:36:56 -0000       1.3
--- modules/vsnprintf-posix-tests       29 Apr 2007 15:34:08 -0000
***************
*** 1,14 ****
  Files:
  tests/test-vsnprintf-posix.c
  tests/test-snprintf-posix.h
  
  Depends-on:
  stdint
  
  configure.ac:
  
  Makefile.am:
! TESTS += test-vsnprintf-posix
! check_PROGRAMS += test-vsnprintf-posix
  EXTRA_DIST += test-snprintf-posix.h
  
--- 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 test-vsnprintf
! check_PROGRAMS += test-vsnprintf-posix test-vsnprintf
  EXTRA_DIST += test-snprintf-posix.h
  
*** tests/test-snprintf.c       29 Apr 2007 09:15:13 -0000      1.2
--- tests/test-snprintf.c       29 Apr 2007 15:34:08 -0000
***************
*** 50,62 ****
        retval = snprintf (buf, size, "%d", 12345);
        if (size < 6)
        {
          ASSERT (retval < 0 || retval >= size);
          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);
        }
        else
        {
--- 50,67 ----
        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);
            }
! #if !CHECK_SNPRINTF_POSIX
!         if (size > 0)
! #endif
!           ASSERT (memcmp (buf + size, "DEADBEEF" + size, 8 - size) == 0);
        }
        else
        {
*** tests/test-vsnprintf.c      29 Apr 2007 09:15:13 -0000      1.2
--- tests/test-vsnprintf.c      29 Apr 2007 15:34:08 -0000
***************
*** 63,75 ****
        retval = my_snprintf (buf, size, "%d", 12345);
        if (size < 6)
        {
          ASSERT (retval < 0 || retval >= size);
          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);
        }
        else
        {
--- 63,80 ----
        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);
            }
! #if !CHECK_VSNPRINTF_POSIX
!         if (size > 0)
! #endif
!           ASSERT (memcmp (buf + size, "DEADBEEF" + size, 8 - size) == 0);
        }
        else
        {





reply via email to

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