C – ¿Acceder a los datos DESPUÉS de que la memoria haya sido liberada ()?

6 minutos de lectura

Estoy leyendo mucho sobre malloc() y free() en Standard C. Según tengo entendido, usted malloc() por algún recuerdo exactamente una vez y luego tú free() ese mismo recuerdo exactamente una vez. Puede ser una mala práctica, pero entiendo que después de ti malloc() memoria, puede definir múltiples punteros a ella. Y una vez que free() cualquiera de esos punteros, la memoria asignada está desasignada?

Considere este ejemplo de juguete:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){

    char* p = (char*)malloc(10 * sizeof(char));     // allocate memory
    int* q = (int*)p;                               // pointer to the same block of memory
    *p = 'A';                                       // Input some data
    printf("TEST::  %c %d\n", *p, *q);              // Everything's ok so far...
    free(p);                                        // free() my allocated memory?
    sleep(10);                                      // wait
    printf("%c\n", *q);                             // q now points to de-allocated memory
                                                    // shouldn't this segfault?

    free(q);                                        // *** SEGFAULTS HERE ***

    return 0;
}

La salida es:

[Linux]$ ./a.out
TEST::  A 65

*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001ac4010 ***
======= Backtrace: =========
...lots of backtrack info...

Así que asumo que cuando yo free() el primer puntero, la memoria se considera free()ed, pero el valores de datos Escribí en este bloque de memoria que todavía están “allí”, por lo que puedo acceder a ellos a través del segundo puntero.

(No estoy proponiendo que esta sea una buena idea, estoy tratando de entender la lógica del sistema).

  • eso se llama “Comportamiento indefinido”. A veces parece funcionar. Otras veces las cosas van mal. Pero, no, no debe acceder a la memoria después de free() eso.

    – Johnny Mopp

    3 de marzo de 2017 a las 21:19


  • No hay una falla de segmento en su código en este momento. El mensaje de error es de su biblioteca C que detecta estructuras internas corruptas.

    – melpomene

    3 de marzo de 2017 a las 21:23


  • Incluso el utilizar del propio puntero después free tiene un comportamiento indefinido, es decir, es posible que ni siquiera lo compares con NULL.

    – Antti Haapala — Слава Україні

    3 de marzo de 2017 a las 21:39


  • por cierto, el tamaño de char es 1, porque sizeof devuelve el tamaño del tipo dado en caracteres.

    – Antti Haapala — Слава Україні

    03/03/2017 a las 21:40


  • Antes de hacer esta pregunta, debería haber escrito ‘acceder a los datos después de gratis’ tanto en Google como en SO. Si no hubieras tenido que hacer la pregunta aquí.

    – KevinDTimm

    3 de marzo de 2017 a las 22:03

Cuando malloc la memoria, se le da un puntero a algún espacio, y cuando lo libera, lo devuelve al sistema. A menudo, aún puede acceder a esta memoria, pero usar la memoria después de haberla liberado es MUY MALO.

El comportamiento exacto no está definido, pero en la mayoría de los sistemas puede continuar accediendo a la memoria o obtiene un error de segmento.

Un experimento interesante que puede probar es tratar de almacenar más memoria después de liberar ese puntero. En la mayoría de los sistemas que he probado, recuperas el mismo bloque (lo cual es un problema, si confiabas en que los datos estaban allí en el bloque liberado). Su programa terminaría usando ambos punteros, pero dado que apuntan a los mismos datos físicos, ¡estará sobrescribiendo sus propios datos!

La razón de esto es que cuando malloc datos (dependiendo de la implementación de malloc, por supuesto), malloc primero solicita un bloque de datos del sistema operativo (generalmente mucho más grande que la solicitud de malloc), y malloc le dará un segmento de eso memoria. Sin embargo, podrá acceder a cualquier parte de la memoria que malloc obtuvo originalmente del sistema operativo, ya que para el sistema operativo, es toda la memoria que su programa usa internamente. Cuando liberas, le estás diciendo al sistema malloc que la memoria está libre y que se puede devolver al programa más adelante.

Escribir fuera del área malloc es muy peligroso porque

  1. Puede fallar, dependiendo de su implementación de c
  2. Puede sobrescribir las estructuras de metadatos en las que se basa malloc, lo que causa MUY MALOS PROBLEMAS cuando libera/malloca más datos más adelante

Si está interesado en obtener más información, le recomendaría ejecutar su programa a través de valgrind, un detector de fugas, para obtener una mejor idea de lo que se libera y lo que no se libera.

PD: En los sistemas sin un sistema operativo, lo más probable es que no obtenga un error de segmento en absoluto, y podrá escribir en cualquier lugar de cualquier manera. El sistema operativo es responsable de desencadenar una falla de segmento (cuando escribe/lee en la memoria a la que no tiene acceso, como kernel o memoria protegida)

Si está interesado en obtener más información, debe intentar escribir su propio malloc y/o leer/aprender acerca de los sistemas operativos de administración de memoria.

  • También es posible que algunos sistemas operativos seguros pongan a cero la memoria que se liberó. Por si acaso.

    – aragaer

    3 de marzo de 2017 a las 21:59

avatar de usuario
Antti Haapala — Слава Україні

El bloqueo en su código se debe al doble free. Apéndice J.2 de C11 dice que el comportamiento no está definido, por ejemplo, cuando:

El argumento de puntero a la función free o realloc no coincide con un puntero devuelto anteriormente por una función de administración de memoria, o el espacio ha sido desasignado por una llamada a free o realloc (7.22.3.3, 7.22.3.5).

Sin embargo, es posible escribir código que se bloquee en Linux simplemente leyendo un valor de la memoria que se acaba de liberar.

En glibc + Linux existen dos mecanismos diferentes de asignación de memoria. Uno usa el brk/sbrk para cambiar el tamaño del segmento de datos, y el otro usa el mmap llamada al sistema para pedirle al sistema operativo que proporcione grandes cantidades de memoria. El primero se usa para asignaciones pequeñas, como los 10 caracteres anteriores, y mmap para trozos grandes. Por lo tanto, es posible que se bloquee incluso al acceder a la memoria justo después de liberarla:

#include <stdio.h>
#include <stdlib.h>

int main(){
    char* p = malloc(1024 * 1024);
    printf("%d\n", *p);
    free(p);
    printf("%d\n", *p);
}

Y finalmente, el estándar C11 dice que el comportamiento no está definido incluso cuando

Se utiliza el valor de un puntero que hace referencia al espacio desasignado por una llamada a la función free o realloc (7.22.3).

Esto significa que después de no sólo eso desreferenciando el puntero (*p) posee comportamiento indefinidopero también que no es seguro utilizar el puntero de cualquier otra forma, incluso haciendo p == NULL posee UB. Esto se sigue de C11 6.2.4p2 que dice:

El valor de un puntero se vuelve indeterminado cuando el objeto al que apunta (o simplemente pasado) llega al final de su vida útil.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad