簡體   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