Sur mon système Debian GNU / Linux 9, lorsqu'un binaire est exécuté,
- la pile n'est pas initialisée mais
- le tas est initialisé à zéro.
Pourquoi?
Je suppose que l'initialisation à zéro favorise la sécurité mais, si pour le tas, pourquoi pas aussi pour la pile? La pile n'a-t-elle pas non plus besoin de sécurité?
Pour autant que je sache, ma question n'est pas spécifique à Debian.
Exemple de code C:
#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;
}
Production:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
La norme C ne demande pas malloc()
d'effacer la mémoire avant de l'allouer, bien sûr, mais mon programme C est simplement à titre d'illustration. La question n'est pas une question sur C ou sur la bibliothèque standard de C. Au contraire, la question est de savoir pourquoi le noyau et / ou le chargeur d'exécution mettent à zéro le tas mais pas la pile.
UNE AUTRE EXPÉRIENCE
Ma question concerne le comportement GNU / Linux observable plutôt que les exigences des documents de normes. Si vous ne savez pas ce que je veux dire, essayez ce code, qui invoque d'autres comportements non définis ( non définis, c'est-à-dire en ce qui concerne la norme C) pour illustrer le point:
#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;
}
Sortie de ma machine:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
En ce qui concerne la norme C, le comportement n'est pas défini, donc ma question ne concerne pas la norme C. Un appel à malloc()
ne pas avoir besoin de renvoyer la même adresse à chaque fois mais, puisque cet appel à malloc()
arrive effectivement à renvoyer la même adresse à chaque fois, il est intéressant de remarquer que la mémoire, qui est sur le tas, est mise à zéro à chaque fois.
La pile, en revanche, ne semblait pas être mise à zéro.
Je ne sais pas ce que ce dernier code fera sur votre machine, car je ne sais pas quelle couche du système GNU / Linux est à l'origine du comportement observé. Vous ne pouvez que l'essayer.
MISE À JOUR
@Kusalananda a observé dans les commentaires:
Pour ce qu'il vaut, votre code le plus récent renvoie différentes adresses et données (occasionnelles) non initialisées (non nulles) lorsqu'il est exécuté sur OpenBSD. Cela ne dit évidemment rien sur le comportement que vous observez sous Linux.
Que mon résultat diffère du résultat sur OpenBSD est en effet intéressant. Apparemment, mes expériences ne découvraient pas un protocole de sécurité du noyau (ou éditeur de liens), comme je l'avais pensé, mais un simple artefact d'implémentation.
Dans cette optique, je pense qu'ensemble, les réponses ci-dessous de @mosvy, @StephenKitt et @AndreasGrapentin tranchent ma question.
Voir aussi sur Stack Overflow: Pourquoi malloc initialise-t-il les valeurs à 0 dans gcc? (crédit: @bta).
new
opérateur en C ++ (également "tas") est sous Linux juste un wrapper pour malloc (); le noyau ne sait ni ne se soucie de ce qu'est le "tas".