天天看点

Solaris中检测内存异常访问

Solaris中检测内存异常访问

By judy on 十一月 30, 2007

本文介绍了在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
kmem_alloc_16 16 59421 59520 1904640 91402
kmem_alloc_24 24 25723 25806 1036288 79258
kmem_alloc_32 32 10552 10625 512000 28811
kmem_alloc_40 40 4288 4380 245760 17876
kmem_alloc_48 48 52219 52224 3342336 64754
kmem_alloc_56 56 653 672 49152 4127
kmem_alloc_64 64 337 352 45056 47603
kmem_alloc_80 80 50732 50736 4947968 60466
kmem_alloc_96 96 120 144 16384 1122
kmem_alloc_112 112 163 192 24576 1363
... ... ...

其中,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:
d5077bc0
d20c2df8
d20c2dd8 d20c2dd8 d20c2e00 f5f00
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
kmem_cache_alloc_debug+0x256
kmem_cache_alloc+0x1ac
kmem_zalloc+0x4b
dtrace_strdup+0x21
dtrace_probe_create+0x99
fbt_provide_module+0x306

继续阅读