Почему я получаю ошибку подтверждения malloc? - PullRequest
70 голосов
/ 07 июня 2010

Я реализую полиномиальный алгоритм «разделяй и властвуй», чтобы я мог сравнить его с реализацией OpenCL, но я не могу заставить работать malloc. Когда я запускаю программу, она распределяет кучу вещей, проверяет некоторые вещи, а затем отправляет size/2 алгоритму. Затем, когда я снова попадаю в строку malloc, она выплевывает следующее:

malloc.c: 3096: sYSMALLOc: утверждение `(old_top == (((mbinptr) (((char *) & ((av) -> bin [[((1) - 1) * 2]))) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size)> = (unsigned long) ((((__buildin_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) 'не удалось. Отменено

Строка, о которой идет речь:

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

Я проверил размер с помощью fprintf, и это положительное целое число (обычно 50 в этой точке). Я попытался позвонить malloc с обычным номером, и все еще получаю ошибку. Я просто ошеломлен тем, что происходит, и ничто из Google, которое я нашел до сих пор, не помогает.

Есть идеи, что происходит? Я пытаюсь выяснить, как скомпилировать новый GCC на случай, если это ошибка компилятора, но я действительно в этом сомневаюсь.

Ответы [ 8 ]

82 голосов
/ 07 июня 2010

99,9% вероятности того, что вы испортили память (переполнили или переполнили буфер, записали в указатель после его освобождения, дважды вызвали освобождение по одному и тому же указателю и т. Д.)

Запустите ваш код под Valgrind , чтобы увидеть, где ваша программа сделала что-то неправильно.

60 голосов
/ 14 октября 2013

Чтобы лучше понять , почему это происходит, я бы хотел немного подробнее остановиться на ответе @ r-samuel-klatchko.

Когда вы звоните malloc, то, что на самом деле происходит, немного сложнее, чем просто дать вам кусок памяти для игры. В тайнике malloc также хранится некоторая служебная информация о памяти, которую она дала вам (что наиболее важно, о ее размере), так что когда вы вызываете free, она знает такие вещи, как, сколько памяти освободить. Эта информация обычно хранится непосредственно перед тем, как ячейка памяти будет возвращена вам malloc. Более подробную информацию можно найти в Интернете , но (очень) основная идея выглядит примерно так:

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

Опираясь на это (и значительно упрощая вещи), когда вы вызываете malloc, он должен получить указатель на следующую доступную часть памяти. Один из очень простых способов сделать это - посмотреть на предыдущий бит памяти, который он выделил, и переместить size байтов дальше вниз (или вверх) в памяти. В этой реализации ваша память выглядит примерно так после выделения p1, p2 и p3:

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

Итак, в чем причина вашей ошибки?

Хорошо, представьте, что ваш код ошибочно записывает объем памяти, который вы выделили (либо потому, что вы выделили меньше, чем нужно, как было вашей проблемой, либо потому, что где-то в вашем коде используются неправильные граничные условия). Скажем, ваш код записывает столько данных в p2, что начинает перезаписывать то, что находится в поле p3 size. При следующем вызове malloc он будет смотреть на последнюю ячейку памяти, которую он возвратил, посмотреть на поле размера, перейти к p3 + size и затем начать выделять память оттуда. Однако, поскольку ваш код перезаписан size, эта область памяти больше не находится после ранее выделенной памяти.

Само собой разумеется, это может разрушить! Поэтому разработчики malloc выдвинули ряд «утверждений» или проверок, которые пытаются выполнить кучу проверок работоспособности, чтобы уловить это (и другие проблемы), если они вот-вот произойдут. В вашем конкретном случае эти утверждения нарушаются, и, таким образом, malloc прерывается, сообщая вам, что ваш код собирается сделать что-то, чего он на самом деле не должен делать.

Как уже говорилось, это грубое упрощение, но этого достаточно, чтобы проиллюстрировать это. Реализация glibc malloc занимает более 5 тыс. Строк, и было проведено значительное количество исследований по созданию хороших механизмов динамического распределения памяти, поэтому охват всего этого в SO-ответе невозможен. Надеюсь, это дало вам представление о том, что действительно вызывает проблему, хотя!

7 голосов
/ 25 декабря 2018

Мое альтернативное решение для использования Valgrind:

Я очень счастлив, потому что я только что помог моему другу отладить программу.У его программы была именно эта проблема (malloc(), вызывающая прерывание), с тем же сообщением об ошибке от GDB.

Я скомпилировал его программу, используя Address Sanitizer с

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

А потом побежал gdb new.Когда программа завершается из-за SIGABRT, вызванного в последующем malloc(), выводится много полезной информации:

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

Давайте посмотрим на вывод, особенно трассировку стека:

Первая часть говорит, что в new.c:59 есть недопустимая операция записи.Эта строка гласит

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

Вторая часть говорит, что память, на которой произошла плохая запись, создана в new.c:55.Эта строка гласит

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

Вот и все.У меня ушло меньше получаса, чтобы найти ошибку, которая смутила моего друга на несколько часов.Ему удалось определить местонахождение ошибки, но это был последующий вызов malloc(), который не удался, но не смог обнаружить эту ошибку в предыдущем коде.

Подвести итог: Попробуйте -fsanitize=address из GCC или Clang.Это может быть очень полезно при отладке проблем с памятью.

2 голосов
/ 31 октября 2016

Я получил следующее сообщение, похожее на ваше:


    program: 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)' failed.

При использовании malloc произошла ошибка при вызове некоторого метода.Ошибочно перезаписал знак умножения '*' на '+', когда обновлял коэффициент после оператора sizeof () - при добавлении поля в массив без знака.

Вот код, отвечающий за ошибку в моем случае:


    UCHAR* b=(UCHAR*)malloc(sizeof(UCHAR)+5);
    b[INTBITS]=(some calculation);
    b[BUFSPC]=(some calculation);
    b[BUFOVR]=(some calculation);
    b[BUFMEM]=(some calculation);
    b[MATCHBITS]=(some calculation);

В другом методе позже я снова использовал malloc, и он выдал сообщение об ошибке, показанное выше.Вызов был (достаточно прост):


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

Подумайте, используя знак «+» - при первом вызове, что приводит к неправильному исчислению в сочетании с немедленной инициализацией массива после (перезаписи)память, которая не была выделена для массива), внесла некоторую путаницу в карту памяти malloc. Следовательно, 2-й вызов не прошел.

2 голосов
/ 07 июня 2010

Вы, вероятно, превышаете пределы выделенной памяти где-то. тогда базовый sw не поймет его, пока вы не вызовете malloc

Возможно, это защитное значение, захваченное malloc.

edit ... добавил это для помощи в проверке границ

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

1 голос
/ 03 мая 2011

Мы получили эту ошибку, потому что мы забыли умножить на sizeof (int).Обратите внимание, что аргумент для malloc (..) - это число байтов, а не количество машинных слов или что-либо еще.

0 голосов
/ 26 апреля 2017

У меня возникла та же проблема, я снова использовал malloc над n в цикле для добавления новых строковых данных char *. Я столкнулся с той же проблемой, но после освобождения выделенной памяти void free() проблема была отсортирована

0 голосов
/ 05 февраля 2012

Я переносил одно приложение из Visual C в gcc через Linux, и у меня была такая же проблема с

malloc.c: 3096: sYSMALLOc: утверждение с использованием gcc в UBUNTU 11.

Я перенес тот же код в дистрибутив Suse (на другом компьютере), и у меня нет проблем.

Я подозреваю, что проблема не в наших программах, а в собственной libc.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...