Resolvendo um Segfault com GDB


Voltar para lista de tutoriais

Segmentation fault (SIGSEGV, falha de segmentação) é um erro muito comum que acontece quando trabalhamos com ponteiros e alocação de memória. Por mais monstruosos que esse problema aparente, podemos analisá-lo e resolve-lo usando o GDB.

Verifique o tutorial de instalação do GDB em Instalação GDB

Nesse tutorial, vamos analisar e consertar um programa que apresenta o erro SIGSEGV.

Programa com SIGSEGV

O seguinte código com erro será usado nesse tutorial.

#include<stdio.h>
#include<stdlib.h>

typedef struct node{
    int valor;
    struct node *prox;
} Node;

void free_lista(Node *no) {
    if (no != NULL) {
        free_lista(no->prox);
        no->prox = NULL;
        free(no);
    }
}

void remover(Node **lista, int valor) {
    Node *tgt, *par;

    tgt = *lista;
    par = NULL;
    while (tgt->valor != valor) {
        par = tgt;
        tgt = tgt->prox;
    }

    tgt->prox = NULL;
    free(tgt);
    tgt = NULL;

    if (par != NULL) {
        par->prox = tgt->prox;
    } else {
        *lista = tgt->prox;
    }

}

void inserir(Node **lista, int valor) {
    Node *new, *tgt;

    new = malloc(sizeof(Node));
    new->valor = valor;
    new->prox = NULL;

    if (*lista == NULL) {
        *lista = new;
    } else {
        tgt = *lista;
        while (tgt->prox != NULL)
            tgt = tgt->prox;

        tgt->prox = new;
    }
}

int main (void) {
 
    Node *lista, *tgt;
    lista = NULL;

    for (int i = 0; i < 10; i++)
        inserir(&lista, i);

    remover(&lista, 5);

    tgt = lista;
    while (tgt != NULL) {
        printf("%d\n", tgt->valor);
        tgt = tgt->prox;
    }

    free_lista(lista);
    lista = NULL;

    return 0;
}

Compile e execute o código.

user@desktop:~$ gcc segf.c -o segf -g
user@desktop:~$ ./segf
Segmentation fault (core dumped)

Como esperado, estamos caindo em um segmentation fault. Execute o programa no gdb.

user@desktop:~$  gdb ./segf
.
.
.
(gdb) run
Starting program: /tmp/segf

Program received signal SIGSEGV, Segmentation fault.
0x000055555555523d in remover (lista=0x7fffffffdd00, valor=5) at segf.c:32
32	        par->prox = tgt->prox;

Ao inspecionar a variável tgt, obtemos que a mesma é um ponteiro para a posição 0x0 (NULL). Logo, o acesso tgt->prox é indevido e resultou no SIGSEGV.

(gdb) p tgt
$1 = (Node *) 0x0

Analisando o código fonte, é possível identificar que o free(tgt) e tgt = NULL são feitos antes da atribuição par->prox = tgt->prox. Assim, para consertar o problema, basta mover as linhas 27, 28 e 29 para depois do bloco do if-else.

Execute o programa e verifique a saída.

user@desktop:~$  ./segf
0
1
2
3
4
6
7
8
9