segmentation c macos error-handling segmentation-fault

segmentation fault c++>



Capturando segfaults en C (8)

Tengo un programa que segfaults de la aritmética del indicador a veces. Sé que esto sucede, pero no puedo verificarlo con anticipación para ver si se segmenta o no, o puedo "preescanear" datos de entrada para ver si causará una segfault (que puede ser imposible de determinar), o puedo reajustarlo para que no use la aritmética del puntero, lo que requeriría una cantidad de trabajo significativamente mayor, o puedo intentar atrapar una segfault. Entonces mi pregunta:

1) ¿Cómo, en C, puedo atrapar un segfault? Sé que algo en el sistema operativo provoca una falla de segmentación, pero ¿qué puede hacer un programa de C en caso de segfeults para morir un poco más elegantemente que solo la Segmentation fault ?

2) ¿Qué tan portátil es esto?

Me imagino que este es un comportamiento muy poco práctico, así que si publicas cualquier código para atrapar un segfault, por favor dime en qué funciona. Estoy en Mac OS X pero me gustaría que mi programa funcione en tantas plataformas como pueda y quiero ver cuáles son mis opciones.

Y no se preocupe: básicamente, todo lo que quiero hacer es imprimir un mensaje de error más fácil de usar y liberar algo de memoria malloc() , y luego morir. No estoy planeando ignorar todos los segfaults que obtengo y avanzo.


Bueno, SIGSEGV es atrapable, y esto es POSIX, por lo que es portátil en ese sentido.

Bu. Me preocupa que parezca querer manejar el segfault en lugar de solucionar el problema que causa el segfault. Si tuviera que elegir si era el sistema operativo defectuoso o mi propio código, sé cuál elegiría. Sugiero que busques ese error, lo arregles y luego escribas un caso de prueba para asegurarte de que nunca más te muerda.


Creo que estás tratando de resolver un problema que no existe. Al menos estás trabajando en el lado equivocado. No podrá detectar un error de segmentación, ya que este error / excepción es provocado por el sistema operativo (es causado por su programa, el sistema operativo simplemente lo atrapa ).

Te aconsejo que reconsideres tu estrategia con respecto a la entrada: ¿por qué es imposible desinfectarla? Lo más importante es verificar el tamaño, para esto el C stdlib tiene las funciones apropiadas. Entonces, por supuesto, debe verificar las entradas válidas con respecto al contenido. Sí, esto probablemente resultará en mucho trabajo, pero es la única manera de escribir un programa sólido.

EDITAR: No soy un experto en C, no sabía que incluso un error de segmentación podría ser manejado por un manejador de señal. Aún así, creo que no es la forma correcta de hacerlo por las razones mencionadas anteriormente.


El manejo de señales es (relativamente) portátil en máquinas Unix (esto incluye Mac y Linux). Las grandes diferencias están en el detalle de excepción, que se pasa como argumento a la rutina de manejo de señal. Sorrty, pero es probable que necesite un montón de #ifdefs para eso, si desea imprimir mensajes de error más razonables (como dónde y por qué dirección ocurrió el error) ...

Ok, aquí hay un fragmento de código para que comiences:

#include <signal.h> /* reached when a segv occurrs */ void SEGVFunction( SIGARGS ) { ... } ... main(...) { signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */ ... ... do your work ... }

Tu tarea es:

  • compruebe qué es SIGARGS (depende del sistema operativo, entonces use un ifdef)
  • vea cómo extraer fault-address y pc de la información de excepción en sigArgs
  • imprimir un mensaje razonable
  • salida

en teoría, incluso podría parchear la PC en el manejador de señal (para después de la instrucción de fallas), y continuar. Sin embargo, los manejadores de señal típicos salen de (a) o a un longjmp () de regreso a un lugar de guardado en el principal.

Saludos


Las acciones seguras en un manejador de señal son muy limitadas. No es seguro llamar a cualquier función de la biblioteca que no se sabe que es reentrante, lo que excluirá, por ejemplo, free() y printf() . La mejor práctica es establecer una variable y regresar, pero esto no te ayuda mucho. También es seguro usar llamadas al sistema como write() .

Tenga en cuenta que en los dos ejemplos de backtrace dados aquí, la función backtrace_symbols_fd() será segura porque usa el fd crudo directamente, pero la llamada a fprintf() es incorrecta, y debe ser reemplazada por el uso de write() .


Puede usar la señal de función para instalar un nuevo manejador de señal para la señal:

#include <signal.h> void (*signal(int signum, void (*sighandler)(int)))(int);

Algo como el siguiente código:

signal(SIGINT , clean_exit_on_sig); signal(SIGABRT , clean_exit_on_sig); signal(SIGILL , clean_exit_on_sig); signal(SIGFPE , clean_exit_on_sig); signal(SIGSEGV, clean_exit_on_sig); // <-- this one is for segmentation fault signal(SIGTERM , clean_exit_on_sig); void clean_exit_on_sig(int sig_num) { printf ("/n Signal %d received",sig_num); }



Tienes que definir un manejador de señal. Esto se hace en sistemas Unix usando la función sigaction . He hecho esto con el mismo código en Fedora de 64 y 32 bits y en Sun Solaris.


Hay un ejemplo de cómo atrapar SIGSEGV e imprimir una traza de pila usando la traza inversa de glibc () aquí:

cómo generar una stacktrace cuando mi aplicación C ++ falla

Puedes usar esto para capturar tu segfault y limpiar, pero ten cuidado: no deberías estar haciendo demasiadas cosas en un manejador de señal, especialmente cosas que implican hacer llamadas como malloc (). Hay muchas llamadas que no son seguras para las señales y puede terminar disparándose en el pie si hace, por ejemplo, una llamada a malloc desde dentro de malloc.