From patchwork Thu Jan 20 18:11:21 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [libfortran] PR46267 strerror() might not be thread-safe Date: Thu, 20 Jan 2011 08:11:21 -0000 From: Janne Blomqvist X-Patchwork-Id: 79746 Message-Id: To: Fortran List , GCC Patches Hello, POSIX does not require strerror() to be thread-safe (although some implementations are), using it is thus a potential error in libgfortran. The attached patch fixes this in the case that strerror_r() is available; thanks to Jakub for the hint about __builtin_classify_type. Regtested on x86_64-unknown-linux-gnu, Ok for trunk? diff --git a/libgfortran/configure.ac b/libgfortran/configure.ac index 7b28f12..4f137e4 100644 --- a/libgfortran/configure.ac +++ b/libgfortran/configure.ac @@ -249,7 +249,7 @@ AC_CHECK_FUNCS(chdir strerror getlogin gethostname kill link symlink perror) AC_CHECK_FUNCS(sleep time ttyname signal alarm ctime clock access fork execl) AC_CHECK_FUNCS(wait setmode execvp pipe dup2 close fdopen strcasestr getrlimit) AC_CHECK_FUNCS(gettimeofday stat fstat lstat getpwuid vsnprintf dup getcwd) -AC_CHECK_FUNCS(localtime_r gmtime_r) +AC_CHECK_FUNCS(localtime_r gmtime_r strerror_r) # Check for glibc backtrace functions AC_CHECK_FUNCS(backtrace backtrace_symbols) diff --git a/libgfortran/intrinsics/gerror.c b/libgfortran/intrinsics/gerror.c index ccb5c3e..44873f9 100644 --- a/libgfortran/intrinsics/gerror.c +++ b/libgfortran/intrinsics/gerror.c @@ -45,14 +45,13 @@ PREFIX(gerror) (char * msg, gfc_charlen_type msg_len) memset (msg, ' ', msg_len); /* Blank the string. */ - p = strerror (errno); + p = gf_strerror (errno); if (p == NULL) return; p_len = strlen (p); if (msg_len < p_len) - memcpy (msg, p, msg_len); - else - memcpy (msg, p, p_len); + p_len = msg_len; + memcpy (msg, p, p_len); } #endif diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c index fa64e20..b7eba0b 100644 --- a/libgfortran/io/unix.c +++ b/libgfortran/io/unix.c @@ -262,7 +262,7 @@ flush_if_preconnected (stream * s) const char * get_oserror (void) { - return strerror (errno); + return gf_strerror (errno); } diff --git a/libgfortran/libgfortran.h b/libgfortran/libgfortran.h index ac86492..d092a54 100644 --- a/libgfortran/libgfortran.h +++ b/libgfortran/libgfortran.h @@ -1,5 +1,6 @@ /* Common declarations for all of libgfortran. - Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011 Free Software Foundation, Inc. Contributed by Paul Brook , and Andy Vaught @@ -756,6 +757,9 @@ internal_proto(notify_std); extern notification notification_std(int); internal_proto(notification_std); +extern char *gf_strerror (int); +internal_proto(gf_strerror); + /* fpu.c */ extern void set_fpu (void); diff --git a/libgfortran/runtime/error.c b/libgfortran/runtime/error.c index 1baf9d3..a04f2c5 100644 --- a/libgfortran/runtime/error.c +++ b/libgfortran/runtime/error.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002, 2003, 2005, 2006, 2007, 2009, 2010 +/* Copyright (C) 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc. Contributed by Andy Vaught @@ -141,6 +141,36 @@ gfc_xtoa (GFC_UINTEGER_LARGEST n, char *buffer, size_t len) return p; } + +/* Hopefully thread-safe wrapper for a strerror() style function. */ + +char * +gf_strerror (int errnum) +{ +#ifdef HAVE_STRERROR_R +#define GF_STRERROR_R_MAXSZ 256 + static __thread char msg[GF_STRERROR_R_MAXSZ]; + /* TODO: How to prevent the compiler warning due to strerror_r of + the untaken branch having the wrong return type? */ + if (__builtin_classify_type (strerror_r (0, msg, 0)) == 5) + { + /* GNU strerror_r() */ + return strerror_r (errnum, msg, GF_STRERROR_R_MAXSZ); + } + else + { + /* POSIX strerror_r () */ + strerror_r (errnum, msg, GF_STRERROR_R_MAXSZ); + return msg; + } +#else + /* strerror () is not necessarily thread-safe, but should at least + be available everywhere. */ + return strerror (errnum); +#endif +} + + /* show_locus()-- Print a line number and filename describing where * something went wrong */