Warum erhalte ich einen C-Malloc-Assertionsfehler?

85

Ich implementiere einen Divide and Conquer-Polynomalgorithmus, damit ich ihn mit einer OpenCL-Implementierung vergleichen kann, aber ich kann nicht mallocarbeiten. Wenn ich das Programm ausführe, ordnet es eine Menge Dinge zu, überprüft einige Dinge und sendet sie dann size/2an den Algorithmus. Wenn ich dann wieder auf die mallocLinie treffe, spuckt es Folgendes aus:

malloc.c:3096: sYSMALLOc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((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)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed.
Aborted

Die fragliche Zeile lautet:

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);
}

Ich habe die Größe mit a überprüft fprintfund es ist eine positive ganze Zahl (normalerweise 50 zu diesem Zeitpunkt). Ich habe versucht, auch mallocmit einer einfachen Nummer anzurufen, und es wird immer noch der Fehler angezeigt. Ich bin nur ratlos darüber, was los ist, und nichts von Google, was ich bisher gefunden habe, ist hilfreich.

Irgendwelche Ideen, was los ist? Ich versuche herauszufinden, wie ein neueres GCC kompiliert werden kann, falls es sich um einen Compilerfehler handelt, aber ich bezweifle es wirklich.

Chris
quelle
Ich vermute, das Problem ist tatsächlich eine Zeile vor dieser. Vielleicht ein Doppel frei?
Mitch Wheat
3. Zeile im Programm: int * mult (int Größe, int * a, int * b) {int * out, i, j, * tmp1, * tmp2, * tmp3, * tmpa1, * tmpa2, * tmpb1, * tmpb2 , d, * res1, * res2; fprintf (stdout, "Größe:% d \ n", Größe); out = (int *) malloc (sizeof (int) * size * 2);
Chris

Antworten:

98

99,9% wahrscheinlich, dass Sie den Speicher beschädigt haben (ein Puffer ist über- oder untergelaufen, wurde nach dem Freigeben in einen Zeiger geschrieben, zweimal mit demselben Zeiger als frei bezeichnet usw.)

Führen Sie Ihren Code unter Valgrind aus , um festzustellen , wo Ihr Programm etwas falsch gemacht hat.

R Samuel Klatchko
quelle
1
Fest. Valgrind hat definitiv geholfen. Ich habe meinen alten Matlab-Code falsch transkribiert und hatte eine for-Schleife, die über j iterierte, dann in j ++, was das Array, auf das es schrieb, am meisten überschrieb und irgendwie dazu führte, dass malloc fehlschlug. Danke für die Hilfe!
Chris
Valgrind war genau das Werkzeug, das ich brauchte, um herauszufinden, was los war, als ich diesen Fehler bekam. Danke, dass du es erwähnt hast.
Alexwells
77

Um Ihnen ein besseres Verständnis dafür zu geben, warum dies geschieht, möchte ich die Antwort von @ r-samuel-klatchko etwas näher erläutern.

Wenn Sie anrufen malloc, ist das, was wirklich passiert, etwas komplizierter, als Ihnen nur einen Teil des Gedächtnisses zum Spielen zu geben. Unter der Haube werden mallocauch einige Haushaltsinformationen über den Speicher gespeichert, den Sie erhalten haben (vor allem über die Größe), sodass er beim Anrufen beispielsweise freeweiß, wie viel Speicher freigegeben werden muss. Diese Informationen werden normalerweise direkt vor dem Speicherort gespeichert, den Sie von erhalten haben malloc. Ausführlichere Informationen finden Sie im Internet ™ , aber die (sehr) Grundidee ist ungefähr so:

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

Darauf aufbauend (und die Dinge stark vereinfachend) muss beim Aufrufen mallocein Zeiger auf den nächsten verfügbaren Teil des Speichers abgerufen werden. Eine sehr einfache Möglichkeit, dies zu tun, besteht darin, das zuvor verschenkte Speicherbit zu betrachten und die sizeBytes im Speicher weiter nach unten (oder oben) zu verschieben. Mit dieser Implementierung, beenden Sie mit Ihrem Gedächtnis auf etwas so aussehen , nachdem die Zuweisung p1, p2und p3:

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

Was verursacht Ihren Fehler?

Stellen Sie sich vor, Ihr Code schreibt fälschlicherweise über die von Ihnen zugewiesene Speichermenge hinaus (entweder weil Sie weniger zugewiesen haben, als Sie als Problem benötigt haben, oder weil Sie irgendwo in Ihrem Code die falschen Randbedingungen verwenden). Sagen Sie den Code schreibt so viele Daten , p2dass es beginnt zu überschreiben , was in ist p3‚s sizeFeld. Wenn Sie jetzt das nächste Mal anrufen malloc, wird der zuletzt zurückgegebene Speicherort angezeigt, das Größenfeld angezeigt, der p3 + sizeSpeicherplatz verschoben und von dort aus mit der Zuweisung von Speicher begonnen. Da Ihr Code jedoch überschrieben sizewurde, befindet sich dieser Speicherort nicht mehr hinter dem zuvor zugewiesenen Speicher.

Unnötig zu sagen, dass dies Chaos zerstören kann! Die Implementierer von mallochaben daher eine Reihe von "Behauptungen" oder Überprüfungen abgegeben, die versuchen, eine Reihe von Überprüfungen der geistigen Gesundheit durchzuführen, um dies (und andere Probleme) zu erkennen, wenn sie in Kürze eintreten. In Ihrem speziellen Fall werden diese Behauptungen verletzt und daher mallocabgebrochen, um Ihnen mitzuteilen, dass Ihr Code etwas tun würde, was er eigentlich nicht tun sollte.

Wie bereits erwähnt, handelt es sich um eine grobe Vereinfachung, die jedoch ausreicht, um den Punkt zu veranschaulichen. Die glibc-Implementierung von mallocumfasst mehr als 5.000 Zeilen, und es wurden umfangreiche Untersuchungen zum Aufbau guter dynamischer Speicherzuweisungsmechanismen durchgeführt, sodass es nicht möglich ist, alles in einer SO-Antwort abzudecken. Hoffentlich haben Sie dadurch einen Überblick darüber bekommen, was das Problem wirklich verursacht!

Jon Gjengset
quelle
14

Meine alternative Lösung zur Verwendung von Valgrind:

Ich bin sehr glücklich, weil ich gerade meinem Freund beim Debuggen eines Programms geholfen habe. Sein Programm hatte genau dieses Problem ( malloc()was zu einem Abbruch führte) mit der gleichen Fehlermeldung von GDB.

Ich habe sein Programm mit Address Sanitizer mit kompiliert

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

Und dann rannte gdb new. Wenn das Programm durch SIGABRTverursacht in einer Folge beendet wird malloc(), werden viele nützliche Informationen gedruckt:

=================================================================
==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)

Schauen wir uns die Ausgabe an, insbesondere die Stapelverfolgung:

Der erste Teil besagt, dass bei eine ungültige Schreiboperation vorliegt new.c:59. Diese Zeile lautet

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

Der zweite Teil besagt, dass der Speicher, in dem das fehlerhafte Schreiben stattgefunden hat, bei erstellt wird new.c:55. Diese Zeile lautet

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

Das ist es. Ich brauchte nur weniger als eine halbe Minute, um den Fehler zu finden, der meinen Freund einige Stunden lang verwirrte. Er hat es geschafft, den Fehler zu lokalisieren, aber es ist ein nachfolgender malloc()Aufruf, der fehlgeschlagen ist, ohne diesen Fehler im vorherigen Code erkennen zu können.

Fazit: Probieren Sie das -fsanitize=addressvon GCC oder Clang. Dies kann beim Debuggen von Speicherproblemen sehr hilfreich sein.

iBug
quelle
1
Du hast mir gerade das Leben gerettet.
Nate Symer
2

Sie laufen wahrscheinlich irgendwo über das zugewiesene Mem hinaus. dann nimmt der zugrunde liegende sw es nicht auf, bis Sie malloc aufrufen

Möglicherweise ist ein Wachwert überfüllt, der von Malloc gefangen wird.

edit ... hat dies hinzugefügt, um die Hilfe zur Überprüfung der Grenzen zu überprüfen

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

pbernatchez
quelle
2

Ich habe die folgende Nachricht erhalten, ähnlich Ihrer:

    Programm: malloc.c: 2372: sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) & (av) -> bins [((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) -> size & 0x1) && ((unsigned long) old_end & pagemask) == 0) 'fehlgeschlagen.

Bei der Verwendung von malloc ist bei einem Methodenaufruf ein Fehler aufgetreten. Das Multiplikationszeichen '*' wurde fälschlicherweise mit einem '+' überschrieben, wenn der Faktor nach dem Operator sizeof () - beim Hinzufügen eines Felds zu einem vorzeichenlosen Zeichenarray aktualisiert wurde.

Hier ist der Code, der für den Fehler in meinem Fall verantwortlich ist:

    UCHAR * b = (UCHAR *) malloc (Größe von (UCHAR) +5);
    b [INTBITS] = (einige Berechnungen);
    b [BUFSPC] = (einige Berechnungen);
    b [BUFOVR] = (einige Berechnungen);
    b [BUFMEM] = (einige Berechnungen);
    b [MATCHBITS] = (einige Berechnungen);

Bei einer anderen Methode habe ich später wieder malloc verwendet und die oben gezeigte Fehlermeldung ausgegeben. Der Anruf war (einfach genug):

    UCHAR * b = (UCHAR *) malloc (Größe von (UCHAR) * 50);

Denken Sie daran, dass die Verwendung des '+' - Zeichens beim ersten Aufruf, was zu einer Fehlkalkulation in Kombination mit einer sofortigen Initialisierung des Arrays nach (Überschreiben des Speichers, der nicht dem Array zugeordnet war) führte, einige Verwirrung in der Speicherzuordnung von malloc verursachte. Daher ging der 2. Anruf schief.

Michael Grieswald
quelle
0

Wir haben diesen Fehler erhalten, weil wir vergessen haben, mit sizeof (int) zu multiplizieren. Beachten Sie, dass das Argument für malloc (..) eine Anzahl von Bytes ist, nicht die Anzahl von Maschinenwörtern oder was auch immer.

Phob
quelle
0

Ich habe das gleiche Problem, ich habe Malloc immer wieder in einer Schleife verwendet, um neue char * string-Daten hinzuzufügen. Ich hatte das gleiche Problem, aber nach der Freigabe wurden die zugewiesenen Speicherprobleme void free()sortiert

namila007
quelle
-2

Ich habe eine Anwendung von Visual C über Linux auf gcc portiert und hatte das gleiche Problem mit

malloc.c: 3096: sYSMALLOc: Assertion mit gcc unter UBUNTU 11.

Ich habe denselben Code in eine Suse-Distribution (auf einem anderen Computer) verschoben und habe kein Problem.

Ich vermute, dass die Probleme nicht in unseren Programmen liegen, sondern in der eigenen Bibliothek.

JMH
quelle