繁体   English   中英

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

[英]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 文档中可能应该包含这样的声明,以使上述推理变得不必要。 有一个声明警告一般不要使用brksbrk

您通常不会使用本节中的函数,因为 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM