zoukankan      html  css  js  c++  java
  • 读书摘要观后感与总结:《Glibc内存管理:ptmalloc2源代码分析》

    更新中

    在Linux平台下做漏洞利用的时候,针对于Heap部分总是有些不求甚解,下面开个博文来记录下《Glibc内存管理:ptmalloc2源代码分析》这本书的读后感和收获,一些简单的点将不再记录
    说明,本博文中所有的实验均在Linux Ubuntu16.04的环境下进行

    目录树:

    一些关于计算size的宏

    "chunk to mem" and "mem to chunk"

    about size

    分箱式内存管理

    smallbins

    largebins

    一些关于计算size的宏

    Ptmalloc设计的时候很巧妙的一点就是利用宏来屏蔽不同平台的差异,一些简单的细节比如chunk的形式在此我就不再赘述,下面记录一下读后有收获的点

    "chunk to mem" and "mem to chunk"

    /* conversion from malloc headers to user pointers, and back */
    #define chunk2mem(p)   ((void*)((char*)(p) + 2*SIZE_SZ))
    #define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ))
    

    about size

    MIN_CHUNK_SIZE定义了最小的chunk大小,MINSIZE定义了最小的分配的内存大小,是对MIN_CHUNK_SIZE进行了2*SIZE_SZ对齐,对齐后与MIN_CHUNK_SIZE的大小仍然是一样的

    /* The smallest possible chunk */
    #define MIN_CHUNK_SIZE        (offsetof(struct malloc_chunk, fd_nextsize))
    /* The smallest size we can malloc is an aligned minimal chunk */
    #define MINSIZE  
      (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
    

    下面说明一下chunk是如何计算其size的

    
    /* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
    #define PREV_INUSE 0x1
    /* extract inuse bit of previous chunk */
    #define prev_inuse(p)       ((p)->mchunk_size & PREV_INUSE)
    /* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
    #define IS_MMAPPED 0x2
    /* check for mmap()'ed chunk */
    #define chunk_is_mmapped(p) ((p)->mchunk_size & IS_MMAPPED)
    /* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
       from a non-main arena.  This is only set immediately before handing
       the chunk to the user, if necessary.  */
    #define NON_MAIN_ARENA 0x4
    
    #define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
    /* Like chunksize, but do not mask SIZE_BITS.  */
    #define chunksize_nomask(p)         ((p)->mchunk_size)
    /* Get size, ignoring use bits */
    #define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
    /* Ptr to next physical malloc_chunk. */
    #define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))
    /* Size of the chunk below P.  Only valid if !prev_inuse (P).  */
    #define prev_size(p) ((p)->mchunk_prev_size)
    

    比如做个实验来验证下,我们的chunksize为0x71,那么它本身的真实size是如何计算的?
    根据宏定义来计算
    可以看到计算得出的结果显然正确

    下面这一组宏定义用来check/set/clear当前chunk使用标志位,有当前chunk的使用标志位存储在下一个chunk的size的P位,所以下面的宏都要首先算出来下一个chunk的地址
    然后再做处理

    /* extract p's inuse bit */
    #define inuse(p)                                                              
      ((((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size) & PREV_INUSE)
    /* set/clear chunk as being inuse without otherwise disturbing */
    #define set_inuse(p)                                                              
      ((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size |= PREV_INUSE
    #define clear_inuse(p)                                                              
      ((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size &= ~(PREV_INUSE)
    

    我们可以简单来实验一下
    define inuse(p) 定义p的inuse

    define set_inuse(p) 设置p的inuse位(p的nextchuhnk来设置)

    define clear_inuse(p) 清理p的inuse位

    下面三个宏用来check/set/clear指定chunk的size域中的使用标志位

    /* check/set/clear inuse bits in known places */
    #define inuse_bit_at_offset(p, s)                                              
      (((mchunkptr) (((char *) (p)) + (s)))->mchunk_size & PREV_INUSE)
    #define set_inuse_bit_at_offset(p, s)                                              
      (((mchunkptr) (((char *) (p)) + (s)))->mchunk_size |= PREV_INUSE)
    #define clear_inuse_bit_at_offset(p, s)                                              
      (((mchunkptr) (((char *) (p)) + (s)))->mchunk_size &= ~(PREV_INUSE))
    

    分箱式内存管理

    smallbins

    smallbins有64个bin,实际共62个bin,bin[0]和bin[1]不存在
    chunk_size = 2 * SIZE_SZ * index
    范围:16B-504B (32B-1008B)
    ptmalloc维护了62个双向环形链表,每个链表都有头节点,便于管理,每个链表内各个空闲的chunk的大小一致

    largebins

    32:大于等于512B
    64:大于等于1024B
    一共63个bins
    每个bin中的chunk大小不是一个固定公差的等差数列,而是分成6组bin,每组bin是一个固定公差的等差数列
    每组的bin数量依次为:32,16, 8, 4, 2, 1
    公差依次为: 64,512,4096,32768,262144

    可以用数学来描述计算largebins的chunk_size
    第一组:chunksize = 512 + 64 * index
    第二组:chunksize = 512 + 64 * 32 + 512 * index
    ……
    可以看到,其实smallbins和largebins差不多满足同样的规律,所以可以将small bins和large bins放在同一个包含128个chunk的数组上,数组前一部分为small bins,后一部分为large bins。
    每个bin的index是chunk数组的下标,于是,可以根据下标来计算chunk大小(small bins)或者chunk大小范围(large bins)
    同样也可以计算所需chunk所属bin的index,ptmalloc使用一组宏来完成计算
    宏bin_index(sz)根据所需内存大小来计算出所需bin的index(如果是用户要分配的size,先使用checked_request2size来计算出chunk的大小)

    /*
       Indexing
        Bins for sizes < 512 bytes contain chunks of all the same size, spaced
        8 bytes apart. Larger bins are approximately logarithmically spaced:
        64 bins of size       8
        32 bins of size      64
        16 bins of size     512
         8 bins of size    4096
         4 bins of size   32768
         2 bins of size  262144
         1 bin  of size what's left
        There is actually a little bit of slop in the numbers in bin_index
        for the sake of speed. This makes no difference elsewhere.
        The bins top out around 1MB because we expect to service large
        requests via mmap.
        Bin 0 does not exist.  Bin 1 is the unordered list; if that would be
        a valid chunk size the small bins are bumped up one.
     */
    #define NBINS             128
    #define NSMALLBINS         64
    #define SMALLBIN_WIDTH    MALLOC_ALIGNMENT
    #define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)
    #define MIN_LARGE_SIZE    ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
    #define in_smallbin_range(sz)  
      ((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)
    #define smallbin_index(sz) 
      ((SMALLBIN_WIDTH == 16 ? (((unsigned) (sz)) >> 4) : (((unsigned) (sz)) >> 3))
       + SMALLBIN_CORRECTION)
    #define largebin_index_32(sz)                                                
      (((((unsigned long) (sz)) >> 6) <= 38) ?  56 + (((unsigned long) (sz)) >> 6) :
       ((((unsigned long) (sz)) >> 9) <= 20) ?  91 + (((unsigned long) (sz)) >> 9) :
       ((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :
       ((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :
       ((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :
       126)
    #define largebin_index_32_big(sz)                                            
      (((((unsigned long) (sz)) >> 6) <= 45) ?  49 + (((unsigned long) (sz)) >> 6) :
       ((((unsigned long) (sz)) >> 9) <= 20) ?  91 + (((unsigned long) (sz)) >> 9) :
       ((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :
       ((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :
       ((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :
       126)
    // XXX It remains to be seen whether it is good to keep the widths of
    // XXX the buckets the same or whether it should be scaled by a factor
    // XXX of two as well.
    #define largebin_index_64(sz)                                                
      (((((unsigned long) (sz)) >> 6) <= 48) ?  48 + (((unsigned long) (sz)) >> 6) :
       ((((unsigned long) (sz)) >> 9) <= 20) ?  91 + (((unsigned long) (sz)) >> 9) :
       ((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :
       ((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :
       ((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :
       126)
    #define largebin_index(sz) 
      (SIZE_SZ == 8 ? largebin_index_64 (sz)                                     
       : MALLOC_ALIGNMENT == 16 ? largebin_index_32_big (sz)                     
       : largebin_index_32 (sz))
    #define bin_index(sz) 
      ((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz))
    

    对于x86,bin[0]和bin[1]不存在,而NBINS定义为128,其实bin[0]和bin[127]都不存在,bin[1]为unsorted bin的chunk链表头
    通过查表可验证以上算法的正确性

    宏bin_at通过bin_index来获得bin的链表头,next_bin用于获得下一个bin的地址,first用于获得bin的第一个可用chunk,last用于获得最后一个可用chunk

    /* addressing -- note that bin_at(0) does not exist */
    #define bin_at(m, i) 
      (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2]))                              
                 - offsetof (struct malloc_chunk, fd))
    /* analog of ++bin */
    #define next_bin(b)  ((mbinptr) ((char *) (b) + (sizeof (mchunkptr) << 1)))
    /* Reminders about list directionality within bins */
    #define first(b)     ((b)->fd)
    #define last(b)      ((b)->bk)
    
  • 相关阅读:
    设计模式总结
    设计模式之工厂
    C#
    UML画图总结
    UML视频总结
    类图
    读取文件信息
    HMAC算法加密
    SHA_1计算消息摘要
    获取指定长度的随机字符串
  • 原文地址:https://www.cnblogs.com/lemon629/p/13844921.html
Copyright © 2011-2022 走看看