Por que obtenho uma falha de declaração C malloc?

86

Estou implementando um algoritmo polinomial de divisão e conquista para que possa compará-lo com uma implementação do OpenCL, mas não consigo malloctrabalhar. Quando executo o programa, ele aloca um monte de coisas, verifica algumas coisas e envia o size/2para o algoritmo. Então, quando eu acerto a malloclinha novamente, ele cospe o seguinte:

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 linha em questão é:

Eu verifiquei o tamanho com um fprintf, e é um número inteiro positivo (normalmente 50 nesse ponto). Também tentei ligar malloccom um número simples e ainda recebo o erro. Estou simplesmente perplexo com o que está acontecendo, e nada do Google que encontrei até agora é útil.

Alguma ideia sobre o que se passa? Estou tentando descobrir como compilar um GCC mais recente no caso de ser um erro do compilador, mas eu realmente duvido.

Chris
fonte
Suspeito que o problema seja, na verdade, uma linha antes dessa. Talvez um duplo grátis?
Mitch Wheat
3ª linha no programa: int * mult (int size, int * a, int * b) {int * out, i, j, * tmp1, * tmp2, * tmp3, * tmpa1, * tmpa2, * tmpb1, * tmpb2 , d, * res1, * res2; fprintf (stdout, "tamanho:% d \ n", tamanho); out = (int *) malloc (sizeof (int) * size * 2);
Chris

Respostas:

100

99,9% de probabilidade de que você tenha corrompido a memória (excesso ou falta de fluxo em um buffer, gravou em um ponteiro depois que ele foi liberado, chamado de free duas vezes no mesmo ponteiro, etc.)

Execute seu código em Valgrind para ver onde seu programa fez algo incorreto.

R Samuel Klatchko
fonte
1
fixo. Valgrind definitivamente ajudou. Eu transcrevi meu código matlab antigo de forma errada e tinha um loop for que iterou sobre j, então dentro dele fez j ++ que sobrescreveu o array em que estava escrevendo e de alguma forma causou a falha de malloc. Obrigado pela ajuda!
Chris
Valgrind era apenas a ferramenta de que eu precisava para descobrir o que estava acontecendo quando recebi esse erro. Obrigado por mencionar isso.
alexwells
78

Para lhe dar uma compreensão melhor de por que isso acontece, gostaria de expandir um pouco a resposta de @ r-samuel-klatchko.

Quando você liga malloc, o que realmente está acontecendo é um pouco mais complicado do que apenas dar a você um pedaço de memória para brincar. Sob o capô, malloctambém mantém algumas informações de manutenção sobre a memória que forneceu (mais importante, seu tamanho), para que, quando você ligar free, ele saiba coisas como quanta memória deve ser liberada. Essas informações são normalmente mantidas imediatamente antes do local da memória retornado a você por malloc. Informações mais completas podem ser encontradas na internet ™ , mas a ideia (muito) básica é mais ou menos assim:

Com base nisso (e simplificando muito as coisas), quando você chama malloc, ele precisa obter um ponteiro para a próxima parte da memória que está disponível. Uma maneira muito simples de fazer isso é examinar o bit de memória anterior que ele cedeu e mover os sizebytes para baixo (ou para cima) na memória. Com esta implementação, você acaba com sua memória parecida com isto após a alocação p1, p2e p3:

Então, o que está causando o seu erro?

Bem, imagine que seu código grava erroneamente além da quantidade de memória que você alocou (ou porque você alocou menos do que o necessário como era o seu problema ou porque está usando as condições de limite erradas em algum lugar do seu código). Diga seu código escreve tantos dados para p2que ele começa a substituir o que está em p3's sizecampo. Na próxima chamada malloc, ele irá olhar para a última localização de memória que retornou, olhar para seu campo de tamanho, mover para p3 + sizee então começar a alocar memória de lá. Como seu código foi sobrescrito size, no entanto, esse local de memória não é mais posterior à memória alocada anteriormente.

Nem é preciso dizer que isso pode causar estragos! Os implementadores de malloc, portanto, colocaram uma série de "asserções", ou verificações, que tentam fazer uma série de verificações de sanidade para detectar este (e outros problemas) se eles estiverem prestes a acontecer. No seu caso específico, essas asserções são violadas e, portanto malloc, são canceladas, informando que seu código estava prestes a fazer algo que realmente não deveria estar fazendo.

Como afirmado anteriormente, esta é uma simplificação grosseira, mas é suficiente para ilustrar o ponto. A implementação glibc de malloctem mais de 5k linhas e tem havido uma quantidade substancial de pesquisas sobre como construir bons mecanismos de alocação de memória dinâmica, portanto, cobrir tudo em uma resposta SO não é possível. Espero que isso tenha lhe dado uma visão do que realmente está causando o problema!

Jon Gjengset
fonte
16

Minha solução alternativa para usar Valgrind:

Estou muito feliz porque acabei de ajudar meu amigo a depurar um programa. Seu programa tinha exatamente este problema (malloc() causando aborto), com a mesma mensagem de erro do GDB.

Compilei seu programa usando Address Sanitizer com

E então correu gdb new. Quando o programa é encerrado por SIGABRTcausado em um subsequente malloc(), uma série de informações úteis são impressas:

Vamos dar uma olhada na saída, especialmente o rastreamento de pilha:

A primeira parte diz que há uma operação de gravação inválida em new.c:59. Essa linha diz

A segunda parte diz que a memória em que a gravação incorreta aconteceu é criada new.c:55. Essa linha diz

É isso aí. Levei menos de meio minuto para localizar o bug que confundiu meu amigo por algumas horas. Ele conseguiu localizar a falha, mas é uma subsequentemalloc() chamada que falhou, sem ser capaz de detectar esse erro no código anterior.

Resumindo: experimente o -fsanitize=addressdo GCC ou do Clang. Pode ser muito útil ao depurar problemas de memória.

iBug
fonte
1
Você acabou de salvar minha vida.
Nate Symer,
2

Você provavelmente está ultrapassando o mem alocado em algum lugar. então o sw subjacente não pega até que você chame malloc

Pode haver um valor de proteção superado que está sendo capturado por malloc.

editar ... adicionado isto para ajuda de verificação de limites

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

Pbernatchez
fonte
2

Recebi a seguinte mensagem, semelhante à sua:

    programa: malloc.c: 2372: sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) & ((av) -> bins [((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((longo sem sinal) (tamanho_antigo)> = (longo sem sinal) ((((__ builtin_offsetof (struct malloc_chunk, fd_nextsize)) + ((2 * (sizeof (size_t)))) - 1)) & ~ ((2 * (sizeof (size_t))) - 1))) && ((old_top) -> size & 0x1) && ((long sem sinal) old_end & pagemask) == 0) 'falhou.

Cometeu um erro alguma chamada de método antes, ao usar malloc. Sobrescreveu erroneamente o sinal de multiplicação '*' com um '+', ao atualizar o fator após o operador sizeof () - ao adicionar um campo a um array de caracteres sem sinal.

Aqui está o código responsável pelo erro no meu caso:

    UCHAR * b = (UCHAR *) malloc (sizeof (UCHAR) +5);
    b [INTBITS] = (algum cálculo);
    b [BUFSPC] = (algum cálculo);
    b [BUFOVR] = (algum cálculo);
    b [BUFMEM] = (algum cálculo);
    b [MATCHBITS] = (algum cálculo);

Posteriormente, em outro método, usei malloc novamente e ele produziu a mensagem de erro mostrada acima. A chamada foi (bastante simples):

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

Pense em usar o sinal '+' - na primeira chamada, o que leva a um cálculo incorreto em combinação com a inicialização imediata do array depois (sobrescrever a memória que não foi alocada para o array), trouxe alguma confusão ao mapa de memória de malloc. Portanto, a segunda chamada deu errado.

Michael Grieswald
fonte
0

Recebemos esse erro porque esquecemos de multiplicar por sizeof (int). Observe que o argumento para malloc (..) é um número de bytes, não um número de palavras de máquina ou qualquer outra coisa.

Phob
fonte
0

Eu tenho o mesmo problema, usei malloc sobre n novamente em um loop para adicionar novos dados de string char *. Eu enfrentei o mesmo problema, mas depois de liberar o void free()problema de memória alocada foram resolvidos

namila007
fonte
-2

Eu estava portando um aplicativo do Visual C para o gcc no Linux e tive o mesmo problema com

malloc.c: 3096: sYSMALLOc: Asserção usando gcc em UBUNTU 11.

Mudei o mesmo código para uma distribuição Suse (em outro computador) e não tenho nenhum problema.

Suspeito que os problemas não estejam em nossos programas, mas na própria libc.

JMH
fonte