10

我在很多地方(musl 邮件列表、macOS 论坛等)都听说过brk()sbrk()不安全的。其中很多地方要么根本不解释,要么解释的很模糊。例如,链接指出“这些功能从根本上被破坏”,并继续说mallocsbrk子系统完全被破坏,它们破坏了堆等。

我的问题是:为什么会这样?如果malloc以这样的方式使用它分配一个sbrk足够大的内存块来平息或大大减少对进一步分配的需求,那么使用起来不应该sbrk并且brk完全安全吗?

这是我的sbrkand实现brk

sbrk

#include <unistd.h>
#include <stddef.h>

void *sbrk(intptr_t inc)
{
        intptr_t curbrk = syscall(SYS_brk, NULL);

        if( inc == 0 ) goto ret;

        if( curbrk < 0 ) return (void *)-1;

        curbrk((void *)(curbrk+inc));
ret:
        return (void *)curbrk;
}

brk

#include <unistd.h>

intptr_t brk(void *ptr)
{
        if( (void *)syscall(SYS_brk, ptr) != ptr )
                return -1;
        else
                return 0;
}
4

1 回答 1

5

现实高度取决于实施,但这里有一些要素:

不安全

brk/sbrk被发明来允许进程从系统请求更多内存,并在单个连续段中释放它。malloc因此,它们被许多free实现使用。问题是,当它返回一个唯一的段时,当多个模块(同一进程的)直接使用它时,事情会出错。由于竞争条件,它在多线程进程中变得更糟。假设 2 个线程想要添加新内存。他们将使用 来查看当前的最高地址,查看相同的地址,使用或sbrk(0)请求新内存,并且由于竞争条件,两者都将使用相同的内存。brksbrk

即使在单线程进程中,一些实现mallocfree假设它们只被允许使用低级s/brk接口,并且任何其他代码都应该使用它们。在这种情况下,如果他们内部维护的中断段的图像不再是假定值,那么事情就会出错。他们应该猜测该段的某些部分被“保留”用于其他用途,可能会破坏释放任何内存的能力。

出于这个原因,用户代码不应该直接使用brk/sbrk并且只依赖malloc/ free。当且仅当您正在编写包含malloc/ realloc/ calloc/的标准库的实现时free,您可以安全地使用brk/sbrk

遗产

在现代系统上,mmap可以更好地使用虚拟内存管理。您可以根据需要使用尽可能多的动态内存段,而无需它们之间的交互。因此,在现代系统上,除非您特别需要使用brk/分配内存,否则sbrk您应该使用mmap.

可移植性

FreeBSD 参考brksbrk指出:

brk() 和 sbrk() 函数是现代虚拟内存管理出现之前的遗留接口。

然后:

BUGS:将 brk() 或 sbrk() 与 malloc(3)、free(3) 或类似函数混合将导致不可移植的程序行为。

于 2019-03-27T15:05:29.987 回答