GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo escribir un controlador de señal para capturar SIGSEGV?

Puede recuperarse de SIGSEGV en Linux. También puede recuperarse de fallas de segmentación en Windows (verá una excepción estructurada en lugar de una señal). Pero el estándar POSIX no garantiza la recuperación, por lo que su código no será portátil.

Echa un vistazo a libsigsegv.


Cuando su controlador de señales regrese (suponiendo que no llame a exit o longjmp o algo que le impida regresar), el código continuará en el punto donde ocurrió la señal, reejecutando la misma instrucción. Dado que en este punto, la protección de la memoria no ha cambiado, simplemente lanzará la señal nuevamente y volverá a su controlador de señal en un ciclo infinito.

Entonces, para que funcione, debe llamar a mprotect en el controlador de señales. Desafortunadamente, como señala Steven Schansker, mprotect no es asíncrono seguro, por lo que no puede llamarlo de manera segura desde el controlador de señal. Entonces, en lo que respecta a POSIX, estás jodido.

Afortunadamente, en la mayoría de las implementaciones (hasta donde yo sé, todas las variantes modernas de UNIX y Linux), mprotect es una llamada al sistema, por lo que es seguro llamar desde un controlador de señal, por lo que puede hacer la mayoría de lo que quiera. El problema es que si desea volver a cambiar las protecciones después de la lectura, tendrá que hacerlo en el programa principal después de la lectura.

Otra posibilidad es hacer algo con el tercer argumento del controlador de señal, que apunta a un sistema operativo y una estructura específica de arco que contiene información sobre dónde se produjo la señal. En Linux, este es un ucontext estructura, que contiene información específica de la máquina sobre la dirección de $PC y otros contenidos de registro donde se produjo la señal. Si modifica esto, cambia el lugar al que regresará el controlador de señal, por lo que puede cambiar el $PC para que esté justo después de la instrucción de falla para que no se vuelva a ejecutar después de que regrese el controlador. Esto es muy complicado de hacer bien (y no es portátil también).

editar

El ucontext la estructura se define en <ucontext.h> . Dentro del ucontext el campo uc_mcontext contiene el contexto de la máquina, y dentro de eso , la matriz gregs contiene el contexto de registro general. Entonces, en su controlador de señal:

ucontext *u = (ucontext *)unused;
unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];

le dará la PC donde ocurrió la excepción. Puede leerlo para averiguar qué instrucción falló y hacer algo diferente.

En lo que respecta a la portabilidad de llamar a mprotect en el controlador de señal, cualquier sistema que siga la especificación SVID o la especificación BSD4 debería ser seguro:permiten llamar a cualquier llamada del sistema (cualquier cosa en la sección 2 del manual) en una señal. controlador.


Has caído en la trampa que todas las personas hacen cuando intentan manejar señales por primera vez. ¿La trampa? Pensar que en realidad puedes hacer cualquier cosa útil con manejadores de señales. Desde un controlador de señal, solo se le permite realizar llamadas de biblioteca asíncronas y seguras para reentrantes.

Consulte este aviso del CERT sobre por qué y una lista de las funciones POSIX que son seguras.

Tenga en cuenta que printf(), que ya está llamando, no está en esa lista.

Tampoco mprotect. No está permitido llamarlo desde un controlador de señales. podría trabajo, pero te prometo que tendrás problemas en el futuro. Tenga mucho cuidado con los controladores de señales, ¡es difícil hacerlo bien!

EDITAR

Como ya estoy siendo un idiota de la portabilidad en este momento, señalaré que tampoco debe escribir en variables compartidas (es decir, globales) sin tomar las precauciones adecuadas.


Linux
  1. Cómo escribir texto en la imagen usando el comando de Linux

  2. ¿Cómo escribir un archivo en otro?

  3. ¿Cómo señalar el final de la entrada Stdin?

  4. ¿Cómo unirse a un hilo que está pendiente de bloquear IO?

  5. ¿Cómo escribo caracteres que no son ASCII usando echo?

Cómo instalar Signal Messenger en Ubuntu 20.04

Cómo escribir un script de Shell en Ubuntu

Cómo instalar la aplicación Signal Messaging en Ubuntu 20.04

Cómo escribir y ejecutar un programa C en Linux

Cómo instalar Signal Messaging en Ubuntu 20.04

¿Cómo escribir en una cámara web virtual en Linux?