首页 > 解决方案 > malloc_chunk 的布局到底是什么样的?

问题描述

(更新了我在底部的一些解释)

我正在研究 malloc 是如何工作的,在对 chunk 的结构有了基本的了解之后,我决定写一个小程序来仔细看看到底是怎么回事。

#include <stdio.h>
#include <stdlib.h>
/*
An allocated chunk looks like this:
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of previous chunk, if unallocated (P clear)  |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of chunk, in bytes                     |A|M|P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             User data starts here...                          .
            .                                                               .
            .             (malloc_usable_size() bytes)                      .
            .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             (size of chunk, but used for application data)    |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of next chunk, in bytes                |A|0|1|
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
int main()
{
    void *a = malloc(24);
    void *b = malloc(16);
    void *chunk_b = NULL, *chunk_after_b = NULL, *chunk_a = NULL;
    
    chunk_a = a - 0x8; //8 byte before a (user data)
    chunk_b = a + malloc_usable_size(a);
    chunk_after_b = b + malloc_usable_size(b);

    *(int *)a = 0xa; 
    *(int *)b = 0xb;

    printf("a:0x%x malloc_usable_size:%d(0x%x)\n", a, malloc_usable_size(a), malloc_usable_size(a));
    printf("b:0x%x malloc_usable_size:%d(0x%x)\n\n", b, malloc_usable_size(b), malloc_usable_size(b));
    
    printf("chunk_a[0]:0x%x value:0x%04x \n", chunk_a, ((int *)chunk_a)[0]);
    printf("chunk_a[1]:0x%x value:0x%04x \n", &((int *)chunk_a)[1], ((int *)chunk_a)[1]);
    printf("chunk_a[2]:0x%x value:0x%04x \n\n", &((int *)chunk_a)[2], ((int *)chunk_a)[2]);
    
    printf("chunk_b[0]:0x%x value:0x%04x \n", chunk_b, ((int *)chunk_b)[0]);
    printf("chunk_b[1]:0x%x value:0x%04x \n", &((int *)chunk_b)[1], ((int *)chunk_b)[1]);
    printf("chunk_b[2]:0x%x value:0x%04x \n\n", &((int *)chunk_b)[2], ((int *)chunk_b)[2]);
    
    printf("chunk_after_b[0]:0x%x value:0x%04x \n", chunk_after_b, ((int *)chunk_after_b)[0]);
    printf("chunk_after_b[1]:0x%x value:0x%04x \n", &((int *)chunk_after_b)[1], ((int *)chunk_after_b)[1]);
    printf("chunk_after_b[2]:0x%x value:0x%04x \n\n", &((int *)chunk_after_b)[2], ((int *)chunk_after_b)[2]);

    free(a);
    free(b); 
    return 0;
}

这就是我得到的:

a:0x625ca2a0 malloc_usable_size:24(0x18)
b:0x625ca2c0 malloc_usable_size:24(0x18)

chunk_a[0]:0x625ca298 value:0x0021
chunk_a[1]:0x625ca29c value:0x0000 
chunk_a[2]:0x625ca2a0 value:0x000a  -->mem (user data)
 
chunk_b[0]:0x625ca2b8 value:0x0021 
chunk_b[1]:0x625ca2bc value:0x0000 
chunk_b[2]:0x625ca2c0 value:0x000b -->mem (user data)
 
chunk_after_b[0]:0x625ca2d8 value:0x0411 
chunk_after_b[1]:0x625ca2dc value:0x0000 
chunk_after_b[2]:0x625ca2e0 value:0x6e756863

(gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) Target: x86_64-linux-gnu)

据我了解, malloc() 将返回“用户数据”部分的地址,因此减去 8 表示“块大小”的 8 个字节,“前一个块的大小”应该是该块的开始(chunk_a 在例如),另一方面,加上 malloc_usable_size() 应该是 nextchunk 的开始(示例中的 chunk_b )。

结果证明有些东西对我来说没有意义。

我认为“块的大小”应该是 chunk_a[1] 和 chunk_b[1],但它们的值都只是 0。但是,我认为“前一个块的大小”应该是 chunk_a[0 ]和chunk_b[0],实际得到的值是0x21,即十进制的33,LSB P减去1后可以得到32。

这个数字对我来说确实有点像,因为 malloc_usable_size(a) 在十进制中是 24 表示用户数据的大小,加上 8,这也表示“块大小”和“前一个块的大小”的 8 个字节,块的实际大小也会得到 32。

那么“块的大小”部分到底应该在哪里,我误解了什么吗?


感谢评论中的所有建议,我做了更多的测试,现在对我来说似乎是合理的。

关键是我错误地将两者都Size of previous chunk视为Size of chunk4 字节,因为我正在为 x86_64 系统进行编译,所以它们都应该是 8 字节。

按照这种方法,我重写了实验程序。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    
    int i=0;
    void *a = malloc(24);
    void *b = malloc(16);
    void *chunk_b = NULL, *chunk_a = NULL;
    
    chunk_a = (u_int64_t *)a - 2;
    chunk_b = a + malloc_usable_size(a);

    ((u_int8_t *)a)[0] = 0xa0;
    ((u_int8_t *)a)[23] = 0xa1;
    ((u_int8_t *)b)[0] = 0xb0;

    printf("a:0x%x malloc_usable_size:%d(0x%x)\n", a, malloc_usable_size(a), malloc_usable_size(a));
    printf("b:0x%x malloc_usable_size:%d(0x%x)\n\n", b, malloc_usable_size(b), malloc_usable_size(b));

    
    printf("chunk_a[0]:0x%x value:0x%016x \n", chunk_a, ((u_int64_t *)chunk_a)[0]);
    printf("chunk_a[1]:0x%x value:0x%016x \n", &((u_int64_t *)chunk_a)[1], ((u_int64_t *)chunk_a)[1]);
    printf("chunk_a[2]:0x%x value:0x%016x \n", &((u_int64_t *)chunk_a)[2], ((u_int64_t *)chunk_a)[2]);
    for(i = 0; i<24;i++)
    {
         printf("a[%d]:0x%x value:0x%02x \n", i, &((u_int8_t *)a)[i], ((u_int8_t *)a)[i]);
    }
    printf("\n");

    printf("chunk_b[0]:0x%x value:0x%016x \n", chunk_b, ((u_int64_t *)chunk_b)[0]);
    printf("chunk_b[1]:0x%x value:0x%016x \n", &((u_int64_t *)chunk_b)[1], ((u_int64_t *)chunk_b)[1]);
    printf("chunk_b[2]:0x%x value:0x%016x \n\n", &((u_int64_t *)chunk_b)[2], ((u_int64_t *)chunk_b)[2]);

   free(b);
   free(a);
    
    return 0;
}

这是这次的结果:

a:0xc2a4b2a0 malloc_usable_size:24(0x18)
b:0xc2a4b2c0 malloc_usable_size:24(0x18)

chunk_a[0]:0xc2a4b290 value:0x0000000000000000
chunk_a[1]:0xc2a4b298 value:0x0000000000000021
chunk_a[2]:0xc2a4b2a0 value:0x00000000000000a0
a[0]:0xc2a4b2a0 value:0xa0
a[1]:0xc2a4b2a1 value:0x00
a[2]:0xc2a4b2a2 value:0x00
a[3]:0xc2a4b2a3 value:0x00
a[4]:0xc2a4b2a4 value:0x00
a[5]:0xc2a4b2a5 value:0x00
a[6]:0xc2a4b2a6 value:0x00
a[7]:0xc2a4b2a7 value:0x00
a[8]:0xc2a4b2a8 value:0x00
a[9]:0xc2a4b2a9 value:0x00
a[10]:0xc2a4b2aa value:0x00
a[11]:0xc2a4b2ab value:0x00
a[12]:0xc2a4b2ac value:0x00
a[13]:0xc2a4b2ad value:0x00
a[14]:0xc2a4b2ae value:0x00
a[15]:0xc2a4b2af value:0x00
a[16]:0xc2a4b2b0 value:0x00
a[17]:0xc2a4b2b1 value:0x00
a[18]:0xc2a4b2b2 value:0x00
a[19]:0xc2a4b2b3 value:0x00
a[20]:0xc2a4b2b4 value:0x00
a[21]:0xc2a4b2b5 value:0x00
a[22]:0xc2a4b2b6 value:0x00
a[23]:0xc2a4b2b7 value:0xa1

chunk_b[0]:0xc2a4b2b8 value:0x0000000000000021
chunk_b[1]:0xc2a4b2c0 value:0x00000000000000b0
chunk_b[2]:0xc2a4b2c8 value:0x0000000000000000

起初,我认为两者都Size of previous chunk应该Size of chunk是 4 个字节,因为 malloc_usable_size() 返回的大小与我从(本例中为 0xa77ef2a0 nextchunk)开始添加返回值(本例中为 0x18)的位置不匹配。user data

我的误解是,如果 LSB P 的设置表明前一个块正在使用,那么它也Size of previous chunk 被视为前一个块的数据字段。Size of chunk

所以对于实验,我将分配内存的最后一个字节(0xc2a4b2b7)设置为“0xa1”,下面应该是Size of chunkchunk_b(0xc2a4b2b8),它确实如此!

现在这对我来说完全合理,但如果我仍然误解某些事情,请告诉我,并再次感谢所有评论。

标签: cmalloc

解决方案


推荐阅读