在我的Debian GNU / Linux 9系统上,执行二进制文件时,
- 堆栈未初始化,但是
- 堆是零初始化的。
为什么?
我认为零初始化可以提高安全性,但是,如果对于堆,为什么不对堆栈也这样做呢?堆栈也不需要安全性吗?
据我所知,我的问题并非专门针对Debian。
示例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;
}
输出:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
C标准当然不要求malloc()
在分配内存之前先清除内存,但是我的C程序仅用于说明。问题不是关于C或关于C的标准库的问题。相反,问题是关于为什么内核和/或运行时加载程序将堆归零而不是栈归零的问题。
另一项实验
我的问题是关于可观察的GNU / Linux行为,而不是标准文件的要求。如果不确定我的意思,请尝试以下代码,该代码调用更多未定义的行为(未定义,即就C标准而言)以说明这一点:
#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;
}
我机器的输出:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
就C标准而言,行为是不确定的,因此我的问题不涉及C标准。调用malloc()
不必每次都返回相同的地址,但是,由于该调用malloc()
确实确实每次都返回相同的地址,因此有趣的是注意到堆上的内存每次都归零。
相比之下,堆栈似乎没有被清零。
我不知道后面的代码将在您的计算机上做什么,因为我不知道GNU / Linux系统的哪一层导致了所观察到的行为。您可以尝试一下。
更新
@Kusalananda在评论中观察到:
不管怎样,当您在OpenBSD上运行时,最新的代码将返回不同的地址和(偶尔的)未初始化的(非零)数据。显然,这并没有说明您在Linux上看到的行为。
我的结果与OpenBSD的结果不同确实很有趣。显然,我的实验不是像我想的那样发现内核(或链接器)安全协议,而只是发现实现工件。
因此,我相信@ mosvy,@ StephenKitt和@AndreasGrapentin的以下答案可以解决我的问题。
另请参见堆栈溢出:为什么malloc在gcc中将值初始化为0?(信用:@bta)。
new
C ++(也称为“堆”)中的运算符在Linux上只是malloc()的包装;内核既不知道也不在乎“堆”是什么。