¿Por qué obtengo un error de afirmación de C malloc?

85

Estoy implementando un algoritmo polinomial de divide y vencerás para poder compararlo con una implementación de OpenCL, pero no puedo ponerme malloca trabajar. Cuando ejecuto el programa, asigna un montón de cosas, verifica algunas cosas y luego las envía size/2al algoritmo. Luego, cuando vuelvo a tocar la malloclínea, escupe esto:

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

La línea en cuestión es:

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

Verifiqué el tamaño con a fprintf, y es un número entero positivo (generalmente 50 en ese punto). Intenté llamar también malloccon un número simple y todavía recibo el error. Estoy perplejo con lo que está pasando, y nada de Google que he encontrado hasta ahora es útil.

¿Alguna idea de qué está pasando? Estoy tratando de averiguar cómo compilar un GCC más nuevo en caso de que sea un error del compilador, pero realmente lo dudo.

Chris
fuente
sospecho que el problema es en realidad una línea antes de esa. ¿Quizás un doble gratis?
Mitch Wheat
Tercera línea en el 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, "tamaño:% d \ n", tamaño); out = (int *) malloc (sizeof (int) * size * 2);
Chris

Respuestas:

98

99,9% de probabilidad de que haya dañado la memoria (un búfer excesivo o insuficiente, escribió en un puntero después de que se liberó, llamó libre dos veces en el mismo puntero, etc.)

Ejecute su código en Valgrind para ver dónde su programa hizo algo incorrecto.

R Samuel Klatchko
fuente
1
fijo. Valgrind definitivamente ayudó. Transcribí mal mi antiguo código de matlab y tuve un bucle for que iteraba sobre j, luego dentro de él hizo j ++ que la mayoría sobrescribió la matriz en la que estaba escribiendo y de alguna manera causó que malloc fallara. ¡gracias por la ayuda!
Chris
Valgrind era la herramienta que necesitaba para averiguar qué estaba pasando cuando recibí este error. Gracias por mencionarlo.
alexwells
77

Para que comprendas mejor por qué sucede esto, me gustaría ampliar un poco la respuesta de @ r-samuel-klatchko.

Cuando llamas malloc, lo que realmente está sucediendo es un poco más complicado que solo darte un trozo de memoria para jugar. Debajo del capó, malloctambién guarda cierta información de limpieza sobre la memoria que le ha proporcionado (lo más importante, su tamaño), para que cuando llame free, sepa cosas como cuánta memoria liberar. Esta información normalmente se guarda justo antes de que le devuelva la ubicación de la memoria malloc. Se puede encontrar información más exhaustiva en Internet ™ , pero la idea (muy) básica es algo como esto:

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

Basándose en esto (y simplificando mucho las cosas), cuando llama malloc, necesita obtener un puntero a la siguiente parte de la memoria que esté disponible. Una forma muy sencilla de hacer esto es mirar el bit de memoria anterior que dio y mover los sizebytes hacia abajo (o hacia arriba) en la memoria. Con esta implementación, terminas con tu memoria con un aspecto similar a esto después de la asignación p1, p2y p3:

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

Entonces, ¿qué está causando tu error?

Bueno, imagine que su código escribe erróneamente más allá de la cantidad de memoria que ha asignado (ya sea porque asignó menos de la que necesitaba, como era su problema, o porque está utilizando las condiciones de límite incorrectas en algún lugar de su código). Supongamos que su código escribe tantos datos p2que comienza a sobrescribir lo que está en p3el sizecampo. La próxima vez que llame malloc, verá la última ubicación de memoria que devolvió, verá su campo de tamaño, se moverá p3 + sizey luego comenzará a asignar memoria desde allí. sizeSin embargo, dado que su código se ha sobrescrito , esta ubicación de memoria ya no es posterior a la memoria asignada anteriormente.

No hace falta decir que esto puede causar estragos. Los implementadores de malloc, por lo tanto, han introducido una serie de "afirmaciones", o comprobaciones, que intentan realizar un montón de comprobaciones de cordura para detectar este (y otros problemas) si están a punto de suceder. En su caso particular, estas afirmaciones se violan y, por lo tanto malloc, se cancelan, indicándole que su código estaba a punto de hacer algo que realmente no debería estar haciendo.

Como se dijo anteriormente, esta es una simplificación excesiva, pero es suficiente para ilustrar el punto. La implementación glibc de malloces de más de 5k líneas, y ha habido una cantidad sustancial de investigación sobre cómo construir buenos mecanismos de asignación de memoria dinámica, por lo que cubrirlo todo en una respuesta SO no es posible. ¡Esperamos que esto le haya dado una idea de lo que realmente está causando el problema!

Jon Gjengset
fuente
14

Mi solución alternativa al uso de Valgrind:

Estoy muy feliz porque acabo de ayudar a mi amigo a depurar un programa. Su programa tenía exactamente este problema ( malloc()provocando un aborto), con el mismo mensaje de error de GDB.

Compilé su programa usando Address Sanitizer con

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

Y luego corrió gdb new. Cuando el programa se termina por una SIGABRTcausa posterior malloc(), se imprime una gran cantidad de información útil:

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

Echemos un vistazo a la salida, especialmente al seguimiento de la pila:

La primera parte dice que hay una operación de escritura no válida en new.c:59. Esa línea dice

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

La segunda parte dice que se crea la memoria en la que ocurrió la mala escritura new.c:55. Esa línea dice

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

Eso es. Solo me tomó menos de medio minuto localizar el error que confundió a mi amigo durante unas horas. Logró localizar la falla, pero es una malloc()llamada posterior que falló, sin poder detectar este error en el código anterior.

En resumen: pruebe con -fsanitize=addressGCC o Clang. Puede resultar muy útil para depurar problemas de memoria.

iBug
fuente
1
Me acaba de salvar la vida.
Nate Symer
2

Probablemente esté sobrepasando la memoria asignada en alguna parte. entonces el sw subyacente no lo detecta hasta que llamas a malloc

Puede haber un valor de guardia golpeado que está siendo capturado por malloc.

editar ... agregué esto para la ayuda de verificación de límites

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

pbernatchez
fuente
2

Recibí el siguiente mensaje, similar al tuyo:

    programa: malloc.c: 2372: sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) & ((av) -> bins [((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && tamaño_antiguo == 0) || ((unsigned long) (old_size)> = (unsigned_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) 'falló.

Cometí un error en alguna llamada al método antes, al usar malloc. Sobrescribió erróneamente el signo de multiplicación '*' con un '+', al actualizar el factor después del operador sizeof () - al agregar un campo a la matriz de caracteres sin firmar.

Aquí está el código responsable del error en mi caso:

    UCHAR * b = (UCHAR *) malloc (tamaño de (UCHAR) +5);
    b [INTBITS] = (algunos cálculos);
    b [BUFSPC] = (algunos cálculos);
    b [BUFOVR] = (algunos cálculos);
    b [BUFMEM] = (algunos cálculos);
    b [MATCHBITS] = (algunos cálculos);

En otro método más tarde, utilicé malloc nuevamente y produjo el mensaje de error que se muestra arriba. La llamada fue (bastante simple):

    UCHAR * b = (UCHAR *) malloc (tamaño de (UCHAR) * 50);

Piense en usar el signo '+' - en la primera llamada, que conduce a errores de cálculo en combinación con la inicialización inmediata de la matriz después (sobrescribir la memoria que no se asignó a la matriz), trajo cierta confusión al mapa de memoria de malloc. Por lo tanto, la segunda llamada salió mal.

Michael Grieswald
fuente
0

Recibimos este error porque nos olvidamos de multiplicar por sizeof (int). Tenga en cuenta que el argumento de malloc (..) es un número de bytes, no un número de palabras de máquina o lo que sea.

Phob
fuente
0

Tengo el mismo problema, utilicé malloc sobre n otra vez en un bucle para agregar nuevos datos de cadena de caracteres *. Me enfrenté al mismo problema, pero después de liberar el void free()problema de memoria asignada se solucionó

namila007
fuente
-2

Estaba transfiriendo una aplicación de Visual C a gcc en Linux y tuve el mismo problema con

malloc.c: 3096: sYSMALLOc: Afirmación usando gcc en UBUNTU 11.

Moví el mismo código a una distribución de Suse (en otra computadora) y no tengo ningún problema.

Sospecho que los problemas no están en nuestros programas sino en la propia libc.

JMH
fuente