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

Я реализую полиномиальный алгоритм «разделяй и властвуй», поэтому я могу сравнить его с реализацией 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

Речь идет о строке:

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 на случай, если это ошибка компилятора, но я очень в этом сомневаюсь.


person Chris    schedule 07.06.2010    source источник
comment
Я подозреваю, что проблема на самом деле в строке перед этой. Возможно, двойной бесплатно?   -  person Mitch Wheat    schedule 07.06.2010
comment
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);   -  person Chris    schedule 07.06.2010


Ответы (8)


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

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

person R Samuel Klatchko    schedule 07.06.2010
comment
исправлено. Valgrind определенно помог. Я неправильно расшифровал свой старый код Matlab, и у меня был цикл for, который повторялся по j, а затем внутри него выполнялся j++, который больше всего перезаписывал массив, на котором он писал, и каким-то образом вызывал сбой malloc. Спасибо за помощь! - person Chris; 07.06.2010
comment
Valgrind был просто инструментом, который мне был нужен, чтобы понять, что происходит, когда я получаю эту ошибку. Спасибо, что упомянули об этом. - person alexwells; 31.12.2012
comment
Посмотрите мой ответ ниже, чтобы найти решение, использующее Address Sanitizer для более простого использования и лучшего руководства/иллюстрации. - person iBug; 11.04.2021

Чтобы дать вам лучшее понимание того, почему это происходит, я хотел бы немного расширить ответ @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, что он начинает перезаписывать то, что находится в поле size p3. Когда вы в следующий раз вызовете malloc, он проверит последнюю возвращенную ячейку памяти, просмотрит поле своего размера, перейдет к p3 + size и затем начнет выделение памяти оттуда. Однако, поскольку ваш код перезаписал size, эта ячейка памяти больше не находится после ранее выделенной памяти.

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

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

person Jon Gjengset    schedule 14.10.2013

Мое альтернативное решение для использования 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. Это может быть очень полезно при отладке проблем с памятью.

person iBug    schedule 25.12.2018
comment
Ты только что спас мне жизнь. - person Nate Symer; 08.04.2020

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

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

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

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

person pbernatchez    schedule 07.06.2010

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


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

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

person Michael Grieswald    schedule 31.10.2016

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

person Phob    schedule 02.05.2011

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

person namila007    schedule 26.04.2017

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

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

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

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

person JMH    schedule 05.02.2012