[译] ptmalloc介绍
字数 2497 2025-08-19 12:41:16

ptmalloc内存分配器详解

1. ptmalloc基础概念

ptmalloc是glibc中使用的内存分配器,基于dlmalloc实现并进行了优化。它负责管理进程堆内存的分配和释放。

1.1 基本分配单位:malloc_chunk

ptmalloc中最基本的分配单位是malloc_chunk结构体,包含6个元数据域:

struct malloc_chunk {
    INTERNAL_SIZE_T mchunk_prev_size;  // 前一块大小(如果前一块空闲)
    INTERNAL_SIZE_T mchunk_size;       // 当前块大小(包括元数据)
    
    // 以下字段只在块空闲时使用
    struct malloc_chunk* fd;          // 前向指针(指向链表下一个块)
    struct malloc_chunk* bk;          // 后向指针(指向链表前一个块)
    
    // 以下字段只在large块空闲时使用
    struct malloc_chunk* fd_nextsize;  // 指向下一个不同大小的块
    struct malloc_chunk* bk_nextsize;  // 指向上一个不同大小的块
};

1.2 块类型

ptmalloc中有三种基本类型的块:

  1. 已分配块(allocated chunk)

    • 前一块大小字段可能被应用程序数据覆盖
    • 下一个邻接块的P位会被设置
  2. 释放块(freed chunk)

    • 包含完整的元数据
    • 下一个邻接块的P位会被清除
    • 根据块大小放入不同的bin中
  3. top块(top chunk)

    • 表示arena当前剩余的可用空间
    • 当空间不足时会通过brk()或mmap()扩展

1.3 块状态标志

mchunk_size的最后三位用于表示块的状态:

  • A (NON_MAIN_ARENA, 0x4): 当块来自非主arena时设置
  • M (IS_MMAPPED, 0x2): 当块通过mmap获取时设置
  • P (PREV_INUSE, 0x1): 当前一个邻接块在使用中时设置

2. 关键数据结构

2.1 malloc_state

malloc_state结构体用于管理堆内存:

struct malloc_state {
    __libc_lock_define(, mutex);      // 访问锁
    int flags;                       // 标志位
    
    // Fast bins
    mfastbinptr fastbinsY[NFASTBINS]; // 快速bin数组
    
    // Base of the topmost chunk
    mchunkptr top;                   // top chunk指针
    
    // The remainder from the most recent split
    mchunkptr last_remainder;        // 最近分割的剩余部分
    
    // Normal bins
    mchunkptr bins[NBINS * 2 - 2];   // 普通bin数组
    
    // Bitmap of bins
    unsigned int binmap[BINMAPSIZE]; // bin位图
    
    // Linked list
    struct malloc_state *next;       // 下一个arena
    struct malloc_state *next_free;  // 空闲arena链表
    
    // Thread count
    INTERNAL_SIZE_T attached_threads; // 附加线程数
    
    // Memory statistics
    INTERNAL_SIZE_T system_mem;      // 系统分配的内存
    INTERNAL_SIZE_T max_system_mem;  // 最大系统内存
};

2.2 重要宏定义

宏定义 x86值 x86-64值 描述
SIZE_SZ 4 8 size_t的大小
MIN_CHUNK_SIZE 16 32 最小块大小
MALLOC_ALIGNMENT 8 16 内存对齐大小
NBINS 128 128 bin总数
NFASTBINS 10 10 fast bin数量
NSMALLBINS 64 64 small bin数量
SMALLBIN_WIDTH 8 16 small bin宽度
DEFAULT_MXFAST 64 128 默认fast bin最大大小
MAX_FAST_SIZE 80 160 fast bin最大大小
MIN_LARGE_SIZE 512 1024 large bin最小大小

3. bin类型与管理

ptmalloc使用四种类型的bin来管理释放的块:

3.1 Fast bin

  • 特点

    • 单链表管理
    • 块大小 < 0x40 (x86) / < 0x80 (x86-64)
    • 不合并相邻空闲块(P位不清除)
    • 先进后出(FILO)分配策略
  • 示例

char *p1 = malloc(0x20);
char *p2 = malloc(0x20);
char *p3 = malloc(0x20);
free(p1); free(p2); free(p3);
// 释放顺序: p1 → p2 → p3
// 分配顺序: p3 → p2 → p1

3.2 Unsorted bin

  • 特点

    • 双链表管理
    • 块大小 ≥ 0x40 (x86) / ≥ 0x80 (x86-64)
    • 是释放大块的第一站
    • 分配时会遍历查找合适块
  • 分配策略

    1. 如果找到精确匹配的块,直接返回
    2. 否则将块放入small bin或large bin

3.3 Small bin

  • 特点

    • 双链表管理
    • 块大小 < 0x200 (x86) / < 0x400 (x86-64)
    • 每个bin管理固定大小的块
    • 先进先出(FIFO)分配策略
  • 示例

char *p1 = malloc(0xa0);
char *p2 = malloc(0x30);
char *p3 = malloc(0xa0);
free(p1); free(p3);
// 释放顺序: p1 → p3
// 分配顺序: p1 → p3

3.4 Large bin

  • 特点

    • 双链表管理
    • 块大小 ≥ 0x200 (x86) / ≥ 0x400 (x86-64)
    • 每个bin管理一个大小范围的块
    • 使用fd_nextsize和bk_nextsize维护大小排序
    • 最佳适配分配策略
  • 分配策略
    查找比请求大小大的最小块进行分配

4. 分配流程

ptmalloc的分配流程如下:

  1. 大小转换:将请求大小转换为实际分配大小
  2. fast bin查找:如果大小≤global_max_fast,尝试fast bin
  3. small bin查找:如果大小在small bin范围内,尝试small bin
  4. malloc_consolidate:如果small bin未找到,合并fast bin到unsorted bin
  5. unsorted bin遍历
    • 查找精确匹配的块
    • 否则将块放入small/large bin
  6. large bin查找:使用最佳适配算法查找合适块
  7. top chunk分割:如果以上都失败,分割top chunk
  8. 系统调用:如果top chunk不足,调用brk/mmap扩展堆

4.1 malloc_consolidate

malloc_consolidate函数用于合并fast bin中的块:

  1. 检查前一个邻接块是否空闲:
    • 如果空闲,合并到前一个块
  2. 检查下一个邻接块是否是top chunk:
    • 如果是,合并到top chunk
    • 否则检查下一个邻接块是否空闲:
      • 如果空闲,合并到当前块
  3. 将合并后的块放入unsorted bin

4.2 关键分配函数

void* __libc_malloc(size_t bytes) {
    // 1. 大小转换
    checked_request2size(bytes, nb);
    
    // 2. fast bin尝试
    if (nb <= get_max_fast()) {
        idx = fastbin_index(nb);
        victim = fastbin(av, idx);
        if (victim != NULL) {
            // 检查fast bin是否损坏
            if (__builtin_expect(fastbin_index(chunksize(victim)) != idx, 0))
                malloc_printerr("malloc(): memory corruption (fast)");
            // 返回分配的内存
            return chunk2mem(victim);
        }
    }
    
    // 3. small bin尝试
    if (in_smallbin_range(nb)) {
        idx = smallbin_index(nb);
        bin = bin_at(av, idx);
        if ((victim = last(bin)) != bin) {
            // 检查small bin链表是否损坏
            if (victim == 0) malloc_consolidate(av);
            else if (__glibc_unlikely(bck->fd != victim))
                malloc_printerr("malloc(): smallbin double linked list corrupted");
            // 返回分配的内存
            return chunk2mem(victim);
        }
    }
    
    // 4. unsorted bin遍历
    while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) {
        // 检查大小是否合法
        if (__builtin_expect(chunksize_nomask(victim) <= 2 * SIZE_SZ, 0) ||
            __builtin_expect(chunksize_nomask(victim) > av->system_mem, 0))
            malloc_printerr("malloc(): memory corruption");
        
        size = chunksize(victim);
        
        // 精确匹配检查
        if (size == nb) {
            // 返回精确匹配的块
            return chunk2mem(victim);
        }
        
        // 放入small/large bin
        if (in_smallbin_range(size)) {
            // 放入small bin
        } else {
            // 放入large bin
        }
    }
    
    // 5. large bin查找
    if (!in_smallbin_range(nb)) {
        // 最佳适配算法查找
    }
    
    // 6. top chunk分割
    victim = av->top;
    size = chunksize(victim);
    if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE)) {
        // 分割top chunk
        remainder_size = size - nb;
        remainder = chunk_at_offset(victim, nb);
        av->top = remainder;
        // 返回分配的内存
        return chunk2mem(victim);
    }
    
    // 7. 系统调用扩展堆
    void *p = sysmalloc(nb, av);
    return p;
}

5. 安全机制

ptmalloc包含多种安全检查:

  1. fast bin检查:验证fast bin中的块大小是否正确
  2. small bin检查:验证small bin的双向链表是否损坏
  3. unsorted bin检查:验证块大小是否合法
  4. large bin检查:验证large bin的排序是否正确
  5. top chunk检查:验证top chunk大小是否合法

当检测到错误时,会调用malloc_printerr输出错误信息并终止程序。

6. 总结

ptmalloc是一个复杂的内存分配器,其核心特点包括:

  1. 使用chunk作为基本管理单位,包含丰富的元数据
  2. 通过四种bin管理空闲内存,针对不同大小优化分配效率
  3. 采用多阶段分配策略,平衡速度和内存利用率
  4. 包含严格的安全检查,防止内存破坏
  5. 支持多线程环境,每个线程有自己的arena

理解ptmalloc的内部机制对于分析堆相关漏洞和进行堆利用至关重要。

ptmalloc内存分配器详解 1. ptmalloc基础概念 ptmalloc是glibc中使用的内存分配器,基于dlmalloc实现并进行了优化。它负责管理进程堆内存的分配和释放。 1.1 基本分配单位:malloc_ chunk ptmalloc中最基本的分配单位是 malloc_chunk 结构体,包含6个元数据域: 1.2 块类型 ptmalloc中有三种基本类型的块: 已分配块(allocated chunk) : 前一块大小字段可能被应用程序数据覆盖 下一个邻接块的P位会被设置 释放块(freed chunk) : 包含完整的元数据 下一个邻接块的P位会被清除 根据块大小放入不同的bin中 top块(top chunk) : 表示arena当前剩余的可用空间 当空间不足时会通过brk()或mmap()扩展 1.3 块状态标志 mchunk_size 的最后三位用于表示块的状态: A (NON_ MAIN_ ARENA, 0x4) : 当块来自非主arena时设置 M (IS_ MMAPPED, 0x2) : 当块通过mmap获取时设置 P (PREV_ INUSE, 0x1) : 当前一个邻接块在使用中时设置 2. 关键数据结构 2.1 malloc_ state malloc_state 结构体用于管理堆内存: 2.2 重要宏定义 | 宏定义 | x86值 | x86-64值 | 描述 | |--------|-------|----------|------| | SIZE_ SZ | 4 | 8 | size_ t的大小 | | MIN_ CHUNK_ SIZE | 16 | 32 | 最小块大小 | | MALLOC_ ALIGNMENT | 8 | 16 | 内存对齐大小 | | NBINS | 128 | 128 | bin总数 | | NFASTBINS | 10 | 10 | fast bin数量 | | NSMALLBINS | 64 | 64 | small bin数量 | | SMALLBIN_ WIDTH | 8 | 16 | small bin宽度 | | DEFAULT_ MXFAST | 64 | 128 | 默认fast bin最大大小 | | MAX_ FAST_ SIZE | 80 | 160 | fast bin最大大小 | | MIN_ LARGE_ SIZE | 512 | 1024 | large bin最小大小 | 3. bin类型与管理 ptmalloc使用四种类型的bin来管理释放的块: 3.1 Fast bin 特点 : 单链表管理 块大小 < 0x40 (x86) / < 0x80 (x86-64) 不合并相邻空闲块(P位不清除) 先进后出(FILO)分配策略 示例 : 3.2 Unsorted bin 特点 : 双链表管理 块大小 ≥ 0x40 (x86) / ≥ 0x80 (x86-64) 是释放大块的第一站 分配时会遍历查找合适块 分配策略 : 如果找到精确匹配的块,直接返回 否则将块放入small bin或large bin 3.3 Small bin 特点 : 双链表管理 块大小 < 0x200 (x86) / < 0x400 (x86-64) 每个bin管理固定大小的块 先进先出(FIFO)分配策略 示例 : 3.4 Large bin 特点 : 双链表管理 块大小 ≥ 0x200 (x86) / ≥ 0x400 (x86-64) 每个bin管理一个大小范围的块 使用fd_ nextsize和bk_ nextsize维护大小排序 最佳适配分配策略 分配策略 : 查找比请求大小大的最小块进行分配 4. 分配流程 ptmalloc的分配流程如下: 大小转换 :将请求大小转换为实际分配大小 fast bin查找 :如果大小≤global_ max_ fast,尝试fast bin small bin查找 :如果大小在small bin范围内,尝试small bin malloc_ consolidate :如果small bin未找到,合并fast bin到unsorted bin unsorted bin遍历 : 查找精确匹配的块 否则将块放入small/large bin large bin查找 :使用最佳适配算法查找合适块 top chunk分割 :如果以上都失败,分割top chunk 系统调用 :如果top chunk不足,调用brk/mmap扩展堆 4.1 malloc_ consolidate malloc_consolidate 函数用于合并fast bin中的块: 检查前一个邻接块是否空闲: 如果空闲,合并到前一个块 检查下一个邻接块是否是top chunk: 如果是,合并到top chunk 否则检查下一个邻接块是否空闲: 如果空闲,合并到当前块 将合并后的块放入unsorted bin 4.2 关键分配函数 5. 安全机制 ptmalloc包含多种安全检查: fast bin检查 :验证fast bin中的块大小是否正确 small bin检查 :验证small bin的双向链表是否损坏 unsorted bin检查 :验证块大小是否合法 large bin检查 :验证large bin的排序是否正确 top chunk检查 :验证top chunk大小是否合法 当检测到错误时,会调用 malloc_printerr 输出错误信息并终止程序。 6. 总结 ptmalloc是一个复杂的内存分配器,其核心特点包括: 使用chunk作为基本管理单位,包含丰富的元数据 通过四种bin管理空闲内存,针对不同大小优化分配效率 采用多阶段分配策略,平衡速度和内存利用率 包含严格的安全检查,防止内存破坏 支持多线程环境,每个线程有自己的arena 理解ptmalloc的内部机制对于分析堆相关漏洞和进行堆利用至关重要。