No meu sistema Debian GNU/Linux 9, quando um binário é executado,
- a pilha não é inicializada, mas
- o heap é inicializado com zero.
Por quê?
Presumo que a inicialização zero promova a segurança, mas, se para o heap, por que não também para a pilha? A pilha também não precisa de segurança?
Minha pergunta não é específica para o Debian até onde eu sei.
Exemplo de código 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;
}
Resultado:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
O padrão C não pede malloc()
para limpar a memória antes de alocá-la, é claro, mas meu programa C é meramente ilustrativo. A pergunta não é sobre C ou sobre a biblioteca padrão de C. Em vez disso, a pergunta é sobre por que o kernel e/ou o carregador de tempo de execução estão zerando o heap, mas não a pilha.
OUTRO EXPERIMENTO
Minha pergunta diz respeito ao comportamento GNU/Linux observável em vez dos requisitos dos documentos de padrões. Se não tiver certeza do que quero dizer, tente este código, que invoca mais comportamento indefinido ( indefinido, ou seja, no que diz respeito ao padrão C) para ilustrar o ponto:
#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;
}
Saída da minha máquina:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
No que diz respeito ao padrão C, o comportamento é indefinido, então minha pergunta não diz respeito ao padrão C. Uma chamada para malloc()
não precisa retornar o mesmo endereço todas as vezes, mas, como essa chamada para de malloc()
fato retorna o mesmo endereço todas as vezes, é interessante notar que a memória, que está no heap, é zerada a cada vez.
A pilha, por outro lado, não parecia estar zerada.
Não sei o que o último código fará em sua máquina, pois não sei qual camada do sistema GNU/Linux está causando o comportamento observado. Você pode, mas tente.
ATUALIZAR
@Kusalananda observou nos comentários:
Para o que vale a pena, seu código mais recente retorna diferentes endereços e (ocasionalmente) dados não inicializados (diferentes de zero) quando executados no OpenBSD. Isso obviamente não diz nada sobre o comportamento que você está testemunhando no Linux.
Que meu resultado difere do resultado no OpenBSD é realmente interessante. Aparentemente, meus experimentos estavam descobrindo não um protocolo de segurança do kernel (ou linker), como eu pensava, mas um mero artefato de implementação.
Diante disso, acredito que, juntas, as respostas abaixo de @mosvy, @StephenKitt e @AndreasGrapentin resolvem minha dúvida.
Veja também no Stack Overflow: Por que o malloc inicializa os valores como 0 no gcc? (crédito: @bta).