Le stockage renvoyé par malloc() n'est pas initialisé à zéro. Ne présumez jamais que c'est le cas.
Dans votre programme de test, c'est juste un coup de chance :je suppose que le malloc()
vient de recevoir un nouveau bloc de mmap()
, mais ne vous y fiez pas non plus.
Par exemple, si j'exécute votre programme sur ma machine de cette façon :
$ 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
Votre deuxième exemple expose simplement un artefact du malloc
implémentation dans la glibc ; si vous faites cela, répétez malloc
/free
avec un tampon supérieur à 8 octets, vous verrez clairement que seuls les 8 premiers octets sont mis à zéro, comme dans l'exemple de code suivant.
#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;
}
Sortie :
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
Quelle que soit la façon dont la pile est initialisée, vous ne voyez pas une pile vierge, car la bibliothèque C fait un certain nombre de choses avant d'appeler main
, et ils touchent la pile.
Avec la bibliothèque GNU C, sur x86-64, l'exécution commence au point d'entrée _start, qui appelle __libc_start_main
pour mettre les choses en place, et ce dernier finit par appeler main
. Mais avant d'appeler le main
, il appelle un certain nombre d'autres fonctions, ce qui entraîne l'écriture de divers éléments de données dans la pile. Le contenu de la pile n'est pas effacé entre les appels de fonction, donc lorsque vous entrez dans main
, votre pile contient les restes des appels de fonction précédents.
Cela n'explique que les résultats que vous obtenez de la pile, voir les autres réponses concernant votre approche générale et vos hypothèses.
Dans les deux cas, vous obtenez non initialisé mémoire, et vous ne pouvez faire aucune hypothèse sur son contenu.
Lorsque le système d'exploitation doit attribuer une nouvelle page à votre processus (que ce soit pour sa pile ou pour l'arène utilisée par malloc()
), il garantit qu'il n'exposera pas les données d'autres processus ; la façon habituelle de s'en assurer est de le remplir avec des zéros (mais il est également valable de remplacer par autre chose, y compris même une page valant /dev/urandom
- en fait un débogage malloc()
les implémentations écrivent des modèles non nuls, pour détecter des hypothèses erronées telles que les vôtres).
Si malloc()
peut satisfaire la demande de la mémoire déjà utilisée et libérée par ce processus, son contenu ne sera pas effacé (en fait, l'effacement n'a rien à voir avec malloc()
et cela ne peut pas être le cas - cela doit se produire avant que la mémoire ne soit mappée dans votre espace d'adressage). Vous pouvez obtenir de la mémoire qui a déjà été écrite par votre processus/programme (par exemple avant main()
).
Dans votre exemple de programme, vous voyez un malloc()
région qui n'a pas encore été écrite par ce processus (c'est-à-dire qu'elle provient directement d'une nouvelle page) et une pile qui a été écrite (par pré-main()
code dans votre programme). Si vous examinez davantage la pile, vous constaterez qu'elle est remplie de zéros plus bas (dans sa direction de croissance).
Si vous voulez vraiment comprendre ce qui se passe au niveau du système d'exploitation, je vous recommande de contourner la couche de la bibliothèque C et d'interagir à l'aide d'appels système tels que brk()
et mmap()
à la place.