malloc 是否具有确定性?说如果我有一个分叉进程,也就是另一个进程的副本,并且在某些时候它们都调用 malloc 函数。两个进程中分配的地址是否相同?假设执行的其他部分也是确定性的。
注意:这里,我只讨论虚拟内存,而不是物理内存。
答案 0 :(得分:27)
根本没有理由确定它是确定性的,事实上它不具有确定性可能有一些好处,例如increasing the complexity of exploiting bugs(另见this paper)。
这种随机性有助于使得难以编写的漏洞利用。要成功利用缓冲区溢出,通常需要做两件事:
如果内存位置不可预测,那么跳转会变得非常困难。
标准§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
之前的单线程孩子。
参考文献:
答案 5 :(得分:0)
您将无法获得相同的实际地址。如果你有进程A和B,则每次调用malloc都会返回一个空闲块的地址。 A和B调用malloc的顺序是不可预测的。但它永远不会“在同一时刻”发生。
答案 6 :(得分:-1)
从技术上讲,如果分叉进程都请求相同大小的块,它们应该分配相同的地址,但每个地址都指向不同的物理/实际内存位置。
Linux使用copy-on-write for fork,因此分叉的子级共享其父级内存,直到任一进程中的某些内容发生更改。此时,内核通过内存复制序列为分叉子项提供内存空间的专用/唯一副本。