2011-05-14 86 views
8

我正在使用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挤压安装有什么选择可以激活这个始终为零的内存?

回答

1

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

7

从我在Linux Kernel Development中读到的内容来看,内核做零页面是因为它可能包含用户程序可以解释的内核数据并以某种方式访问​​系统。

malloc向内核请求更多页面,所以内核负责你正在接收的内存。

+0

根据这个WP页面上的brk/sbrk函数:http://en.wikipedia.org/wiki/Sbrk你是对的。但是这对于内核来说似乎是一件非常浪费的事情。 – 2011-05-14 21:29:57

+2

为什么?这对于一个程序来说似乎是一件聪明的事情。如果你有非常愚蠢的程序来保存未加密的愚蠢数据,然后在没有免费()的情况下死掉,你可能会编写一个程序来利用它。我很肯定你可以在编译内核时禁用它。 – 2011-05-14 23:12:11

+0

“禁用它”?绝对没有办法通过普通选项向内核泄露数据给用户空间。你不得不故意破坏它来做到这一点。由于新页面是COW引用零页面的事实,因此不存在会泄漏的“缺省情况”。 – 2011-05-15 03:43:09

3

你第一次malloc一个块内存有一个公平的机会它将为零,因为系统调用(sbrk,mmap)分配的内存由内核归零。但是,如果你再次释放并再次使用malloc,则内存将被回收并可能不包含零。

1

在大多数进程之间有隔离的操作系统上,您会发现内存已被调整。原因在于不能允许进程偷看另一个进程释放的内存,因此内存页必须在某个进程释放的时间与其他进程释放的时间之间被擦除。在实践中,擦除意味着归零,并且内存在进程分配时通常为零。

当你在你的玩具程序中调用malloc时,内存还没有用于其他任何事情。所以它在内核中仍然是新鲜的,充满了零。如果你尝试了一个已经分配并释放了很多堆块的真正程序,你会发现你的进程已经使用了的内存仍然包含你(或者内存管理系统)可能存在的任何垃圾。

16

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

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

+0

Windows中有一个线程,其任务是将未使用的物理页面清零,以提供可安全映射到用户空间的新页面池。 (通过比较,允许内核为其自己使用分配未定位页面)。 – Neil 2011-05-14 22:06:43

+0

然而,内核开发者仍然必须确保其“未定界”页面的内存中的数据不会泄漏到任何用户模式进程中。 此外,考虑到内存在后台调​​零,除非有明显的内存流失,否则对系统的影响最小。但是,无论是否进行调零,通过记忆搅动都可能是性能问题。 – Brian 2011-05-14 22:43:17

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) - 必须是零初始化,作为此类的任何其它内容将包括一个安全漏洞。