获取中...

-

Just a minute...

当我们使用malloc的时候,函数调用的是__libc_malloc函数。 __ libc_malloc函数是用来封装 _int_malloc函数的。

strong_alias是GNU C中的定义,编译器判定这里malloc是libc_malloc的别名,__libc_malloc定义在malloc.c中

1
strong_alias (__libc_malloc, __malloc) strong_alias (__libc_malloc, malloc)

__libc_malloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void *
__libc_malloc (size_t bytes)
/*这里的 bytes是用户申请分配的空间的大小这是用户提出
申请的原始大小,如果malloc(8),那么bytes的大小就是8,
并不是经过request2size宏计算得到的对应 chunk 的大小。*/
{
mstate ar_ptr;
void *victim;

/*首先检查是否有内存分配钩子malloc_hook,如果有,调用钩子并返回。malloc_hook
函数主要用于进程在创建新线程过程中分配内存,或者支持用户供的内存分配函数*/
void *(*hook) (size_t, const void *)
= atomic_forced_read (__malloc_hook);
if (__builtin_expect (hook != NULL, 0))
return (*hook)(bytes, RETURN_ADDRESS (0));

/*如果没有寻找一个arena获取一个可用的分配区来试图分配内存*/
arena_get (ar_ptr, bytes);

/*调用_int_malloc函数去申请对应的内存。_int_malloc函数是堆块分配的主函数*/
victim = _int_malloc (ar_ptr, bytes);

/*如果分配内存失败,ptmalloc会尝试再去寻找一个可用的arena,并分配内存*/
if (!victim && ar_ptr != NULL)
{
LIBC_PROBE (memory_malloc_retry, 1, bytes);
ar_ptr = arena_get_retry (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
}

/*如果申请到了arena,在退出前需要释放该主分配区的锁*/
if (ar_ptr != NULL)
(void) mutex_unlock (&ar_ptr->mutex);

/*判断目前的状态是否满足以下条件
1.没有申请到内存 2.是mmap的内存 3.申请到的内存必须在其所分配的arena中*/
assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
ar_ptr == arena_for_chunk (mem2chunk (victim)));
//返回内存
return victim;
}
libc_hidden_def (__libc_malloc)

首先函数先检查了 __malloc_hook 是否被设置,若被设置,则直接调用 _ _malloc_hook。
若未被设置,则首先调用arena_get函数获取一个可用的分配区,然后调用 __int_malloc函数,该函数是堆块分配的主函数。如果 __int_malloc函数返回的chunk的指针为空,且当前的分配区指针不为空,就再次尝试 _ init_malloc,对分配之后的chunk指针进行地址检查,检查是否为mmap并存在于当前分配区的chunk。

malloc_hook_ini

libc_malloc函数中定义了一个全局钩子malloc_hook

1
2
void *(*hook) (size_t, const void *)
= atomic_forced_read (__malloc_hook);

查看这个钩子,这个钩子被赋值为malloc_hook_ini

1
2
void *weak_variable (*__malloc_hook)
(size_t __size, const void *) = malloc_hook_ini;

如果我们需要自定义堆分配函数,那么就可以把 __malloc_hook 重新设置成我们自定义的函数,在 _libc_malloc 的最开始处会判断是否调用 __malloc_hook。也就是说ptmalloc提供了一个机会去使用自己定义的堆分配函数来完成对堆空间的申请,申请完成后直接返回

1
2
3
4
void *(*hook) (size_t, const void *)
= atomic_forced_read (__malloc_hook);
if (__builtin_expect (hook != NULL, 0))
return (*hook)(bytes, RETURN_ADDRESS (0));

如果没有自定义堆分配函数,在调用__libc_malloc函数时就会调用 malloc_hook_ini进行内存的分配。malloc_hook_ini 是义在hook.c文件夹中的。

1
2
3
4
5
static void * malloc_hook_ini (size_t sz, const void *caller){
__malloc_hook = NULL;//把__malloc_hook设置为NULL
ptmalloc_init ();
return __libc_malloc (sz);
}

malloc_hook_ini 函数会将 _ _ malloc _ hook函数设置为NULL,然后调用 ptmalloc_init 函数,这个函数是对ptmalloc进行初始化的,然后会重复调用 _ _ libc_malloc函数。当第一次调用 _ _ libc _ malloc 函数申请堆空间的时候,首先会进入 malloc_hook_ini 函数里面进行对ptmalloc的初始化工作,然后再次进入 _ _ libc _ malloc的时候,此时钩子函数_ _malloc_hook已经被置空了,从而继续执行剩余的代码,转入 _int_malloc 函数。所以调用malloc函数的时候调用的函数是不同的。

当第一次调用malloc函数时,_ _ malloc _ hook函数没有被置空,会依次调用 _ _ libc _ malloc函数,malloc_hook_ini函数,ptmalloc_init函数,_ _ libc_malloc函数,_ int_malloc函数

之后再次调用malloc函数时,_ _ malloc _ hook函数被置空,会依次调用 _ _ libc _ malloc函数,_int_malloc函数。

ptmalloc_init

ptmalloc_init用来对整个ptmalloc框架进行初始化,定义在arena.c中。全局变量 __malloc_initialized用来保证全局只初始化ptmalloc一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
static void
ptmalloc_init (void)
{
//检测全局变量的状态
if (__malloc_initialized >= 0)//大于0已经初始化
return;

__malloc_initialized = 0;//等于0正在初始化

#ifdef SHARED
Dl_info di;
struct link_map *l;
if (_dl_open_hook != NULL
|| (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
&& l->l_ns != LM_ID_BASE))
__morecore = __failing_morecore;

#endif
thread_arena = &main_arena;
thread_atfork (ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2);
const char *s = NULL;
if (__glibc_likely (_environ != NULL))
{
char **runp = _environ;
char *envline;

while (__builtin_expect ((envline = next_env_entry (&runp)) != NULL,
0))
{
size_t len = strcspn (envline, "=");

if (envline[len] != '=')
continue;

switch (len)
{
case 6:
if (memcmp (envline, "CHECK_", 6) == 0)
s = &envline[7];
break;
case 8:
if (!__builtin_expect (__libc_enable_secure, 0))
{
if (memcmp (envline, "TOP_PAD_", 8) == 0)
__libc_mallopt (M_TOP_PAD, atoi (&envline[9]));
else if (memcmp (envline, "PERTURB_", 8) == 0)
__libc_mallopt (M_PERTURB, atoi (&envline[9]));
}
break;
case 9:
if (!__builtin_expect (__libc_enable_secure, 0))
{
if (memcmp (envline, "MMAP_MAX_", 9) == 0)
__libc_mallopt (M_MMAP_MAX, atoi (&envline[10]));
else if (memcmp (envline, "ARENA_MAX", 9) == 0)
__libc_mallopt (M_ARENA_MAX, atoi (&envline[10]));
}
break;
case 10:
if (!__builtin_expect (__libc_enable_secure, 0))
{
if (memcmp (envline, "ARENA_TEST", 10) == 0)
__libc_mallopt (M_ARENA_TEST, atoi (&envline[11]));
}
break;
case 15:
if (!__builtin_expect (__libc_enable_secure, 0))
{
if (memcmp (envline, "TRIM_THRESHOLD_", 15) == 0)
__libc_mallopt (M_TRIM_THRESHOLD, atoi (&envline[16]));
else if (memcmp (envline, "MMAP_THRESHOLD_", 15) == 0)
__libc_mallopt (M_MMAP_THRESHOLD, atoi (&envline[16]));
}
break;
default:
break;
}
}
}
if (s && s[0])
{
__libc_mallopt (M_CHECK_ACTION, (int) (s[0] - '0'));
if (check_action != 0)
__malloc_check_init ();
}
void (*hook) (void) = atomic_forced_read (__malloc_initialize_hook);
if (hook != NULL)
(*hook)();
__malloc_initialized = 1; //将__malloc_initialized设置为1,表示初始化完成
}

进入 ptmalloc_init,首先判断 _ malloc_initialized 的值, _malloc _ initialized 是一个全局变量,它标记着 ptmalloc 的初始化状态。当值大于0时,初始化完成。当值等于0时,正在初始化。当值小于0时,没有初始化。 当 ptmalloc_init 中完成对 ptmalloc 的初始化工作后,设置__malloc_initialized 为 1,之后再次进入 ptmalloc_init 时就会直接退出,不会重复初始化。

libc_mallopt

__libc_mallopt定义在malloc.c中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
int __libc_mallopt (int param_number, int value)
{
mstate av = &main_arena;
int res = 1;

if (__malloc_initialized < 0)
ptmalloc_init ();
(void) mutex_lock (&av->mutex);
/* Ensure initialization/consolidation */
malloc_consolidate (av);

LIBC_PROBE (memory_mallopt, 2, param_number, value);

switch (param_number)
{
case M_MXFAST:
if (value >= 0 && value <= MAX_FAST_SIZE)
{
LIBC_PROBE (memory_mallopt_mxfast, 2, value, get_max_fast ());
set_max_fast (value);
}
else
res = 0;
break;

case M_TRIM_THRESHOLD:
LIBC_PROBE (memory_mallopt_trim_threshold, 3, value,
mp_.trim_threshold, mp_.no_dyn_threshold);
mp_.trim_threshold = value;
mp_.no_dyn_threshold = 1;
break;

case M_TOP_PAD:
LIBC_PROBE (memory_mallopt_top_pad, 3, value,
mp_.top_pad, mp_.no_dyn_threshold);
mp_.top_pad = value;
mp_.no_dyn_threshold = 1;
break;

case M_MMAP_THRESHOLD:
/* Forbid setting the threshold too high. */
if ((unsigned long) value > HEAP_MAX_SIZE / 2)
res = 0;
else
{
LIBC_PROBE (memory_mallopt_mmap_threshold, 3, value,
mp_.mmap_threshold, mp_.no_dyn_threshold);
mp_.mmap_threshold = value;
mp_.no_dyn_threshold = 1;
}
break;

case M_MMAP_MAX:
LIBC_PROBE (memory_mallopt_mmap_max, 3, value,
mp_.n_mmaps_max, mp_.no_dyn_threshold);
mp_.n_mmaps_max = value;
mp_.no_dyn_threshold = 1;
break;

case M_CHECK_ACTION:
LIBC_PROBE (memory_mallopt_check_action, 2, value, check_action);
check_action = value;
break;

case M_PERTURB:
LIBC_PROBE (memory_mallopt_perturb, 2, value, perturb_byte);
perturb_byte = value;
break;

case M_ARENA_TEST:
if (value > 0)
{
LIBC_PROBE (memory_mallopt_arena_test, 2, value, mp_.arena_test);
mp_.arena_test = value;
}
break;

case M_ARENA_MAX:
if (value > 0)
{
LIBC_PROBE (memory_mallopt_arena_max, 2, value, mp_.arena_max);
mp_.arena_max = value;
}
break;
}
(void) mutex_unlock (&av->mutex);
return res;
}
libc_hidden_def (__libc_mallopt)

malloc_hook_ini最后会回调 _ _libc_malloc 函数 , _ _malloc_hook为null

arena_get

之后调用__libc_molloc的arena_get函数获得一个分配区。arena_get是个宏定义,定义在arena.c中

1
2
3
4
#define arena_get(ptr, size) do { \
arena_lookup (ptr); \
arena_lock (ptr, size); \
} while (0)

arena_lookup从私有变量里获取分配区指针

1
2
3
4
#define arena_lookup(ptr) do { \
void *vptr = NULL; \
ptr = (mstate) tsd_getspecific (arena_key, vptr); \
} while (0)

tsd_getspecific也是个宏定义,就是获取前面调用tsd_setspecific设置的分配区指针,这里取出的可能是主分配去指针,也可能是非主分配去指针,然后调用arena_lock对malloc_state中的mutex加锁。

1
2
3
4
5
6
#define arena_lock(ptr, size) do {                        \
if (ptr && !arena_is_corrupt (ptr)) \
(void) mutex_lock (&ptr->mutex); \
else \
ptr = arena_get2 (ptr, (size), NULL); \
} while (0)

获得分配去的指针后,就会调用 _ int_malloc开始分配内存了

libc_malloc总结

要么没有申请到内存

要么通过mmap/brk申请内存

要么在其arena中分配内存(或者出错退出)

最终返回内存(return)

核心:调用_int_malloc函数

相关文章
评论
分享
  • Alloc to Stack&Arbitary Alloc

    Alloc to Stack和Arbitary Alloc都利用了fastbin链表的特性。 Alloc To Stack利用了fastbin链表的特性。当前的chunk的fd指向下一个chunk。Alloc To Stack核心...

    Alloc to Stack&Arbitary Alloc
  • House of Spirit

    House of Spirit针对fastbin,也是fastbin attach的一种。核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。 原理House of Spi...

    House of Spirit
  • Fastbin Double Free

    double free 是任意地址写的一种技巧,指堆上的某块内存被释放后,并没有将指向该堆块的指针清零,那么,我们就可以利用程序的其他部分对该内存进行再次的free, 利用条件Fastbin Double Free 能够成功利用主...

    Fastbin Double Free
Please check the parameter of comment in config.yml of hexo-theme-Annie!