Почему я получаю ошибку утверждения C malloc?

86

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

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

Речь идет о следующей строке:

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

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

Крис
источник
Я подозреваю, что проблема на самом деле в строке перед этой. Возможно двойной бесплатный?
Mitch Wheat
3-я строка в программе: int * mult (int size, int * a, int * b) {int * out, i, j, * tmp1, * tmp2, * tmp3, * tmpa1, * tmpa2, * tmpb1, * tmpb2 , d, * res1, * res2; fprintf (stdout, "размер:% d \ n", размер); out = (int *) malloc (sizeof (int) * size * 2);
Крис

Ответы:

100

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

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

R Сэмюэл Клатчко
источник
1
исправлено. Valgrind определенно помог. Я неправильно переписал свой старый код Matlab и у меня был цикл for, который повторял j, затем внутри него выполнялся j ++, который больше всего перезаписывал массив, в который он писал, и каким-то образом вызывал сбой malloc. Спасибо за помощь!
Крис
Valgrind был как раз тем инструментом, который мне нужен, чтобы понять, что происходит, когда я получил эту ошибку. Спасибо, что упомянули об этом.
Alexwells
78

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

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

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

Итак, что вызывает вашу ошибку?

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

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

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

Джон Дженгсет
источник
16

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

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

Я скомпилировал его программу с помощью Address Sanitizer с

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

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

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

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

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

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

iBug
источник
1
Ты только что спас мне жизнь.
Нейт Саймер 08
2

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

Может быть сбито охранное значение, которое перехватывается malloc.

изменить ... добавил это для справки по проверке границ

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

Pbernatchez
источник
2

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

    программа: 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) 'не удалось.

Раньше совершал ошибку при вызове метода при использовании malloc. Ошибочно заменял знак умножения '*' на '+' при обновлении множителя после sizeof () - оператора при добавлении поля в массив unsigned char.

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

    UCHAR * b = (UCHAR *) malloc (sizeof (UCHAR) +5);
    b [INTBITS] = (некоторый расчет);
    b [BUFSPC] = (небольшой расчет);
    b [BUFOVR] = (небольшой расчет);
    b [BUFMEM] = (небольшой расчет);
    b [MATCHBITS] = (некоторый расчет);

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

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

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

Майкл Грисвальд
источник
0

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

Фоб
источник
0

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

namila007
источник
-2

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

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

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

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

JMH
источник