用slub track调试use after free问题

死机重启问题中,有部分是访问了已释放的内存导致,这就是典型的use after free问题.

打开CONFIG_SLUB_DEBUG和CONFIG_SLUB_DEBUG_ON宏开关后,系统就可以监测内存的释放与分配调用栈.

 

1. slab 内存布局

slub的内存管理原理这里就不在详述.直接给出slabobject对象的内存布局,object内存包含下面四个部分:

 

object_size +Redzone + Freepointer +2*track+pading

 

object_size :待分配内存的大小,alloc时为0x5a,free后为0x6b,但是最后一个Byte为0xa5表示object的结束,之后的数据都为metadata.

Redzone : 标记区.alloc填充0xcc,free后填充0xbb

Freepointer :指向下一个空闲的object

2个structtrack结构,用于跟踪内存的分配与释放栈

pading :填充区,为了内存对齐,填充为0x5a

下表为典型的object内存布局

 

 

 

2. 相关变量

kmem_cache->inuse= object_size + Redzone

kmem_cache->offset= inuse or =0

kmem_cache->size= object_size +Redzone+Freepointer+2*track+pading

 

3. 查找track信息

当系统出现KE时,用gdb调试打印内存,发现访问的内存全变成了0x6b,则可以怀疑是userafter free。

这里以skb为例,假如skb->protocol开始的内存全变成0x6b,获取protocol的地址

(gdb) p&(skb->protocol)

$6 = (__be16 *)0xffffffc02a3020c0

输出从0xffffffc02a3020c0地址开始的512Byte内容

 

 

(gdb) x /512xb0xffffffc02a3020c0

0xffffffc02a3020c0: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b

0xffffffc02a3020c8: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b

0xffffffc02a3020d0: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b

0xffffffc02a3020d8: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b

0xffffffc02a3020e0: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0xa5

0xffffffc02a3020e8: 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb

0xffffffc02a3020f0: 0x80 0x21 0x30 0x2a 0xc0 0xff 0xff 0xff

0xffffffc02a3020f8: 0x90 0xdb 0xa8 0x00 0xc0 0xff 0xff 0xff

0xffffffc02a302100: 0x9c 0x96 0x1e 0x04 0x58 0x9d 0x1e 0x04

0xffffffc02a302108: 0x3c 0xb0 0x1e 0x04 0x8c 0xdb 0xa8 0x04

0xffffffc02a302110: 0x28 0x0f 0xba 0x04 0x8c 0x36 0xba 0x04

0xffffffc02a302118: 0xb8 0x37 0xba 0x04 0x6c 0x16 0xba 0x04

0xffffffc02a302120: 0x00 0x00 0x00 0x00 0x11 0x55 0x00 0x00

0xffffffc02a302128: 0xc4 0x29 0x4e 0x01 0x01 0x00 0x00 0x00

0xffffffc02a302130:0x04 0xce 0xa8 0x00 0xc0 0xff 0xff 0xff

0xffffffc02a302138: 0x14 0xa0 0x1e 0x04 0xb8 0xa3 0x1e 0x04

0xffffffc02a302140: 0x6c 0xb8 0x1e 0x04 0x00 0xce 0xa8 0x04

0xffffffc02a302148: 0xb4 0xe3 0xa8 0x04 0x38 0x53 0xa9 0x04

0xffffffc02a302150: 0xb0 0x96 0xb8 0x04 0x3c 0x33 0xa8 0x04

0xffffffc02a302158: 0x03 0x00 0x00 0x00 0x29 0x01 0x00 0x00

0xffffffc02a302160: 0xc2 0x29 0x4e 0x01 0x01 0x00 0x00 0x00

0xffffffc02a302168: 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a

0xffffffc02a302170: 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a

0xffffffc02a302178: 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a

0xffffffc02a302180: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b

0xffffffc02a302188: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b

0xffffffc02a302190: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b

…...............................................................省去部分内存..................................................................

0xffffffc02a302258: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b

0xffffffc02a302260: 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0x6b 0xa5

0xffffffc02a302268: 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb

0xffffffc02a302270: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0xffffffc02a302278: 0x90 0xdb 0xa8 0x00 0xc0 0xff 0xff 0xff

0xffffffc02a302280: 0x9c 0x96 0x1e 0x04 0x58 0x9d 0x1e 0x04

0xffffffc02a302288: 0x3c 0xb0 0x1e 0x04 0x8c 0xdb 0xa8 0x04

0xffffffc02a302290: 0xcc 0x43 0xa9 0x04 0xcc 0xa3 0xa8 0x04

0xffffffc02a302298: 0x68 0x9e 0xb8 0x04 0x44 0x32 0xa8 0x04

0xffffffc02a3022a0: 0x03 0x00 0x00 0x00 0xeb 0x02 0x00 0x00

0xffffffc02a3022a8: 0xc2 0x29 0x4e 0x01 0x01 0x00 0x00 0x00

0xffffffc02a3022b0: 0x04 0xce 0xa8 0x00 0xc0 0xff 0xff 0xff

0xffffffc02a3022b8: 0x14 0xa0 0x1e 0x04 0xb8 0xa3 0x1e 0x04

从内存中查找关键字0xa5,0xbb,再参考slabobject的内存布局,很容易找到alloctrack和freetrack的地址,再用gdb打印出内存

free:0xffffffc02a302130

p /x *((struct track*)0xffffffc02a302130)

$13 = {addr =0xffffffc000a8ce04, addrs = {0x41ea014, 0x41ea3b8, 0x41eb86c,0x4a8ce00, 0x4a8e3b4, 0x4a95338, 0x4b896b0, 0x4a8333c}, cpu = 0x3,pid = 0x129,

when =0x1014e29c2}

同理,也可以打印出alloctrack的内存。

4. 转换track信息

track 的定义

struct track {

unsigned longaddr; /* Called from address */

#ifdefCONFIG_STACKTRACE

u32addrs[TRACK_ADDRS_COUNT]; //函数调用栈信息

/* we store theoffset after MODULES_VADDR for kernel module and kernel text address */

#endif

int cpu; /* Wasrunning on cpu */

int pid; /* Pidcontext */

unsigned longwhen; /* When did the operation occur */

};

为了节省空间,存储地址时,去掉了内核code起始地址,也就是需要addrs的值加上MODULES_VADDR,(MODULES_VADDR+t->addrs[i]),在这里MODULES_VADDR=0xffffffbffc000000,最后通过addr2line打印出free的调用栈为:

 

kernel-3.18/net/socket.c:791

kernel-3.18/net/unix/af_unix.c:1914

kernel-3.18/net/core/datagram.c:243

/kernel-3.18/net/core/skbuff.c:630

kernel-3.18/net/core/skbuff.c:561

kernel-3.18/mm/slub.c:2923

 

另外,需要说明MODULES_VADDR的值,每个平台都可能不一样,所有要根据实际情况取值.

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值