From 1608fbc0104136faf98fcd89416a6c2ab4bff692 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 18 May 2011 18:16:59 -0600 Subject: [PATCH] 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 --- ChangeLog | 11 +++++++++++ doc/posix-functions/strerror.texi | 11 +++++++++-- doc/posix-functions/strerror_r.texi | 4 ++++ lib/strerror_r.c | 16 ++++++++++++++++ m4/strerror.m4 | 21 ++++++++++----------- m4/strerror_r.m4 | 14 +++++++++++--- tests/test-strerror.c | 20 +++++++++++++++++++- tests/test-strerror_r.c | 16 ++++++++++------ 8 files changed, 90 insertions(+), 23 deletions(-) diff --git a/ChangeLog b/ChangeLog index b21dc1bad..8c1200c75 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2011-05-19 Eric Blake + + 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 intprop-tests: port to older and more-pedantic compilers diff --git a/doc/posix-functions/strerror.texi b/doc/posix-functions/strerror.texi index 68a98da4c..6f9519a52 100644 --- a/doc/posix-functions/strerror.texi +++ b/doc/posix-functions/strerror.texi @@ -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: diff --git a/doc/posix-functions/strerror_r.texi b/doc/posix-functions/strerror_r.texi index bf431645d..9d6639eda 100644 --- a/doc/posix-functions/strerror_r.texi +++ b/doc/posix-functions/strerror_r.texi @@ -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. diff --git a/lib/strerror_r.c b/lib/strerror_r.c index 3bba26cd9..fc0603cb3 100644 --- a/lib/strerror_r.c +++ b/lib/strerror_r.c @@ -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 { diff --git a/m4/strerror.m4 b/m4/strerror.m4 index 73d1d54d8..d891031f6 100644 --- 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_DEFUN([gl_FUNC_STRERROR_SEPARATE], [AC_RUN_IFELSE( [AC_LANG_PROGRAM( [[#include + #include ]], - [[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 - ]], - [[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 diff --git a/m4/strerror_r.m4 b/m4/strerror_r.m4 index 1883458c0..ca3904c27 100644 --- 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 @@ 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 diff --git a/tests/test-strerror.c b/tests/test-strerror.c index 66dbe823d..46d339e64 100644 --- a/tests/test-strerror.c +++ b/tests/test-strerror.c @@ -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; } diff --git a/tests/test-strerror_r.c b/tests/test-strerror_r.c index b2fdaf926..0661bdfc7 100644 --- a/tests/test-strerror_r.c +++ b/tests/test-strerror_r.c @@ -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)); -- 2.11.0