尝试使用 sbrk 减少数据段大小时在 glibc 中中止

Arc*_*Arc -1 gcc mmap glibc heap-memory sbrk

在使用 glibc 时,我尝试使用sbrk使用负参数来减少数据段,并发现了一个最奇怪的行为。

\n

我首先使用malloc,然后free使用它,然后使用 减少数据段sbrk,然后malloc再次使用与第一个相同的大小。

\n

问题是,如果malloc大小(两个malloc大小相同)足够小(32k,或八个 4k 页),那么一切都可以正常工作。但是当我增加一点malloc- free-malloc大小(到九个 4k 页)时,我会得到核心转储。更奇怪的是,当我将大小提高malloc到超过mmap阈值(128k)时,我会得到调整中止行为。

\n

C代码:

\n
#define _GNU_SOURCE 1\n#include <stdio.h>\n#include <malloc.h>\n#include <unistd.h>\n\n// set MMAP_ALLOC_SIZE to 8 4k-pages it will work, \n// set it to 9 4k-pages it raises a \'segmentation fault (core dumped)\'\n// set it to 33 4k-pages it raises a \'break adjusted to free malloc space\' and \'abort (core dumped)\'\n#define MMAP_ALLOC_SIZE \xc2\xa0 (33 * 4096)\n#define PRINT_MEM { \\\n\xc2\xa0 \xc2\xa0 struct mallinfo mi; \\\n\xc2\xa0 \xc2\xa0 mi = mallinfo(); \\\n\xc2\xa0 \xc2\xa0 printf("ptr \xc2\xa0 \xc2\xa0%p\\n", ptr); \\\n\xc2\xa0 \xc2\xa0 printf("brk(0) %p\\n", sbrk(0)); \\\n\xc2\xa0 \xc2\xa0 printf("heap \xc2\xa0 %d bytes\\n", mi.arena); \\\n\xc2\xa0 \xc2\xa0 printf("mmap \xc2\xa0 %d bytes\\n\\n", mi.hblkhd); \\\n}\n\nint main(int argc, char *argv[])\n{\n\xc2\xa0 \xc2\xa0 void *ptr;\n\xc2\xa0 \xc2\xa0 ptr = NULL; \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0PRINT_MEM\n\xc2\xa0 \xc2\xa0 printf("1) will malloc > MMAP_THRESHOLD (128 KiB) ...\\n");\n\xc2\xa0 \xc2\xa0 ptr = malloc(MMAP_ALLOC_SIZE); \xc2\xa0 PRINT_MEM\n\xc2\xa0 \xc2\xa0 printf("2) will free malloc ...\\n");\n\xc2\xa0 \xc2\xa0 free(ptr); \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 PRINT_MEM\n\xc2\xa0 \xc2\xa0 printf("3) will reduce brk \xc2\xa0...\\n");\n\xc2\xa0 \xc2\xa0 ptr = sbrk(-100000); \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 PRINT_MEM\n\xc2\xa0 \xc2\xa0 printf("4) will malloc > MMAP_THRESHOLD (128 KiB) ... \\n");\n\xc2\xa0 \xc2\xa0 ptr = malloc(MMAP_ALLOC_SIZE); \xc2\xa0 PRINT_MEM\n\xc2\xa0 \xc2\xa0 printf("5) completion.\\n"); // never happens if MMAP_ALLOC_SIZE is > 8 4k-pages\n\xc2\xa0 \xc2\xa0 return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编译为:

\n
gcc -Wall testbrk.c -o testbrk\n
Run Code Online (Sandbox Code Playgroud)\n

这给出了成功的输出MMAP_ALLOC_SIZE (8 * 4096)

\n
ptr \xc2\xa0 \xc2\xa0(nil)\nbrk(0) 0xf46000\nheap \xc2\xa0 0 bytes\nmmap \xc2\xa0 0 bytes\n\n1) will malloc > MMAP_THRESHOLD (128 KiB) ...\nptr \xc2\xa0 \xc2\xa00xf25670\nbrk(0) 0xf46000\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 0 bytes\n\n2) will free malloc ...\nptr \xc2\xa0 \xc2\xa00xf25670\nbrk(0) 0xf46000\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 0 bytes\n\n3) will reduce brk \xc2\xa0...\nptr \xc2\xa0 \xc2\xa00xf46000\nbrk(0) 0xf2d960\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 0 bytes\n\n4) will malloc > MMAP_THRESHOLD (128 KiB) ... \nptr \xc2\xa0 \xc2\xa00xf25670\nbrk(0) 0xf2d960\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 0 bytes\n\n5) completion.\n
Run Code Online (Sandbox Code Playgroud)\n

以下中止输出MMAP_ALLOC_SIZE (9 * 4096)

\n
ptr \xc2\xa0 \xc2\xa0(nil)\nbrk(0) 0x1b7f000\nheap \xc2\xa0 0 bytes\nmmap \xc2\xa0 0 bytes\n\n1) will malloc > MMAP_THRESHOLD (128 KiB) ...\nptr \xc2\xa0 \xc2\xa00x1b5e670\nbrk(0) 0x1b7f000\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 0 bytes\n\n2) will free malloc ...\nptr \xc2\xa0 \xc2\xa00x1b5e670\nbrk(0) 0x1b7f000\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 0 bytes\n\n3) will reduce brk \xc2\xa0...\nptr \xc2\xa0 \xc2\xa00x1b7f000\nbrk(0) 0x1b66960\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 0 bytes\n\n4) will malloc > MMAP_THRESHOLD (128 KiB) ... \nSegmentation fault (core dumped)\n
Run Code Online (Sandbox Code Playgroud)\n

以及以下调整中止输出MMAP_ALLOC_SIZE (33 * 4096)

\n
ptr \xc2\xa0 \xc2\xa0(nil)\nbrk(0) 0x1093000\nheap \xc2\xa0 0 bytes\nmmap \xc2\xa0 0 bytes\n\n1) will malloc > MMAP_THRESHOLD (128 KiB) ...\nptr \xc2\xa0 \xc2\xa00x7fdd1c7f6010\nbrk(0) 0x1093000\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 139264 bytes\n\n2) will free malloc ...\nptr \xc2\xa0 \xc2\xa00x7fdd1c7f6010\nbrk(0) 0x1093000\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 0 bytes\n\n3) will reduce brk \xc2\xa0...\nptr \xc2\xa0 \xc2\xa00x1093000\nbrk(0) 0x107a960\nheap \xc2\xa0 135168 bytes\nmmap \xc2\xa0 0 bytes\n\n4) will malloc > MMAP_THRESHOLD (128 KiB) ... \nbreak adjusted to free malloc space\nAborted (core dumped)\n
Run Code Online (Sandbox Code Playgroud)\n

因此,sbrk缩减调用不会出现错误,但malloc即使仍有足够的内存可用,后续调用也会引发核心转储。

\n

我是做错了什么还是这是数据段调整大小的限制?

\n

编辑:除了使用我在下面发布的代码解决方案之外malloc_trim(),非常欢迎接受的答案,关于这个问题还有一些重要的事情需要了解,如从聊天中恢复的:

\n

首先,该man页面说避免使用sbrk,但 glibc 手册没有。

\n

malloc.c来自 glibc 确实包含有关可能使用有符号整数(因此是负整​​数)调用内存参数减少值的注释sbrk,并且确实有关于调用brk和 与sbrk保持一致的规定malloc。的工作原理malloc对 并非不敏感sbrk,它们不是来自“不同级别”的编码,它们应该和谐地一起工作,至少通过代码注释是这样。另外,我的第一个测试用例确实运行良好,这意味着这sbrk本身不是一个需要处理的问题malloc,但只是在某些特定情况下没有得到处理。

\n

最后,重要的是有人可以破坏 glibc 分配,这可能是一个安全漏洞。例如,黑客可以使用由另一层间接访问的 glibc 的某些实例来调用,从而sbrk导致库崩溃。我不是安全专家,但鉴于 glibc 在全球有如此多不同的用途,原则上某些恶意程序员可能会利用此sbrk崩溃来访问未受保护的系统。不确定,但肯定应该由 glibc 开发人员进行调查。

\n

我确信这不是一个无意义的问题。

\n

n. *_* m. 5

mallocglibc 内部使用有详细记录sbrk。如果没有另外说明,它也可以使用获得的内存sbrk用于内部簿记目的。这些内部簿记数据的确切存储位置既没有记录,也无法猜测。因此,拿走(通过或以其他方式)获得的任何内存都会使该数据无效。mallocsbrk

因此,sbrk带有负参数的程序永远不应该在也使用 的程序中使用malloc(当然还有任何可能使用 的库函数malloc,例如printf)。glibc 文档中可能应该包含这样的声明,以使上述推理变得不必要。有一个声明警告不要使用brkandsbrk一般情况下:

您通常不会使用本节中的函数,因为内存分配中描述的函数更容易使用。这些是 GNU C 库内存分配器的接口,该分配器使用其自身下面的函数。下面的函数是系统调用的简单接口。

如果要在 glibcmalloc竞技场结束时释放未使用的内存,请使用malloc_trim()(glibc 扩展,而不是标准 C 或 POSIX 函数)。