web-dev-qa-db-fra.com

Pourquoi ai-je un échec d'assertion C malloc?

J'implémente un algorithme polynomial divide and conquer afin de pouvoir le comparer à une implémentation OpenCL, mais je ne parviens pas à faire fonctionner malloc. Lorsque je lance le programme, il alloue un tas de choses, vérifie certaines choses, puis envoie le size/2 à l'algorithme. Puis, lorsque je clique à nouveau sur la ligne malloc, le message suivant est affiché:

malloc.c: 3096: sYSMALLOc: Assertion `(old_top == ((mbinptr) ((char *) & ((av)) -> bacs [((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size)> = (unsigned long) (((__ builtin_offsetof (struct malloc_chunk, fd_nextsize))) + ((2 * (sizeof (size_t)) - 1)) & ~ ((2 * (sizeof (size_t))) - 1))) && ((old_top) -> taille & 0x1) && ((unsigned long) old_end & pagemask) == 0) 'a échoué. Avorté

La ligne en question est:

int *mult(int size, int *a, int *b) {
    int *out,i, j, *tmp1, *tmp2, *tmp3, *tmpa1, *tmpa2, *tmpb1, *tmpb2,d, *res1, *res2;
    fprintf(stdout, "size: %d\n", size);

    out = (int *)malloc(sizeof(int) * size * 2);
}

J'ai vérifié la taille avec un fprintf, et c'est un entier positif (généralement 50 à ce moment-là). J'ai essayé d'appeler malloc avec un numéro ordinaire également et j'obtiens toujours l'erreur. Je ne comprends pas ce qui se passe et rien de ce que j'ai trouvé de Google jusqu'à présent ne m'aide.

Avez-vous une idée de ce qui passe? J'essaie de comprendre comment compiler un nouveau GCC au cas où il s'agirait d'une erreur du compilateur, mais j'en doute vraiment.

69
Chris

99,9% de probabilités que votre mémoire soit corrompue (mémoire tampon saturée ou dépassée, écriture dans un pointeur après sa libération, appelée free deux fois sur le même pointeur, etc.)

Exécutez votre code sous Valgrind pour voir où votre programme a effectué une erreur.

83

Pour vous aider à mieux comprendre pourquoi cela se produit, j'aimerais développer un peu la réponse de @ r-samuel-klatchko.

Lorsque vous appelez malloc, ce qui se passe réellement est un peu plus compliqué que de vous donner un morceau de mémoire avec lequel vous pourrez jouer. Sous le capot, malloc conserve également des informations de maintenance concernant la mémoire qu'il vous a donnée (sa taille surtout), de sorte que, lorsque vous appelez free, il sache, par exemple, combien de mémoire libre. Ces informations sont généralement conservées juste avant que l'emplacement mémoire vous soit renvoyé par malloc. Des informations plus exhaustives peuvent être trouvées sur Internet ™ , mais l’idée (très) fondamentale est quelque chose comme ceci:

+------+-------------------------------------------------+
+ size |                  malloc'd memory                +
+------+-------------------------------------------------+
       ^-- location in pointer returned by malloc

En partant de cela (et en simplifiant grandement les choses), lorsque vous appelez malloc, il doit renvoyer un pointeur sur la prochaine partie de la mémoire disponible. Un moyen très simple de procéder consiste à examiner le peu de mémoire précédent cédé, et à déplacer size octets plus bas (ou plus haut) dans la mémoire. Avec cette implémentation, vous vous retrouvez avec une mémoire qui ressemble à ceci après avoir alloué p1, p2 Et p3:

+------+----------------+------+--------------------+------+----------+
+ size |                | size |                    | size |          +
+------+----------------+------+--------------------+------+----------+
       ^- p1                   ^- p2                       ^- p3

Alors, quelle est la cause de votre erreur?

Eh bien, imaginez que votre code écrive par erreur au-delà de la quantité de mémoire que vous avez allouée (soit parce que vous en avez alloué moins que nécessaire, soit parce que vous utilisez les mauvaises conditions aux limites, quelque part dans votre code). Dites que votre code écrit tellement de données dans p2 Qu'il commence à écraser ce qui se trouve dans le champ p3 De size. Lors de votre prochain appel à malloc, il examinera le dernier emplacement mémoire renvoyé, son champ de taille, passera à p3 + size, Puis commencera à allouer de la mémoire à partir de là. Puisque votre code a écrasé size, toutefois, cet emplacement de mémoire ne correspond plus à la mémoire allouée précédemment.

Inutile de dire que cela peut faire des ravages! Les implémenteurs de malloc ont donc mis en place un certain nombre d'assertions, ou vérifications, qui tentent de faire beaucoup de vérifications de cohérence pour détecter ce problème (et d'autres problèmes) s'ils sont sur le point de se produire. Dans votre cas particulier, ces assertions sont violées et donc malloc est annulé, vous indiquant que votre code était sur le point de faire quelque chose qu'il ne devrait vraiment pas faire.

Comme indiqué précédemment, il s’agit d’une simplification excessive, mais suffisante pour illustrer ce point. L'implémentation glibc de malloc représente plus de 5 000 lignes et de nombreuses recherches ont été menées sur la manière de créer de bons mécanismes d'allocation de mémoire dynamique. Il n'est donc pas possible de tout couvrir dans une réponse SO. Espérons que cela vous a donné un aperçu de ce qui cause vraiment le problème!

61
Jon Gjengset

Ma solution alternative à l'utilisation de Valgrind:

Je suis très heureux parce que je viens d'aider mon ami à déboguer un programme. Son programme avait exactement ce problème (malloc() provoquant l’abandon), avec le même message d’erreur de GDB.

J'ai compilé son programme en utilisant Address Sanitizer avec

gcc -Wall -g3 -fsanitize=address -o new new.c
              ^^^^^^^^^^^^^^^^^^

Et puis a couru gdb new. Lorsque le programme est terminé par SIGABRT et que, ultérieurement, malloc(), une foule d'informations utiles sont imprimées:

=================================================================
==407==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6060000000b4 at pc 0x7ffffe49ed1a bp 0x7ffffffedc20 sp 0x7ffffffed3c8
WRITE of size 104 at 0x6060000000b4 thread T0
    #0 0x7ffffe49ed19  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x5ed19)
    #1 0x8001dab in CreatHT2 /home/wsl/Desktop/hash/new.c:59
    #2 0x80031cf in main /home/wsl/Desktop/hash/new.c:209
    #3 0x7ffffe061b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #4 0x8001679 in _start (/mnt/d/Desktop/hash/new+0x1679)

0x6060000000b4 is located 0 bytes to the right of 52-byte region [0x606000000080,0x6060000000b4)
allocated by thread T0 here:
    #0 0x7ffffe51eb50 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50)
    #1 0x8001d56 in CreatHT2 /home/wsl/Desktop/hash/new.c:55
    #2 0x80031cf in main /home/wsl/Desktop/hash/new.c:209
    #3 0x7ffffe061b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

Jetons un coup d'oeil à la sortie, en particulier la trace de la pile:

La première partie dit qu'il y a une opération d'écriture invalide à new.c:59. Cette ligne se lit

memset(len,0,sizeof(int*)*p);
             ^^^^^^^^^^^^

La deuxième partie indique que la mémoire sur laquelle la mauvaise écriture s'est produite est créée à new.c:55. Cette ligne se lit

if(!(len=(int*)malloc(sizeof(int)*p))){
                      ^^^^^^^^^^^

C'est ça. Cela ne m'a pris que moins d'une demi-minute pour localiser le virus qui a perturbé mon ami pendant quelques heures. Il a réussi à localiser l’échec, mais c’est un appel ultérieur malloc() qui a échoué, sans pouvoir repérer cette erreur dans le code précédent.

Résumé: Essayez le -fsanitize=address De GCC ou Clang. Cela peut être très utile lors du débogage de problèmes de mémoire.

7
iBug

Vous êtes probablement en train de dépasser la mémoire allouée quelque part. alors le sw sous-jacent ne le détecte pas jusqu'à ce que vous appeliez malloc

Une valeur de garde peut être saisie qui est capturée par Malloc.

edit ... a ajouté ceci pour l'aide de vérification des limites

http://www.lrde.epita.fr/~akim/ccmp/doc/bounds-checking.html

2
pbernatchez

J'ai reçu le message suivant, semblable à votre:

 
 programme: malloc.c: 2372: sysmalloc: Assertion `(old_top == ((mbinptr) ((char *) & ((av)) -> bacs [((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size)> = (unsigned long) (((__ builtin_offsetof (struct malloc_chunk, fd_nextsize))) + ((2 * (sizeof (size_t)) - 1)) & ~ ((2 * (sizeof (size_t))) - 1))) && ((old_top) -> taille & 0x1) && ((unsigned long) old_end & pagemask) == 0) 'a échoué. 
 

Fait une erreur un appel de méthode avant, lorsque vous utilisez malloc. A remplacé par erreur le signe de multiplication '*' par un '+' lors de la mise à jour du facteur après l'opérateur sizeof () - lors de l'ajout d'un champ au tableau de caractères non signé.

Voici le code responsable de l'erreur dans mon cas:

 
 UCHAR * b = (UCHAR *) malloc (sizeof (UCHAR) +5); 
 B [INTBITS] = (un calcul); 
 B [ BUFSPC] = (un calcul); 
 B [BUFOVR] = (un calcul); 
 B [BUFMEM] = (un calcul); 
 B [MATCHBITS] = (un autre calcul);

Dans une autre méthode, plus tard, j’ai utilisé à nouveau malloc et le message d’erreur indiqué ci-dessus a été généré. L'appel était (assez simple):

 
 UCHAR * b = (UCHAR *) malloc (sizeof (UCHAR) * 50); 
 

Pensez à utiliser le signe '+' sur le premier appel, ce qui conduit à un calcul erroné associé à l'initialisation immédiate du tableau après (mémoire écrasée qui n'a pas été allouée au tableau), a semé la confusion dans la carte mémoire de malloc. Par conséquent, le deuxième appel s'est mal passé.

2
Michael Grieswald

Nous avons eu cette erreur parce que nous avons oublié de multiplier par sizeof (int). Notez que l'argument de malloc (..) est un nombre d'octets, pas un nombre de mots machine ou autre.

1
Phob

Je portais une application de Visual C vers gcc sous Linux et j’ai eu le même problème avec

malloc.c: 3096: sYSMALLOc: Assertion utilisant gcc sur UBUNTU 11.

J'ai déplacé le même code vers une distribution Suse (sur un autre ordinateur) et je n'ai aucun problème.

Je soupçonne que les problèmes ne sont pas dans nos programmes mais dans notre propre bibliothèque.

0
JMH

j'ai eu le même problème, j'ai utilisé malloc over n à nouveau dans une boucle pour ajouter de nouvelles données char * string. J'ai rencontré le même problème, mais après avoir libéré la mémoire allouée void free() problème ont été triés

0
namila007