c - Ubuntu 16.04 - malloc 实现。指向下一个 block 的指针在哪里?

标签 c linux memory-management malloc heap-memory

我想了解 glibc 中的 malloc 实现是如何工作的。根据 malloc 的源代码(glibc 2.23 中的 malloc.c),空闲内存块具有以下结构。

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of previous chunk                            |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `head:' |             Size of chunk, in bytes                         |P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Forward pointer to next chunk in list             |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |                 Back pointer to previous chunk in list        |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Unused space (may be 0 bytes long)                .
            .                                                               .
            .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `foot:' |             Size of chunk, in bytes                           |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

通常我们也应该能够在 gnu 调试器 (gdb) 中看到这个结构。所以我写了下面的程序。该程序分配了 6 个内存块,大小为 64 字节。每个 chunk 都填充了 memset,所以我们以后可以很容易地在 gdb 中看到相应的 chunk。因为 block 1、3 和 6 已被释放,所以它们应该具有上述结构。由于中间有分配的 block ,释放的 block 无法合并,因此它们通过每个 block 中的指针组织在双链表中。

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

void to_jump();

int main(int argc, char **argv){
    char *b1, *b2, *b3, *b4, *b5, *b6;

    //allocate 6 chunks of memory
    b1 = malloc(64);
    b2 = malloc(64);
    b3 = malloc(64);
    b4 = malloc(64);
    b5 = malloc(64);
    b6 = malloc(64);

    memset(b1, 'B', 64);
    memset(b2, 'C', 64);
    memset(b3, 'D', 64);
    memset(b5, 'E', 64);
    memset(b6, 'F', 64);
    //free chunks 1,3 and 6
    free(b1);
    free(b3);
    free(b6);

    strcpy(b4, argv[1]);   // <-- set breakpoint here
    //exploit this line
    free(b4);
    free(b5);
    free(b2);
}

void to_jump(){
    printf("Exploited");
}

当我在 gdb 中启动程序并在 strcpy(b4, argv[1]); 行中设置断点时,我们应该能够看到释放的 block 被组织在一个双链表中.但是 gdb 输出如下:

gdb-peda$ p b1
$11 = 0x602010 ""
gdb-peda$ x/62xg 0x602000
0x602000:   0x0000000000000000  0x0000000000000051  
0x602010:   0x0000000000000000  0x4242424242424242   |
0x602020:   0x4242424242424242  0x4242424242424242   | b1 (freed)
0x602030:   0x4242424242424242  0x4242424242424242   |
0x602040:   0x4242424242424242  0x4242424242424242   |
0x602050:   0x0000000000000000  0x0000000000000051
0x602060:   0x4343434343434343  0x4343434343434343   |
0x602070:   0x4343434343434343  0x4343434343434343   | b2 (allocated)
0x602080:   0x4343434343434343  0x4343434343434343   |
0x602090:   0x4343434343434343  0x4343434343434343   |
0x6020a0:   0x0000000000000000  0x0000000000000051
0x6020b0:   0x0000000000602000  0x4444444444444444   | 0x602000 is pointing to b1 (previous freed block)
0x6020c0:   0x4444444444444444  0x4444444444444444   | b3 (freed)
0x6020d0:   0x4444444444444444  0x4444444444444444   | 
0x6020e0:   0x4444444444444444  0x4444444444444444   |
0x6020f0:   0x0000000000000000  0x0000000000000051
0x602100:   0x0000000000000000  0x0000000000000000   |
0x602110:   0x0000000000000000  0x0000000000000000   | b4 (will be filled trough strcpy(b4, argv[1]);
0x602120:   0x0000000000000000  0x0000000000000000   |
0x602130:   0x0000000000000000  0x0000000000000000   |
0x602140:   0x0000000000000000  0x0000000000000051
0x602150:   0x4545454545454545  0x4545454545454545   |
0x602160:   0x4545454545454545  0x4545454545454545   | b5 (allocated)
0x602170:   0x4545454545454545  0x4545454545454545   |
0x602180:   0x4545454545454545  0x4545454545454545   |
0x602190:   0x0000000000000000  0x0000000000000051
0x6021a0:   0x00000000006020a0  0x4646464646464646   | 0x6020a0 is pointing to b3 (previous freed block)
0x6021b0:   0x4646464646464646  0x4646464646464646   | b6 (freed)
0x6021c0:   0x4646464646464646  0x4646464646464646   |
0x6021d0:   0x4646464646464646  0x4646464646464646   |
0x6021e0:   0x0000000000000000  0x0000000000020e21

在此输出中,我们可以看到已释放的 block 和指向前一个已释放 block 的后向指针(请参阅输出右侧的注释)。但是前向指​​针和前一个 block 的大小在哪里?

最佳答案

从 security.stackexchange 交叉发布

根据要释放的 block 的大小, block 被保存在不同类型的 bins(链表)中:

  • 未分类的垃圾箱
  • 小垃圾箱
  • 大垃圾箱

如果您有兴趣了解这些 bin 是如何维护的,建议您查看源代码。但所有这些容器中的一个共同点是列表是双向链接的。所以你的假设是正确的,你应该在释放的 block 中找到一个 forward 和一个 backward 指针(也许还有 以前的大小 场)

但是,还有另一种特殊的 bin,称为 fastbin。非常小的 block (通常在 16 到 80 字节之间,但不同版本可能略有不同)保存在这些 fastbins 中。与您的常规垃圾桶不同,这些垃圾桶是单链接。它们根据它们的大小被保存在一个合适的 fastbin 中(每个 bin 包含相同大小的 block )。不必遍历列表,可以按 LIFO 顺序添加和删除 block ,从而提高性能。与常规 block 不同的是,相邻的 fastbin-chunks 没有合并(这当然会导致碎片化,但可以更快地释放)。这也意味着您也不需要前一个 block 的大小。

你程序中的 block 也可能是这些 fastbins 之一的一部分。因此,要查看您的期望,请尝试分配和释放更大的内存。

关于c - Ubuntu 16.04 - malloc 实现。指向下一个 block 的指针在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41208970/

相关文章:

createNotify 事件调用过于频繁

linux - 迭代 Bash 脚本错误

递增指针会导致内存泄漏吗?

java - 如何使基于 Eclipse 的应用程序使用用户计算机上的所有内存?

c - 指向数组解引用的指针

c - C中的慢基数排序

c - C 中的制表符补全(readline 库)

java - linux 机器上 apache-tomcat 的推荐/标准安装路径是什么

linux - 环境变量在哪里定义?

memory-management - 使用比较 Xcode iOS9 中的 'Other Processes' 内存不足