Commit c3d1b07c authored by Yang Tse's avatar Yang Tse
Browse files

ensure that errno is not modified inside Curl_strerror()

parent 77bafd82
Loading
Loading
Loading
Loading
+39 −33
Original line number Diff line number Diff line
@@ -24,10 +24,11 @@
#include "setup.h"

#ifdef HAVE_STRERROR_R
#if !defined(HAVE_POSIX_STRERROR_R) && !defined(HAVE_GLIBC_STRERROR_R)
#error "you MUST have either POSIX or glibc strerror_r if strerror_r is found"
#endif /* !POSIX && !glibc */
#endif /* HAVE_STRERROR_R */
#  if (!defined(HAVE_POSIX_STRERROR_R) && !defined(HAVE_GLIBC_STRERROR_R)) || \
       (defined(HAVE_POSIX_STRERROR_R) &&  defined(HAVE_GLIBC_STRERROR_R))
#    error "strerror_r MUST be either POSIX-style or glibc-style"
#  endif
#endif

#include <curl/curl.h>
#include <stdlib.h>
@@ -43,14 +44,6 @@
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>

#if defined(HAVE_STRERROR_R) && defined(HAVE_NO_STRERROR_R_DECL)
#ifdef HAVE_POSIX_STRERROR_R
/* seen on AIX 5100-02 gcc 2.9 */
extern int strerror_r(int errnum, char *strerrbuf, size_t buflen);
#else
extern char *strerror_r(int errnum, char *buf, size_t buflen);
#endif
#endif

const char *
curl_easy_strerror(CURLcode error)
@@ -590,6 +583,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
{
  char *buf, *p;
  size_t max;
  int old_errno = ERRNO;

  DEBUGASSERT(conn);
  DEBUGASSERT(err >= 0);
@@ -601,17 +595,15 @@ const char *Curl_strerror(struct connectdata *conn, int err)
#ifdef USE_WINSOCK

#ifdef _WIN32_WCE
  buf[0]=0;
  {
    wchar_t wbuf[256];
    wbuf[0] = L'\0';

    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
                  LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL);
    wcstombs(buf,wbuf,max);
  }

#else

  /* 'sys_nerr' is the maximum errno number, it is not widely portable */
  if(err >= 0 && err < sys_nerr)
    strncpy(buf, strerror(err), max);
@@ -622,34 +614,43 @@ const char *Curl_strerror(struct connectdata *conn, int err)
      snprintf(buf, max, "Unknown error %d (%#x)", err, err);
  }
#endif

#else /* not USE_WINSOCK coming up */

  /* These should be atomic and hopefully thread-safe */
#ifdef HAVE_STRERROR_R
  /* There are two different APIs for strerror_r(). The POSIX and the GLIBC
     versions. */
#ifdef HAVE_POSIX_STRERROR_R
  strerror_r(err, buf, max);
  /* this may set errno to ERANGE if insufficient storage was supplied via
     'strerrbuf' and 'buflen' to contain the generated message string, or
     EINVAL if the value of 'errnum' is not a valid error number.*/
#else
#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R)
 /*
  * The POSIX-style strerror_r() may set errno to ERANGE if insufficient
  * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
  * message string, or EINVAL if 'errnum' is not a valid error number.
  */
  if(0 != strerror_r(err, buf, max)) {
    if('\0' == buf[0])
      snprintf(buf, max, "Unknown error %d", err);
  }
#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
 /*
  * The glibc-style strerror_r() only *might* use the buffer we pass to
  * the function, but it always returns the error message as a pointer,
  * so we must copy that string unconditionally (if non-NULL).
  */
  {
    /* HAVE_GLIBC_STRERROR_R */
    char buffer[256];
    char *msg = strerror_r(err, buffer, sizeof(buffer));
    /* this version of strerror_r() only *might* use the buffer we pass to
       the function, but it always returns the error message as a pointer,
       so we must copy that string unconditionally (if non-NULL) */
    if(msg)
      strncpy(buf, msg, max);
    else
      snprintf(buf, max, "Unknown error %d", err);
  }
#endif /* end of HAVE_GLIBC_STRERROR_R */
#else /* HAVE_STRERROR_R */
  strncpy(buf, strerror(err), max);
#endif /* end of HAVE_STRERROR_R */
#else
  {
    char *msg = strerror(err);
    if(msg)
      strncpy(buf, msg, max);
    else
      snprintf(buf, max, "Unknown error %d", err);
  }
#endif

#endif /* end of ! USE_WINSOCK */

  buf[max] = '\0'; /* make sure the string is zero terminated */
@@ -659,6 +660,10 @@ const char *Curl_strerror(struct connectdata *conn, int err)
     *p = '\0';
  if((p = strrchr(buf,'\r')) != NULL && (p - buf) >= 1)
     *p = '\0';

  if(old_errno != ERRNO)
    SET_ERRNO(old_errno);

  return buf;
}

@@ -680,6 +685,7 @@ const char *Curl_idn_strerror (struct connectdata *conn, int err)

  buf = conn->syserr_buf;
  max = sizeof(conn->syserr_buf)-1;
  *buf = '\0';

#ifndef CURL_DISABLE_VERBOSE_STRINGS
  switch ((Idna_rc)err) {