Solaris中检测内存异常访问

Solaris中检测内存异常访问

本文介绍了在solaris中如何利用核心内存分配的调试功能检测内存异常(corruption)。引起内存异常的常见操作包括:
  • 越界访问
  • 访问未被初始化的数据
  • 访问已被释放的内存
我们用前一篇blog中生成的核心core文件为例,一步步进行分析。

核心缓存(Kernel Memory Cache)

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

> ::findleaks
CACHE
LEAKED
BUFCTL
CALLER
... ...
dac32030
1
d4ec7748tleak_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:deadbeefdeadbeefdeadbeefdeadbeef

deadbeefdeadbeefdeadbeefdeadbeef

deadbeefdeadbeefdeadbeefdeadbeef

deadbeefdeadbeefdeadbeefdeadbeef

deadbeefdeadbeefdeadbeefdeadbeef

deadbeefdeadbeefdeadbeefdeadbeef

deadbeefdeadbeefdeadbeefdeadbeef

deadbeefdeadbeefdeadbeefdeadbeef

feedfacefeedfaced7c9dbf823272f16

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


已分配缓冲(0xbaddcafe)

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

> d4db0000/32X
0xd4db0000:0000

0000

0000

00d5077bc00

00d20c2df80

d20c2dd8d20c2dd8d20c2e00f5f00

00baddcabbbaddcafe

feedface65f9d4ec7a18
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
UnallocatedRedZone
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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值