是malloc确定性的?

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

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

Solutions Collecting From Web of "是malloc确定性的?"

它没有任何理由是确定性的,实际上它可能会有一些好处,而不是确定性的,例如增加开发漏洞的复杂性 (另见本文 )。

这种随机性可能有助于使得难度更大。 要成功利用缓冲区溢出,通常需要做两件事:

  1. 将有效载荷传送到可预测/已知的存储位置
  2. 导致执行跳转到该位置

如果内存位置不可预测,那么跳转就会变得相当困难。

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

malloc函数为大小由大小指定且值不确定的对象分配空间

如果是为了使其具有确定性,那么就会明确地说明这一点。

即使今天看起来确定,我也不会用更新的内核或更新的libc / GCC版本来维持这种状态。

C99规范(至少在其最终的公开草案中 )在“J.1未指明行为”中声明:

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

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

这完全取决于malloc实现。 为什么一个特定的malloc实现会引入非确定性(除了可能作为应用程序的模糊测试,但即使这样,它应该被默认禁用)也没有内在的原因。 例如, Doug Lea的malloc不使用rand(3)或其他类似的方法。

但是,由于malloc在Linux或VirtualAlloc上调用内核(例如sbrk(2)mmap(2) ,这些系统调用可能并不总是确定性的,即使在其他方面相同的进程中也是如此。 内核可能决定故意在不同的进程中出于任何原因提供不同的mmap地址。

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

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

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

但是,由于诸如堆溢出攻击之类的安全攻击, malloc (这是一个堆管理器)在其实现中引入了一些随机性。 (但是,它的熵相对较小,因为堆管理者必须考虑速度和空间)因此,不要在堆管理者中采取严格的决定论。

而且,当你分叉进程时,有各种随机性的来源,包括ASLR 。

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

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

无论好坏, malloc 都不在“异步信号安全”函数列表中。

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

结论:你不能指望malloc在父母和孩子中产生相同的结果。 如果程序是多线程的,那么你就不能指望malloc在子进程中工作了,直到它调用exec为止 – 并且还有一个合理的问题,就是在子进程调用之前,它是否实际上保证能够工作exec

参考文献:

  1. fork规格
  2. 异步信号安全功能

你不会得到相同的物理地址。 如果你有进程A和B,每个malloc调用都会返回一个空闲块的地址。 A和B调用malloc的顺序是不可预测的。 但是这绝不会发生在“同一时刻”。

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

Linux使用fork的copy-on-write功能,所以分叉的子分享他们父母的内存,直到在这两个过程中发生了变化。 此时,内核会通过内存复制序列为分叉的子进程分配内存空间的专用/唯一副本。