Question

J'ai un bogue étrange dans mon programme, il me semble que malloc () est à l'origine d'un SIGSEGV, ce qui, à ma connaissance, n'a aucun sens. J'utilise une bibliothèque appelée simclist pour les listes dynamiques.

Voici une structure qui est référencée plus tard:

typedef struct {
    int msgid;
    int status;
    void* udata;
    list_t queue;
} msg_t;

Et voici le code:

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 est l'endroit où le programme échoue, voici le code pour 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 ligne l- > spareels = (struct list_entry_s **) malloc (SIMCLIST_MAX_SPARE_ELEMS * ) est l'endroit où le SIGSEGV est généré en fonction de la trace de pile. J'utilise gdb / nemiver pour le débogage mais je le suis La première fois que cette fonction est appelée, elle fonctionne bien, mais la deuxième fois, elle échoue toujours. Comment malloc () peut-il provoquer un SIGSEGV?

Ceci est la trace de la pile:

#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

Toute aide ou idée est très appréciée!

Était-ce utile?

La solution

malloc peut commettre une erreur de segmentation, par exemple, lorsque le segment de mémoire est corrompu. Vérifiez que vous n'écrivez rien au-delà des limites d'une allocation précédente.

Autres conseils

Une violation de mémoire se produit probablement dans une autre partie de votre code. Si vous êtes sur Linux, vous devez absolument essayer valgrind. Je ne ferais jamais confiance à mes propres programmes C à moins que celui-ci réussisse Valgrind.

EDIT: un autre outil utile est la clôture électrique . Glibc fournit également la variable d’environnement MALLOC_CHECK_ pour résoudre les problèmes de mémoire. Ces deux méthodes n'affectent pas la vitesse d'exécution autant que valgrind.

Vous avez probablement corrompu votre tas quelque part avant cet appel par un dépassement de tampon ou en appelant free avec un pointeur qui n'a pas été alloué par malloc (ou qui était déjà libéré).

Si les structures de données internes utilisées par malloc sont corrompues de cette façon, malloc utilise des données non valides et risque de planter.

Il existe une multitude de façons de déclencher un vidage de la base de malloc () (et realloc () et calloc () ). Ceux-ci incluent:

  • Buffer overflow: écriture au-delà de la fin de l'espace alloué (informations de contrôle piétinées que malloc () y conservait).
  • Débordement de mémoire tampon: écriture avant le début de l'espace alloué (informations de contrôle piétinées que malloc () y conservait).
  • Libération de la mémoire non allouée par malloc () . Dans un programme mixte C et C ++, cela inclurait la libération de la mémoire allouée en C ++ par new .
  • Libération d'un pointeur pointant à travers un bloc de mémoire alloué par malloc () - qui est un cas particulier du cas précédent.
  • Libérer un pointeur déjà libéré - le fameux «double libre».

L'utilisation d'une version de diagnostic de malloc () ou l'activation des diagnostics dans la version standard de votre système peut aider à identifier certains de ces problèmes. Par exemple, il peut détecter les petits sous-débits et les débordements (car il alloue de l'espace supplémentaire pour fournir une zone tampon autour de l'espace que vous avez demandé) et peut probablement détecter les tentatives de libération de mémoire non allouée ou déjà libérée. ou des pointeurs à mi-chemin dans l'espace alloué - car il stockera les informations séparément de l'espace alloué. Le coût est que la version de débogage prend plus d'espace. Un très bon allocateur pourra enregistrer le numéro de trace de la pile et les numéros de ligne pour vous indiquer le lieu où la répartition a eu lieu dans votre code ou le premier libre.

Vous devriez essayer de déboguer ce code isolément pour voir si le problème est réellement localisé à l'endroit où le segfault est généré. (Je soupçonne que ce n'est pas le cas).

Cela signifie:

# 1: compilez le code avec -O0 pour vous assurer que gdb obtient les informations de numérotation des lignes correctes.

# 2: écrivez un test unitaire appelant cette partie du code.

Je pense que le code fonctionnera correctement s'il est utilisé séparément. Vous pouvez ensuite tester vos autres modules de la même manière, jusqu'à ce que vous découvriez la cause du bogue.

Utiliser Valgrind, comme d'autres l'ont suggéré, est également une très bonne idée.

Le code est problématique. Si malloc renvoie NULL, ce cas n'est pas traité correctement dans votre code. Vous supposez simplement que la mémoire a été allouée pour vous alors qu'elle ne l'a pas été. Cela peut provoquer une corruption de la mémoire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top