[FAQ19076]如何查看slub内存申请/释放的调用栈

[DESCRIPTION]
踩内存是最难调试的问题之一,kernel里大部分kernel结构体都是从slub分配出去的,因此slub踩内存也是常见的问题。
通常遇到踩内存,我们会切换的eng版本复现,eng版本有开slub debug功能,会对free memory填充0x6b,pad填充0x5a等等,同时还会记录申请和释放的调用栈,可以轻易查出use after free问题。
下面将给出查看slub内存申请/释放的调用栈的方法。
 
[SOLUTION]
在eng版本,抓到minidump或ramdump情况下,如果有dump到slub memory,那么可以根据slub memory layout找出存放申请/释放的调用栈的位置,推导出申请/释放调用栈。具体位置看对应的代码就知道了:
[C/C++] hide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static struct track *get_track( struct kmem_cache *s, void *object, enum track_item alloc)
 
{
 
     struct track *p;
 
 
 
     if (s->offset)
 
        p = object + s->offset + sizeof ( void *);
 
     else
 
         p = object + s->inuse;
 
 
 
     return p + alloc;
 
}
可以看到是放在后面,已下面的例子来讲:
[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
_____________________address|________0________4________8________C_0123456789ABCDEF
 
  NSD:0000:FFFFFFC08167F790| 6B6B6B6B 6B6B6B6B 6B6B6B6B 6B6B6B6B kkkkkkkkkkkkkkkk ^E__+^Track+
 
  NSD:0000:FFFFFFC08167F7A0| 6B6B6B6B 6B6B6B6B 6B6B6B6B 6B6B6B6B kkkkkkkkkkkkkkkk
 
  NSD:0000:FFFFFFC08167F7B0| 6B6B6B6B 6B6B6B6B 6B6B6B6B 6B6B6B6B kkkkkkkkkkkkkkkk
 
  NSD:0000:FFFFFFC08167F7C0| 6B6B6B6B 6B6B6B6B 6B6B6B6B 6B6B6B6B kkkkkkkkkkkkkkkk
 
  NSD:0000:FFFFFFC08167F7D0| 6B6B6B6B 6B6B6B6B 6B6B6B6B 6B6B6B6B kkkkkkkkkkkkkkkk
 
  NSD:0000:FFFFFFC08167F7E0| 6B6B6B6B 6B6B6B6B 6B6B6B6B 6B6B6B6B kkkkkkkkkkkkkkkk
 
  NSD:0000:FFFFFFC08167F7F0| 6B6B6B6B 6B6B6B6B 6B6B6B6B A56B6B6B kkkkkkkkkkkkkkk.
 
  NSD:0000:FFFFFFC08167F800| BBBBBBBB BBBBBBBB 00000000 00000000 ................
 
  NSD:0000:FFFFFFC08167F810| 005A0268 FFFFFFC0 041E9A40 041EA47C h.Z.....@...|...
 
  NSD:0000:FFFFFFC08167F820| 041EAA58 045A0268 045930E8 04594AB0 X...h.Z..0Y..JY.
 
  NSD:0000:FFFFFFC08167F830| 0420B4CC 0420B6DC 00000000 00000242 .. ... .....B...
 
  NSD:0000:FFFFFFC08167F840| 00038494 00000001 0059FF2C FFFFFFC0 ........,.Y.....
 
  NSD:0000:FFFFFFC08167F850| 041E939C 041E9744 041EB8EC 0459FF2C ....D.......,.Y.
 
  NSD:0000:FFFFFFC08167F860| 040C3CDC 04085450 00000000 00000000 .<..PT..........
 
  NSD:0000:FFFFFFC08167F870| 00000000 00000348 00038494 00000001 ....H...........
0xBB是redzone,那么track起始地址是0xFFFFFFC08167F810,用trace32可以case出2个track结构体内容:
[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
(( struct track*)0xFFFFFFC08167F810)[0..1] = (
 
(
 
  addr = 0xFFFFFFC0005A0268,
 
  addrs = (
 
   0x041E9A40,
 
   0x041EA47C,
 
   0x041EAA58,
 
   0x045A0268,
 
   0x045930E8,
 
   0x04594AB0,
 
   0x0420B4CC,
 
   0x0420B6DC),
 
  cpu = 0x0,
 
  pid = 0x0242,
 
  when = 0x0000000100038494),
 
(
 
  addr = 0xFFFFFFC00059FF2C,
 
  addrs = (
 
   0x041E939C,
 
   0x041E9744,
 
   0x041EB8EC,
 
   0x0459FF2C,
 
   0x040C3CDC,
 
   0x04085450,
 
   0x0,
 
   0x0),
 
  cpu = 0x0,
 
  pid = 0x0348,
 
  when = 0x0000000100038494))
其中第0个是申请时的信息,addrs是申请调用栈,第1个是释放的信息,addrs是释放调用栈。
如果是64位OS的话,还需要将32位地址转换为64位地址才行。看代码就知道如何转换了:
[C/C++] hide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void set_track( struct kmem_cache *s, void *object, enum track_item alloc, unsigned long addr)
 
{
 
     ......
 
     for (i = 0; i < TRACK_ADDRS_COUNT; i++) {
 
         if (addrs[i])
 
             p->addrs[i] = addrs[i] - MODULES_VADDR;
 
         else
 
            p->addrs[i] = 0;
 
     }
 
     ......
 
}
只要加上MODULES_VADDR就可以。
然后再通过addr2line转换为具体的函数名,就可以看到调用栈了。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值