[FAQ18276]增强slub越界调试功能

[DESCRIPTION]
踩内存通常很难排查,原因在于踩内存的时候不一定崩溃,所以我们要千方百计加强各种内存检查。
在quick start里有专题讲解踩内存问题:
  • MediaTek On-Line> Quick Start> 踩内存专题分析
kernel中使用的内存大部分来自slub,因此slub的调试非常重要。
slub本身是自带调试功能的,不过只有 eng版本有开,user、userdebug默认关闭。
以下列出slub的调试功能:
kernel-3.18/include/linux/slab.h
[C/C++] hide
1
2
3
4
5
6
7
#define SLAB_DEBUG_FREE 0x00000100UL /* DEBUG: Perform (expensive) checks on free */
#define SLAB_RED_ZONE 0x00000400UL /* DEBUG: Red zone objs in a cache */
#define SLAB_POISON 0x00000800UL /* DEBUG: Poison objects */
#define SLAB_STORE_USER 0x00010000UL /* DEBUG: Store the last owner for bug hunting */
red zone可以检查内存越界问题,但由于slub的设计是固定大小的内存池,导致有部分空间是没有使用的(比如:申请1字节,slub给你8字节,7字节没有被使用),这导致了有些小范围越界无法被检查出来。
为了能检查到所有越界的行为,就必须记录申请时的大小,然后根据实际大小设定red zone区域。以下讲解方法。
[SOLUTION]
前提是打开了CONFIG_SLUB_DEBUG,CONFIG_SLUB_DEBUG_ON
1. 修改slab_alloc_node相关的函数,传入申请的内存大小,并初始化red zone。
1) 修改slab_alloc_node()函数,多添加一个参数size和相关代码:
[C/C++] hide
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
static __always_inline void *slab_alloc_node( struct kmem_cache *s, size_t size /* 添加size参数 */ , gfp_t gfpflags, int node, unsigned long addr)
{
......
if (unlikely(gfpflags & __GFP_ZERO) && object)
memset (object, 0, s->object_size);
/* 添加这段代码 */
if (object && (s->flags&SLAB_RED_ZONE)) {
size = s->object_size - size;
set_freepointer(s, object, ( void *)( uintptr_t )size);
if (size)
memset (( void *)object + s->object_size - size, SLUB_RED_ACTIVE, size); /* 初始化red zone */
}
/* 添加结束 */
slab_post_alloc_hook(s, gfpflags, object);
return object;
}
2) 所有调用slab_alloc_node()函数的函数都要修改,多添加一个参数size:
[C/C++] hide
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
static __always_inline void *slab_alloc( struct kmem_cache *s, size_t size /* 添加size参数 */ , gfp_t gfpflags, unsigned long addr)
{
return slab_alloc_node(s, size /* 添加size参数 */ , gfpflags, NUMA_NO_NODE, addr);
}
void *kmem_cache_alloc( struct kmem_cache *s, gfp_t gfpflags)
{
void *ret = slab_alloc(s, s->object_size /* 添加object_size参数 */ , gfpflags, _RET_IP_);
......
}
void *kmem_cache_alloc_trace( struct kmem_cache *s, gfp_t gfpflags, size_t size)
{
void *ret = slab_alloc(s, size /* 添加size参数 */ , gfpflags, _RET_IP_);
......
}
void *kmem_cache_alloc_node( struct kmem_cache *s, gfp_t gfpflags, int node)
{
void *ret = slab_alloc_node(s, s->object_size /* 添加object_size参数 */ , gfpflags, node, _RET_IP_);
......
}
void *kmem_cache_alloc_node_trace( struct kmem_cache *s, gfp_t gfpflags, int node, size_t size)
{
void *ret = slab_alloc_node(s, size /* 添加size参数 */ , gfpflags, node, _RET_IP_);
......
}
void *__kmalloc( size_t size, gfp_t flags)
{
......
ret = slab_alloc(s, size /* 添加size参数 */ , flags, _RET_IP_);
......
}
void *__kmalloc_node( size_t size, gfp_t flags, int node)
{
......
ret = slab_alloc_node(s, size /* 添加size参数 */ , flags, node, _RET_IP_);
......
}
void *__kmalloc_track_caller( size_t size, gfp_t gfpflags, unsigned long caller)
{
......
ret = slab_alloc(s, size /* 添加size参数 */ , gfpflags, caller);
......
}
void *__kmalloc_node_track_caller( size_t size, gfp_t gfpflags, int node, unsigned long caller)
{
......
ret = slab_alloc_node(s, size /* 添加size参数 */ , gfpflags, node, caller);
......
}
2. 调整check_object()函数,多检查修改后的red zone:
[C/C++] hide
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
static int check_object( struct kmem_cache *s, struct page *page, void *object, u8 val)
{
......
/* Check free pointer validity */
/* 添加这段代码 */
if (val == SLUB_RED_ACTIVE) {
if (s->flags&SLAB_RED_ZONE) {
size_t remain = ( size_t )get_freepointer(s, p);
BUG_ON(remain >= s->object_size);
if (remain && !check_bytes_and_report(s, page, object, "Redzone" , object + s->object_size - remain, val, remain))
return 0;
}
} else
/* 添加结束 */
if (!check_valid_pointer(s, page, get_freepointer(s, p))) {
object_err(s, page, p, "Freepointer corrupt" );
set_freepointer(s, p, NULL);
return 0;
}
return 1;
}
3. 修改slab_ksize相关的函数,获取实际的内存大小。
1) 修改slab_ksize()函数,多添加一个参数object和相关代码:
[C/C++] hide
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
static inline size_t slab_ksize( const struct kmem_cache *s, const void *object /* 添加object参数 */ )
{
#ifdef CONFIG_SLUB_DEBUG
/* 添加这段代码 */
if ((s->flags&SLAB_RED_ZONE) && object) {
size_t remain = ( size_t )get_freepointer(( void *)s, ( void *)object);
BUG_ON(remain >= s->object_size);
return s->object_size - remain;
}
/* 添加结束 */
if (s->flags&(SLAB_RED_ZONE|SLAB_POISON))
return s->object_size;
#endif
......
}
2) 所有调用slab_ksize()函数的函数都要修改,多添加一个参数object:
[C/C++] hide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static inline void slab_post_alloc_hook( struct kmem_cache *s, gfp_t flags, void *object)
{
......
kmemcheck_slab_alloc(s, flags, object, slab_ksize(s, object /* 添加这个参数 */ ));
......
}
size_t ksize( const void *object)
{
......
return slab_ksize(page->slab_cache, object /* 添加object参数 */ );
}
4. early_kmem_cache_node_alloc()函数调整:
[C/C++] hide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void early_kmem_cache_node_alloc( int node)
{
......
#ifdef CONFIG_SLUB_DEBUG
set_freepointer(kmem_cache_node, n, NULL); /* 添加这行代码 */
init_object(kmem_cache_node, n, SLUB_RED_ACTIVE);
init_tracking(kmem_cache_node, n);
#endif
......
}

你可能感兴趣的:([FAQ18276]增强slub越界调试功能)