Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Regla 0x3014h: Prohibición de doble liberación de memoria (double free) sobre el mismo puntero

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

0x3014h: Prohibición de doble liberación de memoria (double free) sobre el mismo puntero

Enunciado normativo

NO DEBE invocarse free dos veces sobre el mismo bloque de memoria. Cada bloque reservado con el asignador DEBE liberarse exactamente una vez, aunque existan varios punteros que lo referencien.

¿Por qué existe esta regla?

El problema

El asignador de memoria (glibc, jemalloc, mimalloc) mantiene estructuras auxiliares con la lista de bloques libres y ocupados. free(ptr) devuelve el bloque a esa lista y puede fusionarlo con vecinos. Invocar free otra vez sobre la misma dirección hace que el asignador procese dos veces un bloque que ya está libre: intenta fusionarlo consigo mismo, corrompe la lista y, en la mayoría de las implementaciones, aborta el proceso con SIGABRT y el mensaje double free or corruption. El error es frecuente con varios caminos de error, con punteros compartidos por alias o dentro de un lazo.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Aborto inmediatoglibc detecta la corrupción y llama a abort() (SIGABRT).
Corrupción de heapSi no se detecta, la lista de bloques queda inconsistente.
SeguridadEl double free es una vulnerabilidad clásica de explotación.

Fundamento en el estándar y en la cátedra

C11 §7.22.3.3 exige que el argumento de free sea un puntero devuelto por el asignador o NULL; liberar dos veces el mismo bloque es comportamiento indefinido. La cátedra adopta la defensa de 0x3002h: Liberá siempre la memoria dinámica y asigná NULL al puntero para mitigar punteros colgantes: anular el puntero inmediatamente después de liberarlo convierte el segundo free en free(NULL), que el estándar define como no-op.

Alcance y excepciones

Aplica a free y a las funciones que liberan por dentro (destructores de TAD). No aplica a free(NULL), que es válido y puede repetirse sin consecuencias. Tampoco exime de liberar: la regla prohíbe la liberación doble, no la liberación única. Si dos módulos comparten propiedad, el contrato debe decidir cuál libera (ver 0x3006h: Documentá la propiedad de los recursos al utilizar punteros).

Ejemplos exhaustivos

❌ Contraejemplo 1 — Doble free consecutivo

char *mensaje = malloc(64);
if (mensaje == NULL)
{
    return -1;
}
copiar(mensaje, "hola");
free(mensaje);
free(mensaje);

Por qué falla: el segundo free procesa un bloque ya devuelto. glibc aborta de inmediato con double free or corruption. Anular la variable después del primer free habría hecho que el segundo sea inofensivo.

❌ Contraejemplo 2 — Doble free por alias

nodo_t *nodo = lista_extraer(lista);
nodo_t *copia = nodo;
procesar(copia);
free(nodo);
free(copia);

Por qué falla: nodo y copia apuntan al mismo bloque. Liberar con uno deja al otro colgando, y el segundo free reincide sobre el mismo bloque. La anulación de un alias no anula el otro: hay que anular todos o liberar una sola vez.

✅ Ejemplo conforme 1 — Liberar y anular

char *mensaje = malloc(64);
if (mensaje == NULL)
{
    return -1;
}
copiar(mensaje, "hola");
free(mensaje);
mensaje = NULL;

Tras la anulación, un free(mensaje) accidental sería free(NULL) y no haría nada. El patrón es la defensa directa contra el doble free y coincide con 0x3002h: Liberá siempre la memoria dinámica y asigná NULL al puntero para mitigar punteros colgantes.

✅ Ejemplo conforme 2 — Un solo dueño en el camino de error

resultado_t *crear_resultado(void)
{
    char *nombre = malloc(64);
    resultado_t *resultado = malloc(sizeof(*resultado));
    if (nombre == NULL || resultado == NULL)
    {
        free(nombre);
        free(resultado);
        return NULL;
    }
    resultado->nombre = nombre;
    return resultado;
}

Cada bloque se libera una sola vez, aun en el camino de error. free(NULL) es seguro, de modo que si sólo una de las reservas falló, la otra igual se libera. La propiedad queda clara y no hay liberación repetida.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cDos free sobre el mismo puntero sin anulación intermedia.
gcc / clanggcc -Wall -Wextra -std=c11 -fanalyzer ...double-'free' en el análisis de caminos.
valgrindvalgrind ./programaInvalid free() / delete / delete[] y double free.

Checklist de autocontrol

Reglas relacionadas