No meu sistema Debian GNU / Linux 9, quando um binário é executado,
- a pilha não foi inicializada, mas
- o heap é inicializado com zero.
Por quê?
Suponho que a inicialização zero promova segurança, mas, se for 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, tanto quanto eu sei.
Código C de exemplo:
#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 questão 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 a pilha, mas não a pilha.
OUTRA EXPERIÊNCIA
Minha pergunta diz respeito ao comportamento observável do GNU / Linux, e não aos requisitos dos documentos de padrões. Se não souber o que quero dizer, tente este código, que invoca um comportamento indefinido adicional ( indefinido, isto é, 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, portanto minha pergunta não considera o padrão C. Uma chamada para malloc()
não precisar retornar o mesmo endereço todas as vezes, mas, como essa chamada malloc()
realmente retorna o mesmo endereço toda vez, é interessante notar que a memória, que está na pilha, é zerada toda vez.
A pilha, por outro lado, não parecia 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 apenas tentar.
ATUALIZAR
@Kusalananda observou nos comentários:
Pelo que vale a pena, seu código mais recente retorna endereços diferentes e dados (não ocasionais) não inicializados (diferentes de zero) quando executados no OpenBSD. Obviamente, isso não diz nada sobre o comportamento que você está testemunhando no Linux.
Que meu resultado seja diferente do resultado no OpenBSD é realmente interessante. Aparentemente, meus experimentos estavam descobrindo não um protocolo de segurança do kernel (ou vinculador), como eu pensava, mas um mero artefato de implementação.
Nesta perspectiva, acredito que, juntas, as respostas abaixo de @mosvy, @StephenKitt e @AndreasGrapentin resolvam minha pergunta.
Veja também no Stack Overflow: Por que o malloc inicializa os valores para 0 em gcc? (crédito: @bta).
new
operador em C ++ (também "heap") está no Linux apenas um invólucro para malloc (); o kernel não sabe nem se importa com o que é o "heap".