内核将内存归零?

时间:2011-05-14 21:13:30

标签: c memory-management linux-kernel

我正在使用Debian挤压并注意到内存始终为零。这是Linux发行版中的新功能吗?前段时间,我相信我可以使用puts()并输出垃圾。

我多次运行此测试程序,但评论结果始终相同。 (我在sysctl.conf中有randomize_va_space = 2,所以我知道每次运行时都会使用不同位置的内存。)


char *a = malloc(50000000);
a[49999999] = '\0';
puts(a); // it outputs nothing since all are zeroes
printf("%p\n", a);
if(a[5000] == '\0') // this condition is always true
{
    puts("It is a nul char.");
}

是否可以使系统内存不为零?这个Debian挤压安装有哪些选项可以激活始终为零内存?

6 个答案:

答案 0 :(得分:18)

在任何现代操作系统上,新获取的内存中包含非零值的唯一方法是先前由程序释放的内存被malloc重用。当从操作系统(内核)获得新内存时,它最初是纯虚拟。它没有物质存在;相反,它被映射为单个共享内存页面的写时复制映射,其中包含0个字节。第一次尝试写入时,内核将捕获写入,分配新的物理内存页,将原始页面的内容(在本例中为0字节)复制到新页面,然后恢复你的计划。如果内核知道新分配的物理内存已经零填充,它甚至可以优化复制步骤。

此程序既必要又有效。这是必要的,因为将可能包含来自内核或其他用户进程的私有数据的内存移交给您的进程将是一个严重的安全漏洞。这是有效的,因为在分配时不执行归零; “零填充”页面只是对共享零页面的引用。

答案 1 :(得分:7)

根据我在Linux内核开发中的内容,内核执行零页面,因为它可能包含用户程序可以解释的内核数据,并且某种方式可以访问系统。

malloc要求内核提供更多页面,因此内核负责您正在接收的内存。

答案 2 :(得分:3)

第一次使用块内存时,它很可能会为零,因为系统调用(sbrk,mmap)分配的内存被内核清零。但如果你再次释放和malloc,内存将被回收,并且可能不包含零。

答案 3 :(得分:1)

您的代码不会测试所有内存是否归零 - 它会测试两个特定字节是否为零 - a [0]和[5000]。此外,malloc()与内核无关 - 它是一个C库函数,而不是系统调用。它的实现者极不可能零内存 - 你所看到的只是你的特定配置的一些随机怪癖。

答案 4 :(得分:1)

您会发现在大多数进程间隔离的操作系统上都会占用内存。原因是不允许进程查看另一个进程释放的内存,因此必须在某个进程释放的时间和另一个进程释放的时间之间擦除内存页面。在实践中,擦除意味着归零,并且存储器通常在进程分配时归零。

当您在玩具程序中调用malloc时,内存尚未用于其他任何内容。所以内核中仍然是新鲜的,充满了零。如果你尝试一个已经分配并释放了大量堆块的实际程序,你会发现你的进程已经使用的内存仍然包含你(或内存管理系统)的垃圾可能已经放在那里。

答案 5 :(得分:1)

如前所述,关键区别在于首次分配分配。如果您尝试:

char *a, tst;
do {
    a = malloc(50000000);
    a[49999999] = '\0';
    printf("%50s\n%p", a, a); // it outputs nothing 1st, but bbbb.... 2nd
    tst = a[5000]
    memset(a, 'b', 50000000);
    free(a);
} while (tst == '\0');

它会打印两行(最有可能的是,至少如果指针是相同的)。

关键是malloc()返回的内存块具有未定义的内容。它可能是也可能不是零,并且取决于程序过去如何完成内存分配(或者使用了什么内存调试工具)。

如果您想保证内容,则需要calloc()或分配后进行显式初始化。

另一方面,系统的完整性/数据分离保证意味着系统请求的任何初始地址空间 - 无论是通过sbrk()还是mmap(MAP_ANON) - 必须为零 - 初始化,因为任何其他内容将包含安全漏洞。