Почему я не могу использовать strerror?
Я портирую некоторый код в Windows, и компилятор Microsoft (Visual C++ 8) говорит мне, что strerror()
- Это небезопасно.
отложив фактор раздражения во всех безопасных строковых материалах от Microsoft, я действительно вижу, что некоторые из устаревших функций опасны. Но я не могу понять, что может быть не так с strerror()
. Требуется код (int
), и возвращает соответствующую строку или пустую строку, если код не известен.
где опасность?
есть ли хорошая альтернатива в C?
есть ли хорошая альтернатива в C++?
[edit]
имея некоторые хорошие ответы, и теперь понимая, что некоторые реализации могут быть достаточно сумасшедшими, чтобы на самом деле писать в общий общий буфер - небезопасно для повторения в одном потоке, не говоря уже о потоках! - мой вопрос перестает быть "Почему я не могу его использовать, и каковы альтернативы?"to" есть ли достойные, краткие альтернативы в C и/или C++?"
спасибо заранее
7 ответов
strerror
устарел, потому что он не является потокобезопасным. strerror
работает на внутреннем статическом буфере, который может быть перезаписан другими, параллельными потоками. Вы должны использовать безопасный вариант под названием strerror_s
.
безопасный вариант требует, чтобы размер буфера был передан функции, чтобы проверить, что буфер достаточно большой, прежде чем писать в него, помогая избежать переполнения буфера, которые могут позволить вредоносный код для выполнения.
strerror
сам по себе не является небезопасным. В прежние времена, до того как нанизать нитку, это не было проблемой. С помощью потоков два или более потоков могут вызывать strerror
оставив возвращаемый буфер в неопределенном состоянии. Для однопоточных программ не помешает использовать strerror
если они не играют в какие-то странные игры в libc, такие как общая память для всех приложений в DLL.
для решения этой проблемы есть новый интерфейс с той же функциональностью:
int strerror_r(int errnum, char *buf, size_t buflen);
обратите внимание, что вызывающий предоставляет пространство буфера и размер буфера. Это решает проблему. Даже для однопоточных приложений вы также можете использовать его. Это не больно, и вы могли бы также привыкнуть делать это безопасным способом.
Примечание: вышеупомянутый прототип является спецификацией XSI. Он может варьироваться в зависимости от платформы или с параметрами компилятора или #define
символы. GNU, например, делает эту или их собственную версию доступной в зависимости от #define
имея некоторые хорошие ответы, и теперь понимая, что некоторые реализации могут быть достаточно сумасшедшими, чтобы на самом деле писать в общий общий буфер - небезопасно для повторения в одном потоке, не говоря уже о потоках! - мой вопрос перестает быть "Почему я не могу его использовать, и каковы альтернативы?"to" существуют ли достойные, краткие альтернативы в C и/или c++?"
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] = '';
}
#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] = '';
}
#endif
fprintf(stderr, "%s: %s (debug information: file %s, at line %lu)\n",
usr_msg, sys_msg, filename, line_number);
}
Я написал эту функцию, потому что функции потоков Posix не изменяют errno
, вместо этого они возвращают код ошибки. Таким образом, эта функция в основном такая же, как perror()
, за исключением того, что он позволяет предоставить код ошибки, отличный от errno
, а также отображает некоторую отладочную информацию. Вы можете приспособить его к вашим потребностям.
вы не можете полагаться на строку, возвращаемую strerror()
потому что она может измениться при следующем вызове функции. Ранее возвращенные значения могут устареть. Особенно в многопоточные среды, вы не можете гарантировать, что строка действительна при доступе к ней.
представьте себе, это:
Thread #1:
char * error = strerror(1);
Thread #2
char * error = strerror(2);
printf(error);
в зависимости от реализации strerror()
этот код выводит код ошибки код ошибки 2, не код ошибки 1.
для краткой обертки вы можете использовать STLSoft ' s stlsoft::error_desc
, например:
std::string errstr = stlsoft::error_desc(errno);
глядя на код, кажется, что он реализован с точки зрения strerror()
, что означает, что он будет безопасен для повторного входа в поток (т. е. если используется несколько раз в данном операторе), но он не решает проблему многопоточности.
они, похоже, работают довольно быстрые циклы выпуска для дефектов, поэтому вы можете попробовать запросить мод?
хотя я не знаю причин Microsoft, я отмечаю, что strerror возвращает не-const char *, что означает, что существует риск того, что какой-то веселый шутник позвонил strerror до вас и изменил сообщение.
Я понимаю другой ответ, но я думаю, что показ с кодом более ясен.
проверьте реализацию 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);
когда errnum
не является известной ошибкой, она должна генерировать строку типа "неизвестная ошибка 41". Эта строка не является константой, а генерируется в выделенный буфер. И buf
это golbal var. поэтому его содержимое может измениться при вызове strerror
снова. Вот почему это небезопасно.
на другая рука,strerror_r(int errnum, char *buf, size_t buflen)
, Он генерирует строку ошибки для аргумента buf
. так что глобального ресурса сейчас нет. Вот почему он безопасен для нитей.
ref: https://github.com/liuyang1/glibc/blob/master/string/strerror.c#L23-L26