¿Cómo puede Malloc() causar un SIGSEGV?
gdb (6)
Tengo un error extraño en mi programa, me parece que malloc () está causando un SIGSEGV, que en mi opinión no tiene ningún sentido. Estoy usando una biblioteca llamada simclist para listas dinámicas.
Aquí hay una estructura a la que se hace referencia más adelante:
typedef struct {
int msgid;
int status;
void* udata;
list_t queue;
} msg_t;
Y aquí está el código:
msg_t* msg = (msg_t*) malloc( sizeof( msg_t ) );
msg->msgid = msgid;
msg->status = MSG_STAT_NEW;
msg->udata = udata;
list_init( &msg->queue );
list_init
es donde falla el programa, aquí está el código para list_init:
/* list initialization */
int list_init(list_t *restrict l) {
if (l == NULL) return -1;
srandom((unsigned long)time(NULL));
l->numels = 0;
/* head/tail sentinels and mid pointer */
l->head_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
l->tail_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
l->head_sentinel->next = l->tail_sentinel;
l->tail_sentinel->prev = l->head_sentinel;
l->head_sentinel->prev = l->tail_sentinel->next = l->mid = NULL;
l->head_sentinel->data = l->tail_sentinel->data = NULL;
/* iteration attributes */
l->iter_active = 0;
l->iter_pos = 0;
l->iter_curentry = NULL;
/* free-list attributes */
l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * sizeof(struct list_entry_s *));
l->spareelsnum = 0;
#ifdef SIMCLIST_WITH_THREADS
l->threadcount = 0;
#endif
list_attributes_setdefaults(l);
assert(list_repOk(l));
assert(list_attrOk(l));
return 0;
}
la línea l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS *
es donde se l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS *
el SIGSEGV según el seguimiento de la pila. Estoy usando gdb / nemiver para la depuración, pero estoy perdido. La primera vez que se llama a esta función funciona bien pero siempre falla la segunda vez. ¿Cómo puede Malloc () causar un SIGSEGV?
Este es el rastro de la pila:
#0 ?? () at :0
#1 malloc () at :0
#2 list_init (l=0x104f290) at src/simclist.c:205
#3 msg_new (msg_switch=0x1050dc0, msgid=8, udata=0x0) at src/msg_switch.c:218
#4 exread (sockfd=8, conn_info=0x104e0e0) at src/zimr-proxy/main.c:504
#5 zfd_select (tv_sec=0) at src/zfildes.c:124
#6 main (argc=3, argv=0x7fffcabe44f8) at src/zimr-proxy/main.c:210
¡Cualquier ayuda o idea es muy apreciada!
Debería intentar depurar este código de forma aislada, para ver si el problema se encuentra realmente donde se genera segfault. (Sospecho que no es así).
Esto significa:
# 1: compila el código con -O0, para asegurarte de que gdb obtenga la información correcta de numeración de línea.
# 2: Escribe una prueba unitaria que llame a esta parte del código.
Supongo que el código funcionará correctamente cuando se use por separado. Luego puede probar sus otros módulos de la misma manera, hasta que descubra qué causa el error.
Usar Valgrind, como otros han sugerido, también es una muy buena idea.
Hay una miríada de formas de desencadenar un volcado del núcleo de malloc()
(y realloc()
y calloc()
). Éstas incluyen:
- Desbordamiento de búfer: escritura más allá del final del espacio asignado (información de control de pisoteo que
malloc()
mantenía allí). - Desbordamiento del búfer: escritura antes del inicio del espacio asignado (información de control del pisoteo que
malloc()
mantenía allí). - Liberación de memoria que no fue asignada por
malloc()
. En un programa mixto de C y C ++, eso incluiría liberar memoria asignada en C ++ pornew
. - Liberando un puntero que apunta en parte a través de un bloque de memoria asignado por
malloc()
- que es un caso especial del caso anterior. - Liberando un puntero que ya fue liberado - el notorio ''doble gratis''.
Usar una versión de diagnóstico de malloc()
o habilitar diagnósticos en la versión estándar de su sistema puede ayudar a identificar algunos de estos problemas. Por ejemplo, puede detectar subdesbordamientos y desbordamientos pequeños (porque asigna espacio adicional para proporcionar una zona de amortiguación alrededor del espacio que usted solicitó), y probablemente pueda detectar intentos de liberar memoria que no fue asignada o que ya fue liberada o punteros en parte del espacio asignado, porque almacenará la información por separado del espacio asignado. El costo es que la versión de depuración requiere más espacio. Un asignador realmente bueno podrá registrar la traza de la pila y los números de línea para indicarle dónde se produjo la asignación en su código, o dónde ocurrió la primera libre.
Probablemente haya corrompido su pila en algún lugar antes de esta llamada mediante un desbordamiento de búfer o llamando free
con un puntero que no fue asignado por malloc
(o que ya fue liberado).
Si las estructuras de datos internas utilizadas por malloc se corrompen de esta manera, malloc está utilizando datos no válidos y podría bloquearse.
malloc
puede segfault por ejemplo cuando el montón está dañado. Verifique que no está escribiendo nada más allá de los límites de cualquier asignación anterior.
El código es problemático. Si malloc devuelve NULL, este caso no se maneja correctamente en su código. Simplemente asume que la memoria ha sido asignada para usted cuando en realidad no ha sido. Esto puede causar daños en la memoria.
Probablemente la violación de memoria ocurra en otra parte de su código. Si estás en Linux, definitivamente deberías probar valgrind. Nunca confiaría en mis propios programas C a menos que pase valgrind.
EDITAR: otra herramienta útil es Cerca eléctrica . Glibc también proporciona la variable de entorno MALLOC_CHECK_ para ayudar a depurar problemas de memoria. Estos dos métodos no afectan tanto la velocidad de carrera como valgrind.