segmentation raspberry error dumped c segmentation-fault iso

raspberry - ¿Cuál es la forma estándar más sencilla para producir un Segfault en C?



segmentation fault raspberry pi (8)

En algunas plataformas, un programa C de conformidad estándar puede fallar con una falla de segmentación si solicita demasiados recursos del sistema. Por ejemplo, asignar un objeto grande con malloc puede parecer exitoso, pero más tarde, cuando se acceda al objeto, se bloqueará.

Tenga en cuenta que dicho programa no es estrictamente conforme; los programas que cumplen con esa definición deben permanecer dentro de cada uno de los límites mínimos de implementación.

Un programa C que se ajusta a las normas no puede producir una falla de segmentación porque de lo contrario, las únicas otras formas son a través de un comportamiento no definido.

La señal SIGSEGV se puede generar explícitamente, pero no hay un símbolo SIGSEGV en la biblioteca C estándar.

(En esta respuesta, "conformidad estándar" significa: "Utiliza solo las características descritas en alguna versión del estándar ISO C, evitando el comportamiento no especificado, definido por la implementación o indefinido, pero no necesariamente limitado a los límites mínimos de implementación").

Creo que la pregunta lo dice todo. Un ejemplo que cubra la mayoría de los estándares de C89 a C11 sería útil. Pensé en esto, pero creo que es solo un comportamiento indefinido:

#include <stdio.h> int main( int argc, char* argv[] ) { const char *s = NULL; printf( "%c/n", s[0] ); return 0; }

EDITAR:

Como algunos votos solicitaron aclaración: quería tener un programa con un error de programación habitual (lo más simple que podía pensar era una segfault), que está garantizado (por estándar) para abortar. Esto es un poco diferente a la pregunta mínima segfault, que no se preocupa por este seguro.


La norma solo menciona un comportamiento indefinido. No sabe nada acerca de la segmentación de memoria. También tenga en cuenta que el código que produce el error no cumple con los estándares. Su código no puede invocar un comportamiento indefinido y cumplir con los estándares al mismo tiempo.

No obstante, la forma más corta de producir una falla de segmentación en las arquitecturas que generan tales fallas sería:

int main() { *(int*)0 = 0; }

¿Por qué es seguro producir una segfault? Porque el acceso a la dirección de memoria 0 siempre queda atrapado por el sistema; nunca puede ser un acceso válido (al menos no por el código de espacio de usuario).

Tenga en cuenta, por supuesto, que no todas las arquitecturas funcionan de la misma manera. En algunos de ellos, lo anterior no pudo fallar en absoluto, sino que produjo otros tipos de errores. O la declaración podría estar perfectamente bien, incluso, y la ubicación de la memoria 0 es accesible muy bien. Cuál es una de las razones por las cuales el estándar realmente no define qué sucede.


Si asumimos que no estamos elevando un aumento de llamada de señal, es probable que la falla de segmentación provenga de un comportamiento no definido. El comportamiento indefinido no está definido y un compilador puede negarse a traducir, por lo que no se garantiza que ninguna respuesta con indefinido falle en todas las implementaciones. Además, un programa que invoca un comportamiento indefinido es un programa erróneo.

Pero este es el más corto que puedo obtener esa segfault en mi sistema:

main(){main();}

( -std=c89 -O0 con gcc y -std=c89 -O0 ).

Y, por cierto, ¿este programa realmente invoca bevahior indefinido?


raise() se puede usar para generar un segfault:

raise(SIGSEGV);


Un programa correcto no produce una segfault. Y no puede describir el comportamiento determinista de un programa incorrecto.

Una "falla de segmentación" es una cosa que hace una CPU x86. Lo obtiene al intentar hacer referencia a la memoria de una manera incorrecta. También puede referirse a una situación en la que el acceso a la memoria causa un error de página (es decir, intenta acceder a la memoria que no está cargada en las tablas de páginas) y el sistema operativo decide que no tenía derecho a solicitar esa memoria. Para activar esas condiciones, debe programar directamente para su sistema operativo y su hardware. No es nada que esté especificado por el lenguaje C.


Una falla de segmentación es un comportamiento definido por la implementación . El estándar no define cómo la implementación debe lidiar con el comportamiento indefinido y, de hecho, la implementación podría optimizar el comportamiento indefinido y seguir siendo compatible. Para ser claros, el comportamiento definido en la implementación es el comportamiento que no está especificado por el estándar pero que la implementación debe documentar. El comportamiento indefinido es un código que no es portátil o erróneo y cuyo comportamiento es impredecible y, por lo tanto, no se puede confiar en él.

Si miramos el C99 borrador de la norma §3.4.3 comportamiento indefinido que viene bajo la sección Términos, definiciones y símbolos en el párrafo 1 , dice ( énfasis mío en el futuro ):

comportamiento, al usar una construcción de programa errónea o no portable o datos erróneos, para los cuales esta Norma Internacional no impone requisitos

y en el párrafo 2 dice:

NOTA El comportamiento indefinido varía desde ignorar completamente la situación con resultados impredecibles, hasta comportarse durante la traducción o la ejecución del programa de una manera documentada característica del entorno (con o sin la emisión de un mensaje de diagnóstico), hasta terminar una traducción o ejecución (con el emisión de un mensaje de diagnóstico).

Si, por otro lado, simplemente quiere un método definido en la norma que cause una falla de segmentación en la mayoría de los sistemas tipo Unix , entonces raise(SIGSEGV) debería lograr ese objetivo. Aunque, estrictamente hablando, SIGSEGV se define de la siguiente manera:

SIGSEGV un acceso no válido al almacenamiento

y §7.14 Manejo de señales <signal.h> dice:

Una implementación no necesita generar ninguna de estas señales, excepto como resultado de llamadas explícitas a la función elevar . Las señales y punteros adicionales para las funciones no declarables, con definiciones macro que comienzan, respectivamente, con las letras SIG y una letra mayúscula o con SIG_ y una letra mayúscula, 219) también pueden especificarse mediante la implementación. El conjunto completo de señales, su semántica y su manejo predeterminado está definido por la implementación ; todos los números de señal serán positivos.


La mayoría de las respuestas a esta pregunta se refieren al punto clave, que es: El estándar C no incluye el concepto de falla de segmentación. (Desde C99 incluye el número de señal SIGSEGV , pero no define ninguna circunstancia en la que se entregue esa señal, aparte de raise(SIGSEGV) , que como se discute en otras respuestas no cuenta).

Por lo tanto, no existe un programa "estrictamente conforme" (es decir, un programa que utilice solo construcciones cuyo comportamiento esté completamente definido por el estándar C, solo) que garantice que causa una falla de segmentación.

Las fallas de segmentación están definidas por un estándar diferente, POSIX . Se garantiza que este programa provocará una falla de segmentación, o el "error de bus" funcionalmente equivalente ( SIGBUS ), en cualquier sistema que cumpla totalmente con POSIX.1-2008, incluidas las opciones de protección de memoria y de tiempo real avanzado, siempre que las llamadas a sysconf , posix_memalign y mprotect tienen éxito. Mi lectura de C99 es que este programa tiene un comportamiento definido por la implementación (¡no indefinido!) Considerando solo ese estándar y, por lo tanto, es conforme, pero no estrictamente conforme .

#define _XOPEN_SOURCE 700 #include <sys/mman.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> int main(void) { size_t pagesize = sysconf(_SC_PAGESIZE); if (pagesize == (size_t)-1) { fprintf(stderr, "sysconf: %s/n", strerror(errno)); return 1; } void *page; int err = posix_memalign(&page, pagesize, pagesize); if (err || !page) { fprintf(stderr, "posix_memalign: %s/n", strerror(err)); return 1; } if (mprotect(page, pagesize, PROT_NONE)) { fprintf(stderr, "mprotect: %s/n", strerror(errno)); return 1; } *(long *)page = 0xDEADBEEF; return 0; }


Es difícil definir un método para segmentar la falla de un programa en plataformas no definidas. Una falla de segmentación es un término flexible que no está definido para todas las plataformas (por ejemplo, computadoras pequeñas y sencillas).

Teniendo en cuenta solo los sistemas operativos que admiten procesos , los procesos pueden recibir notificaciones de que se produjo un error de segmentación.

Además, al limitar los sistemas operativos a sistemas operativos ''unix like'', un método confiable para que un proceso reciba una señal SIGSEGV es kill(getpid(),SIGSEGV)

Como es el caso en la mayoría de los problemas de plataforma cruzada, cada plataforma puede (generalmente tiene) una definición diferente de seg-fallamiento.

Pero para ser prácticos, los sistemas operativos Mac, Linux y Win actuales segmentarán en

*(int*)0 = 0;

Además, no es un mal comportamiento causar una segfault. Algunas implementaciones de assert() causan una señal SIGSEGV que puede producir un archivo central. Muy útil cuando necesitas autopsia.

Lo que es peor que causar un segfault es ocultarlo:

try { anyfunc(); } catch (...) { printf("?/n"); }

que oculta el origen de un error y todo lo que tienes que hacer es:

?

.