[英]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.