Sul mio sistema Debian GNU / Linux 9, quando viene eseguito un binario,
- lo stack non è inizializzato ma
- l'heap è inizializzato a zero.
Perché?
Presumo che l'inizializzazione zero promuova la sicurezza ma, se per l'heap, perché non anche per lo stack? Anche lo stack non ha bisogno di sicurezza?
La mia domanda non è specifica per Debian per quanto ne so.
Codice C di esempio:
#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;
}
Produzione:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
Lo standard C non richiede malloc()
di cancellare la memoria prima di allocarla, ovviamente, ma il mio programma C è solo a scopo illustrativo. La domanda non è una domanda su C o sulla libreria standard di C. Piuttosto, la domanda è una domanda sul perché il kernel e / o il caricatore di runtime stanno azzerando l'heap ma non lo stack.
UN ALTRO ESPERIMENTO
La mia domanda riguarda il comportamento osservabile di GNU / Linux piuttosto che i requisiti dei documenti standard. Se non sei sicuro di cosa intendo, prova questo codice, che invoca ulteriori comportamenti indefiniti ( indefiniti, vale a dire, per quanto riguarda lo standard C) per illustrare il 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;
}
Uscita dalla mia macchina:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
Per quanto riguarda lo standard C, il comportamento non è definito, quindi la mia domanda non riguarda lo standard C. Una chiamata malloc()
non deve necessariamente restituire lo stesso indirizzo ogni volta ma, poiché questa chiamata a malloc()
restituisce effettivamente lo stesso indirizzo ogni volta, è interessante notare che la memoria, che si trova nell'heap, viene azzerata ogni volta.
La pila, al contrario, non sembrava essere stata azzerata.
Non so che cosa farà l'ultimo codice sulla tua macchina, dal momento che non so quale strato del sistema GNU / Linux sta causando il comportamento osservato. Puoi solo provarlo.
AGGIORNARE
@Kusalananda ha osservato nei commenti:
Per quello che vale, il tuo codice più recente restituisce diversi indirizzi e dati (occasionali) non inizializzati (diversi da zero) quando eseguito su OpenBSD. Questo ovviamente non dice nulla sul comportamento a cui stai assistendo su Linux.
Che il mio risultato differisca da quello su OpenBSD è davvero interessante. Apparentemente, i miei esperimenti stavano scoprendo non un protocollo di sicurezza del kernel (o linker), come avevo pensato, ma un semplice artefatto implementativo.
In questa luce, credo che, insieme, le risposte di seguito di @mosvy, @StephenKitt e @AndreasGrapentin risolvano la mia domanda.
Vedi anche su Stack Overflow: Perché malloc inizializza i valori su 0 in gcc? (credito: @bta).
new
operatore in C ++ (anche "heap") è su Linux solo un wrapper per malloc (); il kernel non sa né importa quale sia il "mucchio".