En mi sistema Debian GNU / Linux 9, cuando se ejecuta un binario,
- la pila no está inicializada pero
- el montón está inicializado en cero.
¿Por qué?
Supongo que la inicialización cero promueve la seguridad, pero, si es para el montón, ¿por qué no también para la pila? ¿La pila tampoco necesita seguridad?
Mi pregunta no es específica de Debian hasta donde yo sé.
Código C de muestra:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 8;
// --------------------------------------------------------------------
// UNINTERESTING CODE
// --------------------------------------------------------------------
static void print_array(
const int *const p, const size_t size, const char *const name
)
{
printf("%s at %p: ", name, p);
for (size_t i = 0; i < size; ++i) printf("%d ", p[i]);
printf("\n");
}
// --------------------------------------------------------------------
// INTERESTING CODE
// --------------------------------------------------------------------
int main()
{
int a[n];
int *const b = malloc(n*sizeof(int));
print_array(a, n, "a");
print_array(b, n, "b");
free(b);
return 0;
}
Salida:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
El estándar C no pide malloc()
borrar memoria antes de asignarlo, por supuesto, pero mi programa C es meramente ilustrativo. La pregunta no es una pregunta sobre C o sobre la biblioteca estándar de C. Más bien, la pregunta es por qué el kernel y / o el cargador de tiempo de ejecución están poniendo a cero el montón pero no la pila.
OTRO EXPERIMENTO
Mi pregunta se refiere al comportamiento observable de GNU / Linux en lugar de los requisitos de los documentos estándar. Si no está seguro de lo que quiero decir, intente este código, que invoca un comportamiento indefinido adicional ( indefinido, es decir, en lo que respecta al estándar C) para ilustrar el punto:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 4;
int main()
{
for (size_t i = n; i; --i) {
int *const p = malloc(sizeof(int));
printf("%p %d ", p, *p);
++*p;
printf("%d\n", *p);
free(p);
}
return 0;
}
Salida de mi máquina:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
En lo que respecta al estándar C, el comportamiento no está definido, por lo que mi pregunta no tiene en cuenta el estándar C. No es necesario que una llamada malloc()
devuelva la misma dirección cada vez, pero dado que esta llamada malloc()
sí devuelve la misma dirección cada vez, es interesante notar que la memoria, que está en el montón, se pone a cero cada vez.
La pila, por el contrario, no parecía estar puesta a cero.
No sé qué hará este último código en su máquina, ya que no sé qué capa del sistema GNU / Linux está causando el comportamiento observado. Solo puedes intentarlo.
ACTUALIZAR
@Kusalananda ha observado en comentarios:
Por lo que vale, su código más reciente devuelve diferentes direcciones y datos (no ocasionales) no inicializados (no cero) cuando se ejecuta en OpenBSD. Obviamente, esto no dice nada sobre el comportamiento que está presenciando en Linux.
Que mi resultado difiere del resultado en OpenBSD es realmente interesante. Aparentemente, mis experimentos descubrieron no un protocolo de seguridad del núcleo (o enlazador), como había pensado, sino un mero artefacto de implementación.
En este sentido, creo que, juntas, las respuestas a continuación de @mosvy, @StephenKitt y @AndreasGrapentin resuelven mi pregunta.
Ver también en Desbordamiento de pila: ¿Por qué malloc inicializa los valores a 0 en gcc? (crédito: @bta).
new
operador en C ++ (también "heap") está en Linux solo como un contenedor para malloc (); el núcleo no sabe ni le importa cuál es el "montón".