gdb分析堆栈破坏实例
原文链接:https://blog.csdn.net/mergerly/article/details/80523750
一、定位bug性质和范围
1、带符号分析dump
$ gdb IMActivityServer.symbol core.32530
(gdb) bt
看不出任何信息,日志也看不出什么,怀疑是堆栈破坏
2、增加堆栈保护, 用编译参数-fstack-protector-all为所有函数插入保护代码,编译版本,再次带符号查看崩溃dump
$ gdb IMActivityServer.symbol core.32530
(gdb) bt
堆栈可以看出函数名了cmdMsgParse,查看源码文件ActivityServer.cpp:930是函数返回地址,断定是某一个消息引起的堆栈破坏,这个是统一消息处理函数,消息量很大,
3、增加消息号处理日志,再放一个版本
多台服务器的日志最后一行都是:
180531-12:43:51 ActivityServer[17701] ERROR: [ActivityInfoManager.cpp:1293] cmd(51) param(0) len(70) server(0) id(0)
基本可以判断是51号消息引起的
二、详细分析bug
51号消息是一个通用转发包装消息,需要解析内部消息内容,考虑下断点统一处理函数
ActivityInfoManager::msgParseTask
1、先找到函数定义,看是否正确,有源码可以省略
2、再断下来解析参数
3、根据参数值下条件断点
// 清除老断点
(gdb) clear
Deleted breakpoint 1
// 下条件断点
(gdb) break ActivityInfoManager.cpp:1293 if ptNullCmd->cmd == 51
Breakpoint 2 at 0x6bdd50: file ActivityInfoManager.cpp, line 1293.
(gdb) c
Continuing.
4、分析51号消息
// 查看断下来的消息
(gdb) p *(const Cmd::t_NullCmd *) 0x7f83877fdc30
$11 = {{{byCmd = 51 '3', byParam = 0 ' 00'}, {cmd = 51 '3', para = 0 ' 00'}}}
// 51号消息结构等价于
struct stActivityInCmd{
BYTE cmd;
BYTE para;
DWORD ActId;
WORD size;
char data[0]
}
// 消息在内部data中,可以知道data是stActivityInCmd结构体地址+8字节,头也是Cmd::t_NullCmd结构体
(gdb) p *(const Cmd::t_NullCmd *) 0x7f83877fdc30+8
$11 = {{{byCmd = 51 '3', byParam = 12 'f'}, {cmd = 51 '3', para = 12 'f'}}}
// 可以看出是51号消息,子消息号是12,查源码知道消息是'Cmd::Activity::stOpMount'
// 显示详细结构内容
(gdb) p *(Cmd::Activity::stOpMount*)0x7f83877fdc38
$10 = {<Cmd::t_NullCmd> = {{{byCmd = 51 '3', byParam = 12 'f'}, {cmd = 51 '3', para = 12 'f'}}}, dwReqFunction = 10,
szName = "领工资 00sigin_pay_map_.size:[%d] 00", byOpType = 1 ' 01', dwIndex = 0, dwUserId = 22684283, wAddType = 32,
dwTimeStart = 1527740441, dwTimeEnd = 1528345241, bNeedDelMount = false, wDelType = 0, bExtension = true}
查看消息Cmd::Activity::stOpMount的处理流程,发现一处堆栈覆盖问题
Cmd::Activity::stActivityInCmd cmd;
...
bcopy(rev->data, cmd.data, rev->size);
消息没有初始化就使用了,直接往data里面写数据,参考上面的结构体定义,data指向的是结构体堆栈末尾,导致数据直接写入了堆栈中,覆盖了原有堆栈内容。
三、修复bug
修复的方法很简单,初始化一下结构体再使用就可以了。
四、gdb打印日志
$ gdb attach 28644
// 加载符号
(gdb) symbol-file IMActivityServer.symbol
Reading symbols from /home/ztgame/IMTESTVERSION/release/IMActivityServer.symbol...done.
// 开启日志
(gdb) set logging on
Future logs will be written to gdb.txt.
Copying output to gdb.txt.
// 下断点
(gdb) break ActivityInfoManager.cpp:1290
Breakpoint 2 at 0x6b70f0: file ActivityInfoManager.cpp, line 1290.
// 导入python库
(gdb) python import datetime
// 增加断点脚本命令
(gdb) commands 2 //指令集设置命令,断点序号
Type commands for breakpoint(s) 2, one per line.
End with a line saying just "end".
>silent //断点触发时不打印断点信息
>python gdb.execute("set $now="" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + """)
>printf "%s cmd(%u) param(%u) len(%u) srvtype(%u) srvid(%u)
",$now,ptNullCmd->cmd, ptNullCmd->para,cmdLen,((Cmd::Activity::stServerParam*)server_param)->type,((Cmd::Activity::stServerParam*)server_param)->serverid
>continue
>end //指令集设置结束时必须用end结束
(gdb) c
打开gdb.txt