c++ c deprecated

c++ - ¿Por qué no puedo usar strerror?



deprecated (7)

Estoy transfiriendo algo de código a Windows y el compilador de Microsoft (Visual C ++ 8) me dice que strerror() no es seguro.

Dejando de lado el factor de molestia en todas las cosas de cadena segura de Microsoft, puedo ver que algunas de las funciones en desuso son peligrosas. Pero no puedo entender lo que podría estar mal con strerror() . Toma un código ( int ), y devuelve la cadena correspondiente, o la cadena vacía si ese código no se conoce.

¿Dónde está el peligro?

¿Hay una buena alternativa en C?

¿Hay una buena alternativa en C ++?

[editar]

Habiendo tenido algunas respuestas correctas, y ahora entendiendo que algunas implementaciones pueden ser lo suficientemente locas como para escribir en un búfer compartido común, no es seguro reingresar en un solo hilo, ¡no importa entre los hilos! - mi pregunta deja de ser "¿Por qué no puedo usarla y cuáles son las alternativas?" a "¿Hay alternativas decentes y sucintas en C y / o C ++?"

Gracias por adelantado


Habiendo tenido algunas respuestas correctas, y ahora entendiendo que algunas implementaciones pueden ser lo suficientemente locas como para escribir en un búfer compartido común, no es seguro reingresar en un solo hilo, ¡no importa entre los hilos! - mi pregunta deja de ser "¿Por qué no puedo usarla y cuáles son las alternativas?" a "¿Hay alternativas decentes y sucintas en C y / o C ++?"

Posix especifica strerror_r() , y en Windows puede usar strerror_s() , que es un poco diferente pero tiene el mismo objetivo. Hago esto:

#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); }

Escribí esta función porque las funciones de los hilos de Posix no modifican el error, sino que devuelven un código de error. Entonces, esta función es básicamente la misma que perror() , excepto que le permite proporcionar un código de error que no sea errno , y también muestra cierta información de depuración. Puedes adaptarlo a tu necesidad.


Aunque no conozco los motivos de Microsoft, observo que strerror devuelve un carácter no const *, lo que significa que existe el riesgo de que algún Merry Prankster haya llamado a strerror antes de que lo hiciera y modificara el mensaje.


Entiendo otra respuesta, pero creo que mostrar con código es más claro.

verifique la implementación de glibc (deberíamos obtener un código similar en 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);

Cuando el errnum no es un error conocido, tiene que generar una cadena como "Error desconocido 41". Esta cadena NO es constante, sino que se genera en un búfer asignado. Y la buf es golbal var. por lo que su contenido puede cambiar al llamar de nuevo a strerror . Es por eso que es hilo-inseguro.

Por otro lado, strerror_r(int errnum, char *buf, size_t buflen) , genera la cadena de error al argumento buf . entonces no hay un recurso global ahora. Por eso es seguro para hilos.

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


No puede confiar en la cadena que devuelve strerror() porque puede cambiar con la siguiente llamada a la función. Los valores devueltos previamente pueden quedar obsoletos entonces. Especialmente en entornos de subprocesos múltiples , no puede asegurarse de que la cadena sea válida cuando acceda a ella.

Imagina esto:

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

Dependiendo de la implementación de strerror() , este código imprime el código de error para el código de error 2, no para el código de error 1.


Para un envoltorio sucinto, puede usar stlsoft::error_desc , como en:

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

Mirando el código, parece que está implementado en términos de strerror() , lo que significa que será seguro para la reentrada dentro de un hilo (es decir, si se usa varias veces dentro de una declaración dada), pero no aborda el problema de subprocesos múltiples.

Parece que operan ciclos de liberación bastante rápidos para los defectos, por lo que podría intentar solicitar un mod.


strerror está en desuso porque no es seguro para subprocesos. strerror funciona en un búfer estático interno, que puede ser sobrescrito por otros subprocesos concurrentes. Debe utilizar una variante segura llamada strerror_s .

La variante segura requiere que el tamaño del búfer se pase a la función para validar que el búfer sea lo suficientemente grande antes de escribir en él, lo que ayuda a evitar el exceso de búfer que podría permitir la ejecución de código malicioso.


strerror por sí mismo no es inseguro. En los viejos tiempos antes de roscar simplemente no era un problema. Con subprocesos, dos o más subprocesos podrían llamar a strerror dejando el búfer devuelto en un estado indefinido. Para los programas de un solo subproceso, no debería hacer daño usar strerror menos que estén jugando algunos juegos extraños en libc, como la memoria común para todas las aplicaciones en una DLL.

Para abordar esto hay una nueva interfaz para la misma funcionalidad:

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

Tenga en cuenta que la persona que llama proporciona el espacio del búfer y el tamaño del búfer. Esto resuelve el problema. Incluso para aplicaciones de un solo subproceso, también podría usarlo. No dolerá un poco, y es mejor que te acostumbres a hacerlo de una manera más segura.

NOTA: el prototipo anterior es la especificación XSI. Puede variar según la plataforma o con las opciones del compilador o los símbolos #define . GNU, por ejemplo, hace que esa o su propia versión esté disponible dependiendo de #define