【问题标题】:What's unsafe/legacy about brk/sbrk?brk/sbrk 有什么不安全/遗留问题?
【发布时间】:2019-08-17 11:10:34
【问题描述】:

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

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

这是我对sbrkbrk 的实现:

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;
}

【问题讨论】:

  • 文本For an application to use them correctly, it must depend on the malloc subsystem never being used, but this is impossible to guarantee since malloc may be used internally by libc functions without documenting this to the application. 中有解释。如果你的应用调用了 sbrk,而你的应用调用了 malloc,而 malloc 调用了 sbrk,而 sbrk 函数使用了全局上下文,那么结果将是一团糟。他们想消除 sbrk 功能,让用户不使用它,而不是因为它本身就是一个糟糕的设计。
  • “全局上下文”是什么意思?快速的 Google 搜索不会显示任何内容。
  • sbrk 通常对单个全局变量进行操作,例如 glibc 中的 __curbrknewlib 中的 static char *heap_end;。这意味着所有“上下文”(线程、函数、malloc、printf 和用户应用程序)共享相同的(“全局”到进程)数据段。
  • 我的猜测是某些malloc 实现被编写为假设没有其他人在背后调用sbrk。例如,假设您调用p = malloc(N),其中 N 是一个很大的数字(可能是页面大小的倍数)。 malloc 执行sbrk(N) 从操作系统获取内存并将此指针返回给您。稍后(没有任何对 malloc 的干预调用)你会做free(p)。现在 malloc “知道”它可以通过 sbrk(-N) 将内存返回给操作系统。如果你在两者之间调用了sbrk,那么你就有问题了。
  • awkwardly written, but good basic malloc introduction 使用brk/sbrk 编写您自己的内存管理器(仅限介绍级别)

标签: c malloc legacy sbrk brk


【解决方案1】:

现实在很大程度上取决于实施,但这里有一些要素:

不安全

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

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

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

旧版

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

便携性

brksbrk 的 FreeBSD 参考声明如下:

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

及以后:

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

【讨论】:

  • 这和我见过的解释差不多。不要将malloc 与您自己对brk/sbrk 的调用混为一谈。我想你是对的。如果malloc 找不到它离开它的中断,那么它可能不会释放它最初分配的任何内存。我没有看过malloc 的来源,但这是有道理的。
  • 这绝对是比我目前看到的更好的解释。然而,它引出了一个问题:如果我可以让malloc()free() 检查程序中断更改,是否可以在我的C 库中安全地使用sbrk()brk()?我也会给你一个 +1 来提及线程。我还没有实现它们,所以我没有考虑过。谢谢!
猜你喜欢
  • 2011-10-15
  • 2017-01-07
  • 1970-01-01
  • 2021-10-04
  • 1970-01-01
  • 2010-11-03
  • 1970-01-01
  • 2015-06-04
  • 2021-09-27
相关资源
最近更新 更多