GNU/Linux >> Tutoriales Linux >  >> Linux

Si el montón se inicializa en cero por seguridad, ¿por qué simplemente no se inicializa la pila?

El almacenamiento devuelto por malloc() es no inicializado en cero. Nunca asumas que lo es.

En su programa de prueba, es solo una casualidad:supongo que el malloc() acabo de recibir un bloque nuevo de mmap() , pero tampoco confíes en eso.

Por ejemplo, si ejecuto su programa en mi máquina de esta manera:

$ echo 'void __attribute__((constructor)) p(void){
    void *b = malloc(4444); memset(b, 4, 4444); free(b);
}' | cc -include stdlib.h -include string.h -xc - -shared -o pollute.so

$ LD_PRELOAD=./pollute.so ./your_program
a at 0x7ffd40d3aa60: 1256994848 21891 1256994464 21891 1087613792 32765 0 0
b at 0x55834c75d010: 67372036 67372036 67372036 67372036 67372036 67372036 67372036 67372036

Su segundo ejemplo es simplemente exponer un artefacto del malloc implementación en glibc; si haces eso repite malloc /free con un búfer de más de 8 bytes, verá claramente que solo los primeros 8 bytes se ponen a cero, como en el siguiente código de ejemplo.

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

const size_t n = 4;
const size_t m = 0x10;

int main()
{
    for (size_t i = n; i; --i) {
        int *const p = malloc(m*sizeof(int));
        printf("%p ", p);
        for (size_t j = 0; j < m; ++j) {
            printf("%d:", p[j]);
            ++p[j];
            printf("%d ", p[j]);
        }
        free(p);
        printf("\n");
    }
    return 0;
}

Salida:

0x55be12864010 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 
0x55be12864010 0:1 0:1 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 
0x55be12864010 0:1 0:1 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 
0x55be12864010 0:1 0:1 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4

Independientemente de cómo se inicialice la pila, no está viendo una pila prístina, porque la biblioteca C hace una serie de cosas antes de llamar a main y tocan la pila.

Con la biblioteca GNU C, en x86-64, la ejecución comienza en el punto de entrada _start, que llama a __libc_start_main para configurar las cosas, y este último termina llamando a main . Pero antes de llamar al main , llama a otras funciones, lo que hace que se escriban varios datos en la pila. El contenido de la pila no se borra entre las llamadas a funciones, por lo que cuando ingresa a main , su pila contiene restos de las llamadas a funciones anteriores.

Esto solo explica los resultados que obtiene de la pila, vea las otras respuestas con respecto a su enfoque general y suposiciones.


En ambos casos, se obtiene sin inicializar memoria, y no puede hacer suposiciones sobre su contenido.

Cuando el sistema operativo tiene que asignar una nueva página a su proceso (ya sea para su pila o para la arena utilizada por malloc() ), garantiza que no expondrá datos de otros procesos; la forma habitual de asegurarse de eso es llenarlo con ceros (pero es igualmente válido sobrescribir con cualquier otra cosa, incluida incluso una página con un valor de /dev/urandom - de hecho, algo de depuración malloc() implementaciones escriben patrones distintos de cero, para detectar suposiciones erróneas como la suya).

Si malloc() puede satisfacer la solicitud de la memoria ya utilizada y liberada por este proceso, su contenido no se borrará (de hecho, la limpieza no tiene nada que ver con malloc() y no puede ser, tiene que suceder antes de que la memoria se asigne a su espacio de direcciones). Puede obtener memoria que haya sido escrita previamente por su proceso/programa (por ejemplo, antes de main() ).

En su programa de ejemplo, está viendo un malloc() región que aún no ha sido escrita por este proceso (es decir, es directamente desde una nueva página) y una pila en la que ha sido escrita (por pre-main() código en su programa). Si examina más de la pila, encontrará que está lleno de ceros más abajo (en su dirección de crecimiento).

Si realmente desea comprender lo que sucede a nivel del sistema operativo, le recomiendo que omita la capa de la Biblioteca C e interactúe mediante llamadas al sistema como brk() y mmap() en su lugar.


Linux
  1. ¿Por qué el servidor bloqueó mi IP?

  2. ¿Grep para cadenas múltiples en archivos y luego listar los archivos en orden de tamaño?

  3. ¿Hay alguna herramienta CLI para dibujar gráficos en la pantalla durante una sesión X?

  4. ¿Por qué el comando free y dmidecode muestran valores diferentes para la RAM?

  5. ¿Por qué el montaje no respeta la opción de solo lectura para los montajes de enlace?

Por qué usar el escritorio Pantheon para Linux Elementary OS

Por qué todavía me encanta Alpine para el correo electrónico en la terminal de Linux

Slack para la CLI – Slack

Los 8 mejores teléfonos Linux seguros para privacidad y seguridad

Por qué en Linux moderno, el tamaño de pila predeterminado es tan grande:8 MB (incluso 10 en algunas distribuciones)

¿Por qué ejecutar named(bind) en chroot es tan importante para la seguridad? ¿O tal vez no lo es?