strerror: enforce POSIX ruling on strerror(0)
authorEric Blake <eblake@redhat.com>
Thu, 19 May 2011 00:16:59 +0000 (18:16 -0600)
committerEric Blake <eblake@redhat.com>
Thu, 19 May 2011 15:33:59 +0000 (09:33 -0600)
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>
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

index b21dc1b..8c1200c 100644 (file)
--- 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
index 68a98da..6f9519a 100644 (file)
@@ -13,11 +13,18 @@ This function does not support the error values that are specified by POSIX
 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:
index bf43164..9d6639e 100644 (file)
@@ -37,6 +37,10 @@ This function does not support the error values that are specified by POSIX
 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.
index 3bba26c..fc0603c 100644 (file)
@@ -436,6 +436,22 @@ strerror_r (int errnum, char *buf, size_t buflen)
     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
 
     {
index 73d1d54..d891031 100644 (file)
@@ -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_DEFUN([gl_FUNC_STRERROR_SEPARATE],
      [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
index 1883458..ca3904c 100644 (file)
@@ -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 @@ AC_DEFUN([gl_FUNC_STRERROR_R],
         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 @@ AC_DEFUN([gl_FUNC_STRERROR_R],
                   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 @@ changequote(,)dnl
                 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 @@ changequote([,])dnl
       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
index 66dbe82..46d339e 100644 (file)
@@ -33,25 +33,43 @@ main (void)
 {
   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;
 }
index b2fdaf9..0661bdf 100644 (file)
@@ -46,13 +46,17 @@ main (void)
   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));