malloc是确定性的吗?

时间:2011-11-17 16:48:24

标签: c linux gcc x86 malloc

malloc 是否具有确定性?说如果我有一个分叉进程,也就是另一个进程的副本,并且在某些时候它们都调用 malloc 函数。两个进程中分配的地址是否相同?假设执行的其他部分也是确定性的。

注意:这里,我只讨论虚拟内存,而不是物理内存。

7 个答案:

答案 0 :(得分:27)

根本没有理由确定它是确定性的,事实上它不具有确定性可能有一些好处,例如increasing the complexity of exploiting bugs(另见this paper)。

这种随机性有助于使得难以编写的漏洞利用。要成功利用缓冲区溢出,通常需要做两件事:

  1. 将有效负载交付到可预测/已知内存位置
  2. 导致执行跳转到该位置
  3. 如果内存位置不可预测,那么跳转会变得非常困难。

    标准§7.20.3.3/ 2的相关引用:

      

    malloc函数为大小为的对象分配空间   由size指定,其值是不确定的

    如果打算将其确定为确定性,则可以明确说明。

    即使它今天看起来确定无疑,我也不会打赌它更新的内核或更新的libc / GCC版本。

答案 1 :(得分:11)

C99规范(至少在其final public draft中)陈述了J.1未指明的行为':

  

以下未指定:   ...   连续调用分配的存储顺序和连续性   calloc,malloc和realloc函数(7.20.3)。

所以看起来malloc并不一定是确定性的。因此,假设它是安全的,这是不安全的。

答案 2 :(得分:7)

这完全取决于malloc实施。特定malloc实现会引入非确定性(除了可能作为应用程序模糊测试之外,没有固有的原因,但即使这样,它也应该默认禁用)。例如,Doug Lea's malloc不使用rand(3)或其中的任何类似方法。

但是,由于malloc在Linux上调用内核(例如sbrk(2)mmap(2)或在Windows上调用VirtualAlloc),因此这些系统调用可能并不总是确定性的,甚至在其他相同的过程中。无论出于何种原因,内核可能会决定在不同的进程中故意提供不同的mmap'ed地址。

因此,对于通常在没有系统调用的情况下在用户空间中进行服务的小分配,很可能在fork()之后生成的指针将是相同的;由系统服务的大型分配可以是相同的。

一般而言,不要依赖它。如果你真的需要在不同的进程中使用相同的指针,要么在分叉之前创建它们,要么使用共享内存并适当地共享它们。

答案 3 :(得分:2)

这取决于malloc的详细实施。典型的malloc实现(例如,dlmalloc)曾经是确定性的。这只是因为算法本身是确定性的。

然而,由于许多安全攻击,例如堆溢出攻击,malloc,即堆管理器,在其实现中引入了一些随机性。 (但是,它的熵相对较小,因为堆管理器必须考虑速度和空间)因此,在堆管理器中不应该假设严格的确定性是安全的。

此外,当你分叉一个过程时,有各种随机性来源,包括ASLR

答案 4 :(得分:2)

是的,它在某种程度上是确定性的,但并不一定意味着它会在一个过程的两个分支中给出相同的结果。

例如,单Unix规范说:“为了避免错误,子进程可能只执行异步信号安全操作,直到调用其中一个exec函数为止。”

无论好坏,malloc在“异步信号安全”功能列表中

此限制在讨论多线程程序的部分中,但未指定该限制是仅适用于多线程程序,还是也适用于单线程程序。

结论:您不能指望malloc在父母和孩子身上产生相同的结果。如果程序是多线程的,那么你不能指望malloc在孩子身上工作,直到它调用exec - 并且还有合理的问题的空间,即使它确实在孩子呼叫exec之前的单线程孩子。

参考文献:

  1. fork specification
  2. async-signal safe functions

答案 5 :(得分:0)

您将无法获得相同的实际地址。如果你有进程A和B,则每次调用malloc都会返回一个空闲块的地址。 A和B调用malloc的顺序是不可预测的。但它永远不会“在同一时刻”发生。

答案 6 :(得分:-1)

从技术上讲,如果分叉进程都请求相同大小的块,它们应该分配相同的地址,但每个地址都指向不同的物理/实际内存位置。

Linux使用copy-on-write for fork,因此分叉的子级共享其父级内存,直到任一进程中的某些内容发生更改。此时,内核通过内存复制序列为分叉子项提供内存空间的专用/唯一副本。