changeset 14769:8e815ef27998

strerror_r: guarantee unchanged errno POSIX guarantees that strerror doesn't change errno on success, and since we implement strerror by using strerror_r, it makes sense to make the same guarantee for strerror_r (rather, going one step further to say that sterror_r does not corrupt errno, since it returns an error value explicitly). See also http://austingroupbugs.net/view.php?id=447 * lib/strerror_r.c (strerror_r): Guarantee unchanged errno. * lib/strerror-impl.h (strerror): Set errno to match strerror_r failure. * tests/test-strerror_r.c (main): Enhance test. Signed-off-by: Eric Blake <eblake@redhat.com>
author Eric Blake <eblake@redhat.com>
date Thu, 19 May 2011 13:35:39 -0600
parents a12deda870d9
children edc214f40f31
files ChangeLog lib/strerror-impl.h lib/strerror_r.c tests/test-strerror_r.c
diffstat 4 files changed, 30 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2011-05-19  Eric Blake  <eblake@redhat.com>
+
+	strerror_r: guarantee unchanged errno
+	* lib/strerror_r.c (strerror_r): Guarantee unchanged errno.
+	* lib/strerror-impl.h (strerror): Set errno to match strerror_r
+	failure.
+	* tests/test-strerror_r.c (main): Enhance test.
+
 2011-05-19  Bruno Haible  <bruno@clisp.org>
 
 	strerror_r: Reorder #if blocks.
--- a/lib/strerror-impl.h
+++ b/lib/strerror-impl.h
@@ -36,6 +36,7 @@
     static char const fmt[] = "Unknown error (%d)";
     verify (sizeof (buf) >= sizeof (fmt) + INT_STRLEN_BOUND (n));
     sprintf (buf, fmt, n);
+    errno = ret;
     return buf;
   }
 }
--- a/lib/strerror_r.c
+++ b/lib/strerror_r.c
@@ -403,21 +403,24 @@
 
     if (msg)
       {
+        int saved_errno = errno;
         size_t len = strlen (msg);
+        int ret = ERANGE;
 
         if (len < buflen)
           {
             memcpy (buf, msg, len + 1);
-            return 0;
+            ret = 0;
           }
-        else
-          return ERANGE;
+        errno = saved_errno;
+        return ret;
       }
   }
 #endif
 
   {
     int ret;
+    int saved_errno = errno;
 
 #if USE_XPG_STRERROR_R
 
@@ -519,7 +522,6 @@
     if (errnum >= 0 && errnum < sys_nerr)
       {
 #  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
-        int saved_errno = errno;
 #   if defined __NetBSD__
         nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
         const char *errmsg =
@@ -554,7 +556,6 @@
 #  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
         if (catd != (nl_catd)-1)
           catclose (catd);
-        errno = saved_errno;
 #  endif
       }
     else
@@ -618,6 +619,7 @@
 
 #endif
 
+    errno = saved_errno;
     return ret;
   }
 }
--- a/tests/test-strerror_r.c
+++ b/tests/test-strerror_r.c
@@ -34,35 +34,45 @@
 
   /* Test results with valid errnum and enough room.  */
 
+  errno = 0;
   buf[0] = '\0';
   ASSERT (strerror_r (EACCES, buf, sizeof (buf)) == 0);
   ASSERT (buf[0] != '\0');
+  ASSERT (errno == 0);
 
+  errno = 0;
   buf[0] = '\0';
   ASSERT (strerror_r (ETIMEDOUT, buf, sizeof (buf)) == 0);
   ASSERT (buf[0] != '\0');
+  ASSERT (errno == 0);
 
+  errno = 0;
   buf[0] = '\0';
   ASSERT (strerror_r (EOVERFLOW, buf, sizeof (buf)) == 0);
   ASSERT (buf[0] != '\0');
+  ASSERT (errno == 0);
 
   /* POSIX requires strerror (0) to succeed.  Reject use of "Unknown
      error", but allow "Success", "No error", or even Solaris' "Error
      0" which are distinct patterns from true out-of-range strings.
      http://austingroupbugs.net/view.php?id=382  */
+  errno = 0;
   buf[0] = '\0';
   ret = strerror_r (0, buf, sizeof (buf));
   ASSERT (ret == 0);
   ASSERT (buf[0]);
+  ASSERT (errno == 0);
   ASSERT (strstr (buf, "nknown") == NULL);
 
   /* Test results with out-of-range errnum and enough room.  */
 
+  errno = 0;
   buf[0] = '^';
   ret = strerror_r (-3, buf, sizeof (buf));
   ASSERT (ret == 0 || ret == EINVAL);
   if (ret == 0)
     ASSERT (buf[0] != '^');
+  ASSERT (errno == 0);
 
   /* Test results with a too small buffer.  */
 
@@ -74,7 +84,9 @@
     for (i = 0; i <= len; i++)
       {
         strcpy (buf, "BADFACE");
+        errno = 0;
         ret = strerror_r (EACCES, buf, i);
+        ASSERT (errno == 0);
         if (ret == 0)
           {
             /* Truncated result.  POSIX allows this, and it actually
@@ -90,8 +102,10 @@
       }
 
     strcpy (buf, "BADFACE");
+    errno = 0;
     ret = strerror_r (EACCES, buf, len + 1);
     ASSERT (ret == 0);
+    ASSERT (errno == 0);
   }
 
   return 0;