Why can't I use strerror?

strerror is deprecated because it's not thread-safe. strerror works on an internal static buffer, which may be overwritten by other, concurrent threads. You should use a secure variant called strerror_s.

The secure variant requires that the buffer size be passed to the function in order to validate that the buffer is large enough before writing to it, helping to avoid buffer overruns that could allow malicious code to execute.


strerror by itself is not unsafe. In the olden days before threading it simply wasn't a problem. With threads, two or more threads could call strerror leaving the returned buffer in an undefined state. For single-threaded programs, it shouldn't hurt to use strerror unless they're playing some weird games in libc, like common memory for all apps in a DLL.

To address this there's a new interface to the same functionality:

int strerror_r(int errnum, char *buf, size_t buflen);

Note that the caller provides the buffer space and the buffer size. This solves the issue. Even for single-threaded applications, you might as well use it. It won't hurt a bit, and you might as well get used to doing it the safer way.

NOTE: the above prototype is from the POSIX spec for strerror_r(). It may vary per platform or with compiler options or #define symbols. GNU, for instance, makes that or their own version available depending on a #define.


Having had some good answers, and now understanding that some implementations may be crazy enough to actually write to a common shared buffer - unsafe to reentrancy within a single-thread, never mind between threads! - my question stops being "Why can't I use it, and what are the alternatives?" to "Are there any decent, succinct alternatives in C and/or C++?"

Posix specifies strerror_r(), and on Windows you can use strerror_s(), which is a bit different but has the same goal. I do this:

#define BAS_PERROR(msg, err_code)\
  bas_perror(msg, err_code, __FILE__, __LINE__)

void bas_perror (const char* msg, int err_code, const char* filename,
                 unsigned long line_number);


void
bas_perror (const char* usr_msg, int err_code, const char* filename,
            unsigned long line_number)
{
  char sys_msg[64];

#ifdef _WIN32
  if ( strerror_s(sys_msg, sizeof sys_msg, err_code) != 0 )
  {
    strncpy(sys_msg, "Unknown error", taille);
    sys_msg[sizeof sys_msg - 1] = '\0';
  }
#else
  if ( strerror_r(err_code, sys_msg, sizeof sys_msg) != 0 )
  {
    strncpy(sys_msg, "Unknown error", sizeof sys_msg);
    sys_msg[sizeof sys_msg - 1] = '\0';
  }
#endif

  fprintf(stderr, "%s: %s (debug information: file %s, at line %lu)\n",
          usr_msg, sys_msg, filename, line_number);
}

I wrote this function because Posix threads functions don't modify errno, they return an error code instead. So this function is basically the same as perror(), except that it allows you to provide an error code other than errno, and also displays some debugging information. You can adapt it to your need.

Tags:

C++

C

Deprecated