【发布时间】:2017-10-20 16:58:21
【问题描述】:
我正在尝试重写 malloc 和 calloc,我的问题是关于 calloc 的实现,而不是如何使用它。
应该始终使用calloc() 而不是malloc()+memset(),因为它可以利用copy-on-write (COW)。
一些calloc 是这样实现的:
void * calloc(size_t nelem, size_t elsize)
{
void *p;
p = malloc (nelem * elsize);
if (p == 0)
return (p);
bzero (p, nelem * elsize);
return (p);
}
但他们根本不使用 COW(而且他们不检查溢出)。
如果这些实现不调用bzero(),他们必须假定他们收到的mmap'ed 页面是零填充的。他们可能是出于安全原因,我们不希望其他进程的数据泄漏,但我找不到任何关于此的标准参考。
我们可以从/dev/zero 中使用mmap,而不是使用MAP_ANON:
fd = open("/dev/zero", O_RDWR);
a = mmap (0, 4096e4, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, 0);
但是/dev/zero 不是 POSIX 强制要求的,人们可以很容易地做到 sudo mv /dev/zero /dev/foo,这会破坏我的实现。
尊重写时复制的有效重写calloc()的正确方法是什么?
【问题讨论】:
-
实现
calloc()和malloc()的正确方法可能涉及进行适当的系统调用。细节是,perforce,系统特定的。 -
@JohnBollinger 例如哪个系统调用?
-
mmap,例如,如果分配足够大并且mmap有一个保证零填充的选项(例如 linux 上的MAP_ANONYMOUS)。虽然我不相信这是一种常见的优化。 -
在answer you linked 中,有一条注释写着
mmap,内核总是清理内存,因为您不会将其他进程的内存留在其中(但是,这可能只能保证我认为在 linux 上)。所以在那种情况下,内存应该已经被归零了。如果它来自您的一个池,您只需要将内存归零。 -
请注意,您的
nelem * elsize很危险,并且是安全漏洞的常见来源。您必须检查那里的溢出(关键字:__builtin_mul_overflow())
标签: c malloc standards calloc copy-on-write