[英]Abort in glibc while trying to use sbrk to reduce the size of the data segment
在使用 glibc 时,我尝试使用sbrk使用负参数来减少数据段,并发现了一个最奇怪的行为。
我首先malloc
,然后free
它,然后用sbrk
减少数据段,然后再次malloc
与第一个大小相同。
问题是,如果malloc
大小(两个malloc
大小相同)足够小(32k 或 8 个 4k 页),那么一切正常。 但是当我增加一点malloc
- free
- malloc
大小(到 9 个 4k 页)时,我得到了核心转储。 更奇怪的是,当我提高malloc
大小以超过mmap
阈值(128k)时,我得到了调整中止行为。
C 代码:
#define _GNU_SOURCE 1
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
// set MMAP_ALLOC_SIZE to 8 4k-pages it will work,
// set it to 9 4k-pages it raises a 'segmentation fault (core dumped)'
// set it to 33 4k-pages it raises a 'break adjusted to free malloc space' and 'abort (core dumped)'
#define MMAP_ALLOC_SIZE (33 * 4096)
#define PRINT_MEM { \
struct mallinfo mi; \
mi = mallinfo(); \
printf("ptr %p\n", ptr); \
printf("brk(0) %p\n", sbrk(0)); \
printf("heap %d bytes\n", mi.arena); \
printf("mmap %d bytes\n\n", mi.hblkhd); \
}
int main(int argc, char *argv[])
{
void *ptr;
ptr = NULL; PRINT_MEM
printf("1) will malloc > MMAP_THRESHOLD (128 KiB) ...\n");
ptr = malloc(MMAP_ALLOC_SIZE); PRINT_MEM
printf("2) will free malloc ...\n");
free(ptr); PRINT_MEM
printf("3) will reduce brk ...\n");
ptr = sbrk(-100000); PRINT_MEM
printf("4) will malloc > MMAP_THRESHOLD (128 KiB) ... \n");
ptr = malloc(MMAP_ALLOC_SIZE); PRINT_MEM
printf("5) completion.\n"); // never happens if MMAP_ALLOC_SIZE is > 8 4k-pages
return 0;
}
编译:
gcc -Wall testbrk.c -o testbrk
这为MMAP_ALLOC_SIZE (8 * 4096)
提供了成功的 output :
ptr (nil)
brk(0) 0xf46000
heap 0 bytes
mmap 0 bytes
1) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0xf25670
brk(0) 0xf46000
heap 135168 bytes
mmap 0 bytes
2) will free malloc ...
ptr 0xf25670
brk(0) 0xf46000
heap 135168 bytes
mmap 0 bytes
3) will reduce brk ...
ptr 0xf46000
brk(0) 0xf2d960
heap 135168 bytes
mmap 0 bytes
4) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0xf25670
brk(0) 0xf2d960
heap 135168 bytes
mmap 0 bytes
5) completion.
以下为MMAP_ALLOC_SIZE (9 * 4096)
中止 output :
ptr (nil)
brk(0) 0x1b7f000
heap 0 bytes
mmap 0 bytes
1) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0x1b5e670
brk(0) 0x1b7f000
heap 135168 bytes
mmap 0 bytes
2) will free malloc ...
ptr 0x1b5e670
brk(0) 0x1b7f000
heap 135168 bytes
mmap 0 bytes
3) will reduce brk ...
ptr 0x1b7f000
brk(0) 0x1b66960
heap 135168 bytes
mmap 0 bytes
4) will malloc > MMAP_THRESHOLD (128 KiB) ...
Segmentation fault (core dumped)
以及MMAP_ALLOC_SIZE (33 * 4096)
的以下调整中止 output :
ptr (nil)
brk(0) 0x1093000
heap 0 bytes
mmap 0 bytes
1) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0x7fdd1c7f6010
brk(0) 0x1093000
heap 135168 bytes
mmap 139264 bytes
2) will free malloc ...
ptr 0x7fdd1c7f6010
brk(0) 0x1093000
heap 135168 bytes
mmap 0 bytes
3) will reduce brk ...
ptr 0x1093000
brk(0) 0x107a960
heap 135168 bytes
mmap 0 bytes
4) will malloc > MMAP_THRESHOLD (128 KiB) ...
break adjusted to free malloc space
Aborted (core dumped)
所以sbrk
减少调用没有错误,但随后的malloc
会引发核心转储,即使足够的 memory 仍然可用。
我做错了什么或者这是数据段调整大小的限制?
有据可查的是 glibc malloc
内部使用sbrk
。 如果没有另外声明,它也可以使用通过sbrk
获得的 memory 用于内部记账目的。 这个内部簿记数据的确切存储位置既没有记录也没有猜测。 因此,取走由malloc
(通过sbrk
或其他方式)获得的任何memory 可以使该数据无效。
It follows that sbrk
with a negative argument should never be used in a program that also uses malloc
(and of course any library function that might use malloc
, such as printf
). glibc 文档中可能应该包含这样的声明,以使上述推理变得不必要。 有一个声明警告一般不要使用brk
和sbrk
:
您通常不会使用本节中的函数,因为 Memory 分配中描述的函数更易于使用。 这些是 GNU C 库 memory 分配器的接口,它使用下面的函数。 下面的函数是系统调用的简单接口。
如果要在 glibc malloc
arena 的末尾释放未使用的 memory,请使用malloc_trim()
(glibc 扩展,不是标准的 Z0D61F8370CAD1D412F80ZB84D143E125 函数)。
正如接受的答案所建议的那样, malloc_trim 完美地完成了这项工作。 将主要更改为:
int main(int argc, char *argv[])
{
void *ptr;
ptr = NULL; PRINT_MEM
printf("1) will malloc > MMAP_THRESHOLD (128 KiB) ...\n");
ptr = malloc(MMAP_ALLOC_SIZE); PRINT_MEM
printf("2) will free malloc ...\n");
free(ptr); PRINT_MEM
printf("3) will reduce malloc_trim ...\n");
int r = malloc_trim(0); PRINT_MEM
printf("r is %d\n", r);
printf("4) will malloc > MMAP_THRESHOLD (128 KiB) ... \n");
ptr = malloc(MMAP_ALLOC_SIZE); PRINT_MEM
printf("5) completion.\n"); // never happens if MMAP_ALLOC_SIZE is > 8 4k-pages
return 0;
}
这为 MMAP_ALLOC_SIZE (8 * 4096) 提供了成功的 output:
ptr (nil)
brk(0) 0x4b3000
heap 0 bytes
mmap 0 bytes
1) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0x492670
brk(0) 0x4b3000
heap 135168 bytes
mmap 0 bytes
2) will free malloc ...
ptr 0x492670
brk(0) 0x4b3000
heap 135168 bytes
mmap 0 bytes
3) will reduce malloc_trim ...
ptr 0x492670
brk(0) 0x493000
heap 4096 bytes
mmap 0 bytes
r is 1
4) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0x492670
brk(0) 0x4bb000
heap 167936 bytes
mmap 0 bytes
5) completion.
以下成功的 output 为 MMAP_ALLOC_SIZE (9 * 4096):
ptr (nil)
brk(0) 0xe30000
heap 0 bytes
mmap 0 bytes
1) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0xe0f670
brk(0) 0xe30000
heap 135168 bytes
mmap 0 bytes
2) will free malloc ...
ptr 0xe0f670
brk(0) 0xe30000
heap 135168 bytes
mmap 0 bytes
3) will reduce malloc_trim ...
ptr 0xe0f670
brk(0) 0xe10000
heap 4096 bytes
mmap 0 bytes
r is 1
4) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0xe0f670
brk(0) 0xe39000
heap 172032 bytes
mmap 0 bytes
5) completion.
以及以下成功的 output 为 MMAP_ALLOC_SIZE (33 * 4096):
ptr (nil)
brk(0) 0xa5b000
heap 0 bytes
mmap 0 bytes
1) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0x7fd54f4c3010
brk(0) 0xa5b000
heap 135168 bytes
mmap 139264 bytes
2) will free malloc ...
ptr 0x7fd54f4c3010
brk(0) 0xa5b000
heap 135168 bytes
mmap 0 bytes
3) will reduce malloc_trim ...
ptr 0x7fd54f4c3010
brk(0) 0xa3b000
heap 4096 bytes
mmap 0 bytes
r is 1
4) will malloc > MMAP_THRESHOLD (128 KiB) ...
ptr 0xa3a670
brk(0) 0xa7c000
heap 270336 bytes
mmap 0 bytes
5) completion.
问题解决了!
另请注意,现在使用带有零参数的 malloc_trim 正确更新来自 mallinfo 的堆大小(在竞技场顶部留下一个 4k 页面),而使用带负参数的 sbrk 确实将 brk(0) 向下移动,但保留 mallinfo 堆大小原封不动。
非常感谢:(尽管仍然向 glibc 开发人员填写该错误报告:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.