为什么我不能使用strerror?

时间:2022-01-21 06:54:29

I'm porting some code to Windows, and the Microsoft compiler (Visual C++ 8) is telling me that strerror() is unsafe.

我将一些代码移植到Windows上,而Microsoft编译器(Visual c++ 8)告诉我strerror()是不安全的。

Putting aside the annoyance factor in all the safe string stuff from Microsoft, I can actually see that some of the deprecated functions are dangerous. But I can't understand what could be wrong with strerror(). It takes a code (int), and returns the corresponding string, or the empty string if that code is not known.

撇开微软所有安全字符串的烦恼因素,我可以看到一些不赞成的功能是危险的。但是我不能理解strerror()的错误。它接受一个代码(int),并返回相应的字符串,如果不知道该代码,则返回空字符串。

Where is the danger?

危险在哪里?

Is there a good alternative in C?

在C语言中有没有好的选择?

Is there a good alternative in C++?

在c++中有一个好的选择吗?

[edit]

(编辑)

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++?"

已经有了一些不错的答案,并且现在了解到一些实现可能已经足够疯狂,可以编写到一个共同的共享缓冲区——在一个线程中,不安全的可重入性,不要在线程之间考虑!-我的问题不再是“为什么我不能使用它,还有其他的选择?”“在C和/或c++中有什么像样的、简洁的替代方案吗?”

Thanks in advance

谢谢提前

7 个解决方案

#1


18  

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.

strerror被弃用,因为它不是线程安全的。strerror作用于一个内部的静态缓冲区,它可能被其他并发线程覆盖。您应该使用名为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.

安全的变体要求将缓冲区大小传递给函数,以验证缓冲区在写入它之前是否足够大,从而帮助避免缓冲区溢出,从而允许恶意代码执行。

#2


16  

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.

strerror本身并不是不安全的。在线程化之前的前几天,它根本不是问题。对于线程,两个或多个线程可以调用strerror,使返回的缓冲区处于未定义的状态。对于单线程程序,使用strerror不应该受到伤害,除非它们在libc中玩一些奇怪的游戏,比如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 the XSI spec. 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

注意:上面的原型是XSI规范,每个平台可能会有不同,或者有编译器选项或#define符号。例如,GNU根据一个#define,使它或它们自己的版本可用。

#3


13  

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++?"

已经有了一些不错的答案,并且现在了解到一些实现可能已经足够疯狂,可以编写到一个共同的共享缓冲区——在一个线程中,不安全的可重入性,不要在线程之间考虑!-我的问题不再是“为什么我不能使用它,还有其他的选择?”“在C和/或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:

Posix指定strerror_r(),在Windows上您可以使用strerror_s(),这有点不同,但是有相同的目标。我这样做:

#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.

我写这个函数是因为Posix线程函数不修改errno,而是返回一个错误代码。因此,这个函数基本上与perror()相同,只是它允许您提供除errno之外的错误代码,并显示一些调试信息。你可以适应你的需要。

#4


5  

You can not rely on the string that is returned by strerror() because it may change with the next call to the function. The previously returned values may become obsolete then. Especially in multi-threaded environments, you can not ensure that the string is valid when you access it.

您不能依赖strerror()返回的字符串,因为它可能随着函数的下一个调用而改变。以前返回的值可能会过时。特别是在多线程环境中,您不能确保在访问该字符串时该字符串是有效的。

Imagine this:

想象一下:

Thread #1:
char * error = strerror(1);
                                    Thread #2
                                    char * error = strerror(2);
printf(error);

Depending on the implementation of strerror(), this code prints out the error code for error code 2, not for error code 1.

根据strerror()的实现,此代码打印错误代码2的错误代码,而不是错误代码1。

#5


2  

For a succinct wrapper, you can use STLSoft's stlsoft::error_desc, as in:

对于简洁的包装,您可以使用STLSoft的STLSoft::error_desc,如:

std::string errstr = stlsoft::error_desc(errno);

Looking at the code, it seems that it's implemented in terms of strerror(), which means it'll be safe for reentrancy within a thread (i.e. if used multiple times within a given statement), but it does not address the multithreading problem.

看一下代码,它似乎是用strerror()来实现的,这意味着在一个线程中,它是安全的(如果在一个给定的语句中使用多次),但是它没有解决多线程问题。

They seem to operate pretty rapid release cycles for defects, so you could try requesting a mod?

它们似乎运行了非常快的缺陷释放周期,所以您可以尝试请求一个mod?

#6


1  

Although I do not know Microsoft's reasons, I note that strerror returns a non-const char *, which means that there is a risk that some Merry Prankster has called strerror before you did and modified the message.

虽然我不知道微软的原因,但我注意到strerror返回的是一个非const char *,这意味着在你做和修改消息之前,一些快乐的Prankster已经调用了strerror。

#7


0  

I understand other answer, but I think showing with code is more clear.

我理解其他的答案,但是我认为用代码来展示更清楚。

check glibc's implementation (we should get similar code in MS lib)

检查glibc的实现(在MS lib中我们应该得到类似的代码)

/* Return a string describing the errno code in ERRNUM.
   The storage is good only until the next call to strerror.
   Writing to the storage causes undefined behavior.  */
libc_freeres_ptr (static char *buf);

When the errnum is not kind of known error, it have to generate string like "Unknown error 41". This string is NOT constant, but generate to an allocated buffer. And the buf is golbal var. so its content may change when call strerror again. That's why it's thread-unsafe.

当errnum不是已知的错误时,它必须生成类似“未知错误41”的字符串。这个字符串不是常量,而是生成一个已分配的缓冲区。而buf是golbal var.所以它的内容可能会在再次调用strerror时发生变化。这就是为什么它是线程不安全的。

On the other hand, strerror_r(int errnum, char *buf, size_t buflen), it generate the error string to the argument buf. so there is NO global resource now. That's why it's thread-safe.

另一方面,strerror_r(int errnum, char *buf, size_t buflen),它会生成参数buf的错误字符串。所以现在没有全球资源。这就是为什么它是线程安全的。

ref: https://github.com/liuyang1/glibc/blob/master/string/strerror.c#L23-L26

裁判:https://github.com/liuyang1/glibc/blob/master/string/strerror.c # L23-L26

#1


18  

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.

strerror被弃用,因为它不是线程安全的。strerror作用于一个内部的静态缓冲区,它可能被其他并发线程覆盖。您应该使用名为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.

安全的变体要求将缓冲区大小传递给函数,以验证缓冲区在写入它之前是否足够大,从而帮助避免缓冲区溢出,从而允许恶意代码执行。

#2


16  

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.

strerror本身并不是不安全的。在线程化之前的前几天,它根本不是问题。对于线程,两个或多个线程可以调用strerror,使返回的缓冲区处于未定义的状态。对于单线程程序,使用strerror不应该受到伤害,除非它们在libc中玩一些奇怪的游戏,比如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 the XSI spec. 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

注意:上面的原型是XSI规范,每个平台可能会有不同,或者有编译器选项或#define符号。例如,GNU根据一个#define,使它或它们自己的版本可用。

#3


13  

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++?"

已经有了一些不错的答案,并且现在了解到一些实现可能已经足够疯狂,可以编写到一个共同的共享缓冲区——在一个线程中,不安全的可重入性,不要在线程之间考虑!-我的问题不再是“为什么我不能使用它,还有其他的选择?”“在C和/或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:

Posix指定strerror_r(),在Windows上您可以使用strerror_s(),这有点不同,但是有相同的目标。我这样做:

#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.

我写这个函数是因为Posix线程函数不修改errno,而是返回一个错误代码。因此,这个函数基本上与perror()相同,只是它允许您提供除errno之外的错误代码,并显示一些调试信息。你可以适应你的需要。

#4


5  

You can not rely on the string that is returned by strerror() because it may change with the next call to the function. The previously returned values may become obsolete then. Especially in multi-threaded environments, you can not ensure that the string is valid when you access it.

您不能依赖strerror()返回的字符串,因为它可能随着函数的下一个调用而改变。以前返回的值可能会过时。特别是在多线程环境中,您不能确保在访问该字符串时该字符串是有效的。

Imagine this:

想象一下:

Thread #1:
char * error = strerror(1);
                                    Thread #2
                                    char * error = strerror(2);
printf(error);

Depending on the implementation of strerror(), this code prints out the error code for error code 2, not for error code 1.

根据strerror()的实现,此代码打印错误代码2的错误代码,而不是错误代码1。

#5


2  

For a succinct wrapper, you can use STLSoft's stlsoft::error_desc, as in:

对于简洁的包装,您可以使用STLSoft的STLSoft::error_desc,如:

std::string errstr = stlsoft::error_desc(errno);

Looking at the code, it seems that it's implemented in terms of strerror(), which means it'll be safe for reentrancy within a thread (i.e. if used multiple times within a given statement), but it does not address the multithreading problem.

看一下代码,它似乎是用strerror()来实现的,这意味着在一个线程中,它是安全的(如果在一个给定的语句中使用多次),但是它没有解决多线程问题。

They seem to operate pretty rapid release cycles for defects, so you could try requesting a mod?

它们似乎运行了非常快的缺陷释放周期,所以您可以尝试请求一个mod?

#6


1  

Although I do not know Microsoft's reasons, I note that strerror returns a non-const char *, which means that there is a risk that some Merry Prankster has called strerror before you did and modified the message.

虽然我不知道微软的原因,但我注意到strerror返回的是一个非const char *,这意味着在你做和修改消息之前,一些快乐的Prankster已经调用了strerror。

#7


0  

I understand other answer, but I think showing with code is more clear.

我理解其他的答案,但是我认为用代码来展示更清楚。

check glibc's implementation (we should get similar code in MS lib)

检查glibc的实现(在MS lib中我们应该得到类似的代码)

/* Return a string describing the errno code in ERRNUM.
   The storage is good only until the next call to strerror.
   Writing to the storage causes undefined behavior.  */
libc_freeres_ptr (static char *buf);

When the errnum is not kind of known error, it have to generate string like "Unknown error 41". This string is NOT constant, but generate to an allocated buffer. And the buf is golbal var. so its content may change when call strerror again. That's why it's thread-unsafe.

当errnum不是已知的错误时,它必须生成类似“未知错误41”的字符串。这个字符串不是常量,而是生成一个已分配的缓冲区。而buf是golbal var.所以它的内容可能会在再次调用strerror时发生变化。这就是为什么它是线程不安全的。

On the other hand, strerror_r(int errnum, char *buf, size_t buflen), it generate the error string to the argument buf. so there is NO global resource now. That's why it's thread-safe.

另一方面,strerror_r(int errnum, char *buf, size_t buflen),它会生成参数buf的错误字符串。所以现在没有全球资源。这就是为什么它是线程安全的。

ref: https://github.com/liuyang1/glibc/blob/master/string/strerror.c#L23-L26

裁判:https://github.com/liuyang1/glibc/blob/master/string/strerror.c # L23-L26