changeset 14762:3c012e3d3f43

strerror: enforce POSIX ruling on strerror(0) http://austingroupbugs.net/view.php?id=382 requires that strerror(0) succeed, but FreeBSD reports "Unknown error: 0" and fails with EINVAL. * m4/strerror.m4 (gl_FUNC_STRERROR_SEPARATE): Expose BSD bug. * m4/strerror_r.m4 (gl_FUNC_STRERROR_R): Likewise. * lib/strerror_r.c (rpl_strerror_r): Work around it. * doc/posix-functions/strerror.texi (strerror): Document it. * doc/posix-functions/strerror_r.texi (strerror_r): Likewise. * tests/test-strerror.c (main): Strengthen test. * tests/test-strerror_r.c (main): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
author Eric Blake <eblake@redhat.com>
date Wed, 18 May 2011 18:16:59 -0600
parents 6c5502aeb486
children 19cd3c889b58
files ChangeLog doc/posix-functions/strerror.texi doc/posix-functions/strerror_r.texi lib/strerror_r.c m4/strerror.m4 m4/strerror_r.m4 tests/test-strerror.c tests/test-strerror_r.c
diffstat 8 files changed, 89 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2011-05-19  Eric Blake  <eblake@redhat.com>
+
+	strerror: enforce POSIX ruling on strerror(0)
+	* m4/strerror.m4 (gl_FUNC_STRERROR_SEPARATE): Expose BSD bug.
+	* m4/strerror_r.m4 (gl_FUNC_STRERROR_R): Likewise.
+	* lib/strerror_r.c (rpl_strerror_r): Work around it.
+	* doc/posix-functions/strerror.texi (strerror): Document it.
+	* doc/posix-functions/strerror_r.texi (strerror_r): Likewise.
+	* tests/test-strerror.c (main): Strengthen test.
+	* tests/test-strerror_r.c (main): Likewise.
+
 2011-05-19  Paul Eggert  <eggert@cs.ucla.edu>
 
 	intprop-tests: port to older and more-pedantic compilers
--- a/doc/posix-functions/strerror.texi
+++ b/doc/posix-functions/strerror.texi
@@ -13,11 +13,18 @@
 but not defined by the system, on some platforms:
 OpenBSD 4.0, OSF/1 5.1, NonStop Kernel, Cygwin 1.5.x, mingw.
 @item
+This function reports failure (by setting @code{errno}) for
+@code{strerror(0)}, although POSIX requires this to leave @code{errno}
+unchanged and report success, on some platforms:
+FreeBSD 8.2
+@item
 This function fails to return a string for out-of-range integers on
 some platforms:
 HP-UX 11, IRIX 6.5, Solaris 8.
-(This is not a POSIX violation, but can still cause bugs because most programs
-call @code{strerror} without setting and testing @code{errno}.)
+(Some return NULL which is a POSIX violation, others return the empty
+string which is valid but not as useful); this can still cause bugs
+because most programs call @code{strerror} without setting and testing
+@code{errno}.)
 @end itemize
 
 Portability problems not fixed by Gnulib:
--- a/doc/posix-functions/strerror_r.texi
+++ b/doc/posix-functions/strerror_r.texi
@@ -37,6 +37,10 @@
 but not defined by the system, on some platforms:
 OpenBSD 4.0, OSF/1 5.1, NonStop Kernel, Cygwin 1.5.x.
 @item
+This function reports failure for @code{strerror_r(0, buf, len)},
+although POSIX requires this to succeed, on some platforms:
+FreeBSD 8.2
+@item
 This function always fails when the third argument is less than 80 on some
 platforms:
 HP-UX 11.31.
--- a/lib/strerror_r.c
+++ b/lib/strerror_r.c
@@ -436,6 +436,22 @@
     if (ret < 0)
       ret = errno;
 
+    /* FreeBSD rejects 0; see http://austingroupbugs.net/view.php?id=382.  */
+    if (errnum == 0 && ret == EINVAL)
+      {
+        if (buflen <= strlen ("Success"))
+          {
+            ret = ERANGE;
+            if (buflen)
+              buf[0] = 0;
+          }
+        else
+          {
+            ret = 0;
+            strcpy (buf, "Success");
+          }
+      }
+
 #elif USE_XPG_STRERROR_R
 
     {
--- a/m4/strerror.m4
+++ b/m4/strerror.m4
@@ -1,4 +1,4 @@
-# strerror.m4 serial 9
+# strerror.m4 serial 10
 dnl Copyright (C) 2002, 2007-2011 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -25,19 +25,18 @@
      [AC_RUN_IFELSE(
         [AC_LANG_PROGRAM(
            [[#include <string.h>
+             #include <errno.h>
            ]],
-           [[return !*strerror (-2);]])],
+           [[int result = 0;
+             if (!*strerror (-2)) result |= 1;
+             errno = 0;
+             if (!*strerror (0)) result |= 2;
+             if (errno) result |= 4;
+             return result;]])],
         [gl_cv_func_working_strerror=yes],
         [gl_cv_func_working_strerror=no],
-        [dnl Assume crossbuild works if it compiles.
-         AC_COMPILE_IFELSE(
-           [AC_LANG_PROGRAM(
-              [[#include <string.h>
-              ]],
-              [[return !*strerror (-2);]])],
-           [gl_cv_func_working_strerror=yes],
-           [gl_cv_func_working_strerror=no])
-      ])
+        [dnl Be pessimistic on cross-compiles for now.
+         gl_cv_func_working_strerror=no])
     ])
     if test $gl_cv_func_working_strerror = no; then
       dnl The system's strerror() fails to return a string for out-of-range
--- a/m4/strerror_r.m4
+++ b/m4/strerror_r.m4
@@ -1,4 +1,4 @@
-# strerror_r.m4 serial 3
+# strerror_r.m4 serial 4
 dnl Copyright (C) 2002, 2007-2011 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -41,6 +41,7 @@
         dnl AIX 6.1 strerror_r fails by returning -1, not an error number.
         dnl HP-UX 11.31 strerror_r always fails when the buffer length argument
         dnl is less than 80.
+        dnl FreeBSD 8.s strerror_r claims failure on 0
         AC_CACHE_CHECK([whether strerror_r works],
           [gl_cv_func_strerror_r_works],
           [AC_RUN_IFELSE(
@@ -53,8 +54,13 @@
                   char buf[79];
                   if (strerror_r (EACCES, buf, 0) < 0)
                     result |= 1;
-                  if (strerror_r (EACCES, buf, sizeof (buf)) != 0)
+                  errno = 0;
+                  if (strerror_r (EACCES, buf, sizeof buf) != 0)
                     result |= 2;
+                  if (strerror_r (0, buf, sizeof buf) != 0)
+                    result |= 4;
+                  if (errno)
+                    result |= 8;
                   return result;
                 ]])],
              [gl_cv_func_strerror_r_works=yes],
@@ -66,6 +72,8 @@
                 aix*)  gl_cv_func_strerror_r_works="guessing no";;
                        # Guess no on HP-UX.
                 hpux*) gl_cv_func_strerror_r_works="guessing no";;
+                       # Guess no on FreeBSD.
+                freebsd*)  gl_cv_func_strerror_r_works="guessing no";;
                        # Guess yes otherwise.
                 *)     gl_cv_func_strerror_r_works="guessing yes";;
               esac
@@ -78,7 +86,7 @@
       else
         dnl The system's strerror() has a wrong signature. Replace it.
         REPLACE_STRERROR_R=1
-        dnl glibc >= 2.3.4 has a function __xpg_strerror_r.
+        dnl glibc >= 2.3.4 and cygwin 1.7.9 have a function __xpg_strerror_r.
         AC_CHECK_FUNCS([__xpg_strerror_r])
       fi
     else
--- a/tests/test-strerror.c
+++ b/tests/test-strerror.c
@@ -33,25 +33,43 @@
 {
   char *str;
 
+  errno = 0;
   str = strerror (EACCES);
   ASSERT (str);
   ASSERT (*str);
+  ASSERT (errno == 0);
 
+  errno = 0;
   str = strerror (ETIMEDOUT);
   ASSERT (str);
   ASSERT (*str);
+  ASSERT (errno == 0);
 
+  errno = 0;
   str = strerror (EOVERFLOW);
   ASSERT (str);
   ASSERT (*str);
+  ASSERT (errno == 0);
 
+  /* POSIX requires strerror (0) to succeed; use of "Unknown error" or
+     "error 0" does not count as success, but "No error" works.
+     http://austingroupbugs.net/view.php?id=382  */
+  errno = 0;
   str = strerror (0);
   ASSERT (str);
   ASSERT (*str);
+  ASSERT (errno == 0);
+  ASSERT (strchr (str, '0') == NULL);
+  ASSERT (strstr (str, "nknown") == NULL);
 
+  /* POSIX requires strerror to produce a non-NULL result for all
+     inputs; as an extension, we also guarantee a non-empty reseult.
+     Reporting EINVAL is optional.  */
+  errno = 0;
   str = strerror (-3);
   ASSERT (str);
   ASSERT (*str);
+  ASSERT (errno == 0 || errno == EINVAL);
 
   return 0;
 }
--- a/tests/test-strerror_r.c
+++ b/tests/test-strerror_r.c
@@ -46,13 +46,17 @@
   ASSERT (strerror_r (EOVERFLOW, buf, sizeof (buf)) == 0);
   ASSERT (buf[0] != '\0');
 
-  /* Test results with out-of-range errnum and enough room.  */
-
-  buf[0] = '^';
+  /* POSIX requires strerror (0) to succeed; use of "Unknown error" or
+     "error 0" does not count as success, but "No error" works.
+     http://austingroupbugs.net/view.php?id=382  */
+  buf[0] = '\0';
   ret = strerror_r (0, buf, sizeof (buf));
-  ASSERT (ret == 0 || ret == EINVAL);
-  if (ret == 0)
-    ASSERT (buf[0] != '^');
+  ASSERT (ret == 0);
+  ASSERT (buf[0]);
+  ASSERT (strchr (buf, '0') == NULL);
+  ASSERT (strstr (buf, "nknown") == NULL);
+
+  /* Test results with out-of-range errnum and enough room.  */
 
   buf[0] = '^';
   ret = strerror_r (-3, buf, sizeof (buf));