From 2f0357585d836b7349192ea4276b78adfd551cbc Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Sat, 21 May 2011 10:08:28 -0600 Subject: [PATCH] strerror_r: avoid clobbering strerror on cygwin Avoid cygwin strerror_r, since it clobbers strerror buffer through cygwin 1.7.9. __xpg_strerror_r is okay, but if a program is compiled on cygwin 1.7.8 or earlier, it is not available. * lib/strerror_r.c (strerror_r): Don't use cygwin's strerror_r; fall back instead to sys_errlist. * modules/strerror (configure.ac): Add witness. * tests/test-strerror_r.c (main): Enhance test. * doc/posix-functions/strerror_r.texi (strerror_r): Document it. * tests/test-perror2.c (main): Free memory before exit. Signed-off-by: Eric Blake --- ChangeLog | 10 ++++++ doc/posix-functions/strerror_r.texi | 3 ++ lib/strerror_r.c | 47 ++++++---------------------- modules/strerror | 1 + tests/test-perror2.c | 5 +++ tests/test-strerror_r.c | 48 +++++++++++++++++++++++++++++ 6 files changed, 77 insertions(+), 37 deletions(-) diff --git a/ChangeLog b/ChangeLog index 56ec3b6fe0..c46dc3a649 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2011-05-21 Eric Blake + + strerror_r: avoid clobbering strerror on cygwin + * lib/strerror_r.c (strerror_r): Don't use cygwin's strerror_r; + fall back instead to sys_errlist. + * modules/strerror (configure.ac): Add witness. + * tests/test-strerror_r.c (main): Enhance test. + * doc/posix-functions/strerror_r.texi (strerror_r): Document it. + * tests/test-perror2.c (main): Free memory before exit. + 2011-05-21 Bruno Haible mkdtemp: Use gnulib naming conventions. diff --git a/doc/posix-functions/strerror_r.texi b/doc/posix-functions/strerror_r.texi index 35cee29baa..21e4181186 100644 --- a/doc/posix-functions/strerror_r.texi +++ b/doc/posix-functions/strerror_r.texi @@ -22,6 +22,9 @@ is essentially equivalent to this code using the glibc function: char *s = strerror_r (err, buf, buflen); @end smallexample @item +This function clobbers the @code{strerror} buffer on some platforms: +Cygwin 1.7.9. +@item This function is sometimes not declared in @code{} on some platforms: glibc 2.8, OSF/1 5.1. @item diff --git a/lib/strerror_r.c b/lib/strerror_r.c index 2144fc652e..65d230ac5b 100644 --- a/lib/strerror_r.c +++ b/lib/strerror_r.c @@ -38,7 +38,7 @@ # define USE_XPG_STRERROR_R 1 -#elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__) +#elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) /* The system's strerror_r function is OK, except that its third argument is 'int', not 'size_t', or its return type is wrong. */ @@ -47,14 +47,16 @@ # define USE_SYSTEM_STRERROR_R 1 -#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */ +#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */ -/* Use the system's strerror(). */ +/* Use the system's strerror(). Exclude glibc and cygwin because the + system strerror_r has the wrong return type, and cygwin 1.7.9 + strerror_r clobbers strerror. */ # undef strerror # define USE_SYSTEM_STRERROR 1 -# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) +# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__ /* No locking needed. */ @@ -75,7 +77,7 @@ extern char *sys_errlist[]; extern int sys_nerr; # endif -/* Get sys_nerr, sys_errlist on native Windows. */ +/* Get sys_nerr, sys_errlist on native Windows and Cygwin. */ # include # else @@ -467,36 +469,6 @@ strerror_r (int errnum, char *buf, size_t buflen) else ret = strerror_r (errnum, buf, buflen); } -# elif defined __CYGWIN__ - /* Cygwin <= 1.7.7 only provides the glibc interface, is thread-safe, and - always succeeds (although it may truncate). In Cygwin >= 1.7.8, for - valid errnum values, instead of truncating, it leaves the buffer - untouched. */ - { - char stackbuf[256]; - - if (buflen < sizeof (stackbuf)) - { - size_t len; - - stackbuf[0] = '\0'; /* in case strerror_r does nothing */ - strerror_r (errnum, stackbuf, sizeof (stackbuf)); - len = strlen (stackbuf); - if (len < buflen) - { - memcpy (buf, stackbuf, len + 1); - ret = 0; - } - else - ret = ERANGE; - } - else - { - buf[0] = '\0'; /* in case strerror_r does nothing */ - strerror_r (errnum, buf, buflen); - ret = 0; - } - } # else ret = strerror_r (errnum, buf, buflen); # endif @@ -526,12 +498,13 @@ strerror_r (int errnum, char *buf, size_t buflen) /* Try to do what strerror (errnum) does, but without clobbering the buffer used by strerror(). */ -# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) /* NetBSD, HP-UX, native Win32 */ +# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Win32, Cygwin */ /* NetBSD: sys_nerr, sys_errlist are declared through _NETBSD_SOURCE and above. HP-UX: sys_nerr, sys_errlist are declared explicitly above. - native Win32: sys_nerr, sys_errlist are declared in . */ + native Win32: sys_nerr, sys_errlist are declared in . + Cygwin: sys_nerr, sys_errlist are declared in . */ if (errnum >= 0 && errnum < sys_nerr) { # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux) diff --git a/modules/strerror b/modules/strerror index 42a476caca..0e7a2549fe 100644 --- a/modules/strerror +++ b/modules/strerror @@ -15,6 +15,7 @@ strerror_r-posix [test $REPLACE_STRERROR = 1] configure.ac: gl_FUNC_STRERROR +gl_MODULE_INDICATOR([strerror]) gl_STRING_MODULE_INDICATOR([strerror]) Makefile.am: diff --git a/tests/test-perror2.c b/tests/test-perror2.c index fe5e33eba0..d8e0ec5e07 100644 --- a/tests/test-perror2.c +++ b/tests/test-perror2.c @@ -86,6 +86,11 @@ main (void) ASSERT (msg2 == msg4 || STREQ (msg2, str2)); ASSERT (msg3 == msg4 || STREQ (msg3, str3)); ASSERT (STREQ (msg4, str4)); + + free (str1); + free (str2); + free (str3); + free (str4); } /* Test that perror uses the same message as strerror. */ diff --git a/tests/test-strerror_r.c b/tests/test-strerror_r.c index 48287678bd..9c4874fb75 100644 --- a/tests/test-strerror_r.c +++ b/tests/test-strerror_r.c @@ -108,5 +108,53 @@ main (void) ASSERT (errno == 0); } +#if GNULIB_STRERROR + /* Test that strerror_r does not clobber strerror buffer. On some + platforms, this test can only succeed if gnulib also replaces + strerror. */ + { + const char *msg1; + const char *msg2; + const char *msg3; + const char *msg4; + char *str1; + char *str2; + char *str3; + char *str4; + + msg1 = strerror (ENOENT); + ASSERT (msg1); + str1 = strdup (msg1); + ASSERT (str1); + + msg2 = strerror (ERANGE); + ASSERT (msg2); + str2 = strdup (msg2); + ASSERT (str2); + + msg3 = strerror (-4); + ASSERT (msg3); + str3 = strdup (msg3); + ASSERT (str3); + + msg4 = strerror (1729576); + ASSERT (msg4); + str4 = strdup (msg4); + ASSERT (str4); + + strerror_r (EACCES, buf, sizeof buf); + strerror_r (-5, buf, sizeof buf); + ASSERT (msg1 == msg2 || msg1 == msg4 || STREQ (msg1, str1)); + ASSERT (msg2 == msg4 || STREQ (msg2, str2)); + ASSERT (msg3 == msg4 || STREQ (msg3, str3)); + ASSERT (STREQ (msg4, str4)); + + free (str1); + free (str2); + free (str3); + free (str4); + } +#endif + return 0; } -- 2.30.2