Solaris OpenSolaris
Judy Chen's Blog
« Solaris中如何检测内核代码的内存泄... | Main | 重设Mac OS X的密码 »
星期五 十一月 30, 2007
Solaris中检测内存异常访问
本文介绍了在solaris中如何利用核心内存分配的调试功能检测内存异常(corruption)。引起内存异常的常见操作包括:
我们用前一篇blog中生成的核心core文件为例,一步步进行分析。

核心缓存(Kernel Memory Cache)

首先回忆一下,为了发现内存泄漏运行mdb的::findleaks其输出为:

> ::findleaks
CACHE
LEAKED
BUFCTL
CALLER
... ...
dac32030
1
d4ec7748 tleak_open+0x35

第 一列是发生了内存泄漏的cache地址。solaris的核心内存分配机制把内存分成若干cache。每一cache由一组固定大小的buffer组成。 kmem_alloc(9F)或kmem_zalloc(9F)将从cache中获得所需内存。cache由数据结构kmem_cache_t (kmem_impl.h)定义。拿前文中的核心core文件做例子,用mdb的::kmastat命令看一下核心中有哪些cache。

> ::kmastat
cache
name
buf
size
buf
in use
buf
total
memory
in use
alloc
succeed
alloc
fail
----------
------
-------
-------
--------
------
----
... ... ...
kmem_alloc_8
8
110939
111010
2674688
205353
0
kmem_alloc_16
16
59421
59520
1904640
91402
0
kmem_alloc_24
24
25723
25806
1036288
79258
0
kmem_alloc_32
32
10552
10625
512000
28811
0
kmem_alloc_40
40
4288
4380
245760
17876
0
kmem_alloc_48
48
52219
52224
3342336
64754
0
kmem_alloc_56
56
653
672
49152
4127
0
kmem_alloc_64
64
337
352
45056
47603
0
kmem_alloc_80
80
50732
50736
4947968
60466
0
kmem_alloc_96
96
120
144
16384
1122
0
kmem_alloc_112
112
163
192
24576
1363
0
... ... ...

其 中,cache的名字kmem_alloc_后面的数字是该cache中buffer的大小。如kmem_alloc_8表示这个cache中的 buffer大小是8个字节。接下来我们用::kmem_cache命令简要查看上文中产生了内存泄漏的cache(也可以用宏%CONTENT%lt; kmem_cache打印数据结构kmem_cache_t)。

> dac32030::kmem_cache
ADDR
NAME
FLAG
CFLAG
BUFSIZE
BUFTOTL
dac32030
kmem_alloc_112
020f
200000
112
192

其 中重要的字段是name、bufsize和flag。从name和bufsize可以看出缓冲大小是112字节。flag的值定义在 kmem_impl.h中。0x20f表示(KMF_HASH | KMF_AUDIT | KMF_DEADBEEF | KMF_REDZONE | KMF_CONTENTS)。
mdb的::walk freemem和::walk kmem可分别用来查看chane的空闲和被占用的缓冲。

> dac32030::walk freemem
d7c91980
d7c91900
d4db0280
d4f05900
d4f05880
... ...
> dac32030::walk kmem
d4db0000
d4db0080
d4db0100
d4db0180
... ...


空闲缓冲(0xdeadbeef)

随便查看一个空闲缓冲的内容

> d7c91980/32X
0xd7c91980: deadbeef deadbeef deadbeef deadbeef

deadbeef deadbeef deadbeef deadbeef

deadbeef deadbeef deadbeef deadbeef

deadbeef deadbeef deadbeef deadbeef

deadbeef deadbeef deadbeef deadbeef

deadbeef deadbeef deadbeef deadbeef

deadbeef deadbeef deadbeef deadbeef

deadbeef deadbeef deadbeef deadbeef

feedface feedface d7c9dbf8 23272f16

缓冲的内容并不是0,而是0xdeedbeef。当缓冲被释放后,其内容会被清成0xdeedbeef。这样,用户就可以很容易地判断出访问的是否是一个已经被释放的内存。


已分配缓冲(0xbaddcafe)

再随便查看一个被占用的缓冲

> d4db0000/32X
0xd4db0000: 0 0 0 0

0 0 0 0

0 0 0 0

0 0 d5077bc0 0

0 0 d20c2df8 0

d20c2dd8 d20c2dd8 d20c2e00 f5f00

0 0 baddcabb baddcafe

feedface 65f9 d4ec7a18
75fcb2f5

缓冲的内容被初始化成0xbaddcafe。根据这个特殊的0xbaddcafe,用户可以判断出是否访问了未被初始化的内存。
一个特殊字段“bb”紧跟在实际要求分配的内存的后面。注意上文中的“baddcabb”而不是“bbddcafe”,这是由于x86系统是little endian的系统造成的。


Redzone (0xfeedface)

空 闲缓冲和被占用缓冲有一个共同字段0xfeedface。0xfeedface是Redzone的标志。它标识了一个buffer的边界。这里所说的边界 和上文bb标识的边界不同。bb表示的是用户请求分配的内存边界,而0xfeedface表示的是整个buffer的边界。0xfeedface和bb 都可用来判断是否有内存越界访问。紧跟Redzone的是一些调试数据,这些数据和redzone一起统称为buftag区(如下图所示)。当一个 cache的KMF_AUDIT、KMF_DEADBEEF或KMF_REDZONE标志位被设,buftag区就会被加到这个cache的每一 buffer后面。

|<------------------------ buffer ----------------------->|<---------- buftag ----------->|
User Data
bb
Unallocated RedZone
Debugging Data
|<------------------- cache_bufsize字节 ------------------->|<--- 64位 ---->|<--- 2个指针 -->|

RedZone:
0xfeedface
encoded index
User data size = encoded_index / 251 字节
Debugging Data:
bcp 指针
bxstat 指针
bcp pointer ^ bxstat pointer = a110c8ed | f4eef4ee

以kmem_alloc_8中的一段内存为例打印其内容

> dec82b18,6/2Xna
0xdec82b18:
75746572
bb006e72
-- User Data
0xdec82b20:
feedface
6de
-- RedZone
0xdec82b28:
decd3150
7fddf9bd
-- Debugging Data




0xdec82b30:
73666e
baddcabb
-- User Data
0xdec82b38:
feedface
3ed
-- RedZone
0xdec82b40:
decd30d8
7fddf835
-- Debugging Data

RedZone的0xfeedface后面是经过编码的用户实际使用的缓冲大小,其计算方法是:

size = redzone_value / 251

则在上述例子中

size = 0x6de / 251 = 7 字节

注意,x86系统是little endian的。


bufctl 指针

Debugging Data中的两个指针,前一个是指向bufctl的bcp指针,后一个是bxstat指针。bxstat用于校验bcp指针的有效性。对于以分配的缓冲, bcp XOR bxstat = 0xa110c8ed(allocated);而对于已释放的缓冲,bcp XOR bxstat = 0xf4eef4ee(freefree)。同样在上面的例子中

decd3150 ^ 7fddf9bd = a110c8ed

当kmem_flags的KMF_AUDIT位被设置后,bcp指针指向一个kmem_bufctl_audit_t结构。该结构包含使该缓冲在 allocated和freed状态之间转换的操作的详细信息。

>decd3150%CONTENT%lt;bufctl_audit
ADDR
BUFADDR
TIMESTAMP
THREAD

CACHE
LASTLOG
CONTENTS
decd3150
dec82b18
8f2a260f73
d5079980

dac2c030
db00cfc0
0

kmem_cache_alloc_debug+0x256

kmem_cache_alloc+0x1ac

kmem_zalloc+0x4b

dtrace_strdup+0x21

dtrace_probe_create+0x99

fbt_provide_module+0x306

Posted at 10:33上午 十一月 30, 2007 by judy in Solaris  |  评论[0]
评论:

发表一条评论:
  • HTML语法: 禁用