gdb分析堆栈破坏实例

一、定位bug性质和范围

1、带符号分析dump

$ gdb IMActivityServer.symbol core.32530
(gdb) bt
#0  0x0000000000a6951a in ?? ()
#1  0x00000000018c6db8 in ?? ()
#2  0x8f127f1911ab2800 in ?? ()
#3  0x00000000018c6d00 in ?? ()
#4  0x0000000400000004 in ?? ()
#5  0x00000000018c6d88 in ?? ()
#6  0x00000000006ba9bf in ?? ()
#7  0x00000000018a4400 in ?? ()
#8  0x8f127f1911ab2800 in ?? ()
#9  0x00000000018c6d00 in ?? ()
#10 0x00007f518b789010 in ?? ()
#11 0x00000000018c6d00 in ?? ()
#12 0x0000000000693166 in ?? ()
#13 0x0000000000000000 in ?? ()

看不出任何信息,日志也看不出什么,怀疑是堆栈破坏

2、增加堆栈保护, 用编译参数-fstack-protector-all为所有函数插入保护代码,编译版本,再次带符号查看崩溃dump

$ gdb IMActivityServer.symbol core.32530
(gdb) bt
#0  0x00007f08a95d7118 in ?? () from /lib64/libgcc_s.so.1
#1  0x00007f08a95d8019 in _Unwind_Backtrace () from /lib64/libgcc_s.so.1
#2  0x00007f08a93110c6 in backtrace () from /lib64/libc.so.6
#3  0x00007f08a927c334 in __libc_message () from /lib64/libc.so.6
#4  0x00007f08a9314a77 in __fortify_fail () from /lib64/libc.so.6
#5  0x00007f08a9314a40 in __stack_chk_fail () from /lib64/libc.so.6
#6  0x00000000006909a9 in ActivityService::cmdMsgParse (this=<optimized out>, ptNullCmd=<optimized out>, 
    nCmdLen=<optimized out>) at ActivityServer.cpp:930
#7  0x00007f08a75ae1e8 in ?? ()
#8  0x00007f0818000978 in ?? ()
#9  0x00007f0818000a08 in ?? ()
#10 0x00007f0818000970 in ?? ()
#11 0x0000000000000000 in ?? ()

堆栈可以看出函数名了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、先找到函数定义,看是否正确,有源码可以省略

(gdb) info func ActivityInfoManager::msgParseTask
All functions matching regular expression "ActivityInfoManager::msgParseTask":

File ActivityInfoManager.cpp:
bool ActivityInfoManager::msgParseTask(Cmd::t_NullCmd const*, unsigned int, void const*);
bool ActivityInfoManager::msgParseTask(unsigned int, Cmd::t_NullCmd const*, unsigned int, void const*);
bool ActivityInfoManager::msgParseTaskByWebTransfer(Cmd::t_NullCmd const*, unsigned int, void const*);
bool ActivityInfoManager::msgParseTaskGM(Cmd::t_NullCmd const*, unsigned int, unsigned int);

2、再断下来解析参数

(gdb) break ActivityInfoManager.cpp:1293
(gdb) c
Continuing.
[Switching to Thread 0x7f83877fe700 (LWP 414)]

Breakpoint 1, ActivityInfoManager::msgParseTask (this=0x7f840c16e010, ptNullCmd=0x7f83877edc30, cmdLen=84, 
    server_param=0x7f83877edc20) at ActivityInfoManager.cpp:1293
1293    ActivityInfoManager.cpp: 没有那个文件或目录.
(gdb) info locals
__temp_format__ = <error reading variable __temp_format__ (can't compute CFA for this frame)>
s = 0x7f83877edc20
buffercmd = <error reading variable buffercmd (can't compute CFA for this frame)>
cmd = <optimized out>
pBase = <optimized out>
// 查看ptNullCmd的结构体定义,有源码可以省略
(gdb) info types Cmd::t_NullCmd
All types matching regular expression "Cmd::t_NullCmd":

File ../../common/zNullCmd.h:
Cmd::t_NullCmd;
Cmd::t_NullCmd;
Cmd::t_NullCmd;
Cmd::t_NullCmd;
Cmd::t_NullCmd;

// 显示结构体的内容,有源码可以省略
(gdb) ptype Cmd::t_NullCmd  
type = class Cmd::t_NullCmd {  
  public:  
    union {  
        <no data fields>  
    };  

    void t_NullCmd(BYTE, BYTE);  
}  

// 显示参数详细内容
(gdb) p *(const Cmd::t_NullCmd *) 0x7f83877edc30
$11 = {{{byCmd = 51 '3', byParam = 0 '\000'}, {cmd = 51 '3', para = 0 '\000'}}}
(gdb) p *(Cmd::Activity::stServerParam*)0x7f83877fdc10
$6 = {asServer = 0 '\000', type = 0, serverid = 0}

// 查看消息值
(gdb) p ptNullCmd->cmd
$2 = 33 '!'

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 '\000'}, {cmd = 51 '3', para = 0 '\000'}}}

// 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 = "领工资\000sigin_pay_map_.size:[%d]\000", byOpType = 1 '\001', 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)\n",$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

2018-05-31 19:45:45 cmd(17) param(1) len(38) srvtype(43) srvid(4301)
2018-05-31 19:45:45 cmd(17) param(1) len(49) srvtype(43) srvid(4301)
2018-05-31 19:45:45 cmd(17) param(1) len(49) srvtype(43) srvid(4301)
2018-05-31 19:45:45 cmd(17) param(1) len(38) srvtype(43) srvid(4301)
2018-05-31 19:45:45 cmd(17) param(1) len(38) srvtype(43) srvid(4301)
2018-05-31 19:45:46 cmd(22) param(14) len(54) srvtype(150) srvid(15000)
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
目录 第1章 嵌入式系统基础知识 .1 1.1 嵌入式系统概述 1 1.1.1 嵌入式系统的发展史 2 1.1.2 嵌入式系统的定义与特点 3 1.1.3 嵌入式系统的特点 4 1.2 嵌入式系统的组成 5 1.2.1 嵌入式系统的硬件架构 6 1.2.2 嵌入式操作系统 9 1.2.3 嵌入式应用软件 11 1.3 arm处理器平台介绍 12 1.3.1 arm处理器简介 12 1.3.2 arm处理器系列 13 1.3.3 arm体系结构简介 17 1.3.4 s3c2410处理器简介 18 1.4 嵌入式系统硬件平台选型 22 1.4.1 硬件平台的选择 22 1.4.2 arm处理器选型 23 1.5 嵌入式系统开发概述 25 1.5.1 嵌入式系统开发流程 25 1.5.2 嵌入式软件开发流程 26 .本章小结 31 动手练练 31 第2章 嵌入式linux c语言开发工具 32 2.1 嵌入式linux下c语言概述 32 2.1.1 c语言简史 33 2.1.2 c语言特点 33 2.1.3 嵌入式linux c语言编程环境 34 2.2 嵌入式linux编辑器vi的使用 35 2.2.1 vi的基本模式 35 2.2.2 vi的基本操作 36 2.2.3 vi的使用实例分析 40 2.3 嵌入式linux编译器gcc的使用 41 2.3.1 gcc概述 41 2.3.2 gcc编译流程分析 42 2.3.3 gcc警告提示 45 2.3.4 gcc使用库函数 47 2.3.5 gcc代码优化 49 2.4 嵌入式linux调试器gdb的使用 49 2.4.1 gdb使用实例 50 2.4.2 设置/删除断点 53 2.4.3 数据相关命令 54 2.4.4 调试运行环境相关命令 55 2.4.5 堆栈相关命令 55 2.5 make工程管理器 55 2.5.1 makefile基本结构 56 2.5.2 makefile变量 58 2.5.3 makefile规则 61 2.5.4 make使用 62 2.6 emacs综合编辑器 63 2.6.1 emacs的启动与退出 63 2.6.2 emacs的基本编辑 64 2.6.3 emacs的c模式 66 2.6.4 emacs的shell模式 69 本章小结 70 动手练练 70 第3章 构建嵌入式linux系统 71 3.1 嵌入式系统开发环境的构建 71 3.1.1 嵌入式交叉编译环境搭建 71 3.1.2 minicom和超级终端配置及使用 76 3.1.3 宿主机服务配置 83 3.2 bootloader 87 3.2.1 bootloader的概念 88 3.2.2 bootloader启动流程分析 89 3.2.3 u-boot概述 89 3.2.4 u-boot源码导读 90 3.3 编译嵌入式linux内核 91 3.4 linux内核目录结构 95 3.5 制作文件系统 95 本章小结 97 动手练练 97 第4章 嵌入式linux c语言基础——数据、表达式 98 4.1 嵌入式linux c语言概述 98 4.2 基本数据类型 100 4.2.1 整型家族 100 4.2.2 实型家族 102 4.2.3 字符型家族 103 4.2.4 枚举家族 104 4.2.5 指针家族 105 4.3 变量与常量 107 4.3.1 变量的定义 107 4.3.2 typedef 113 4.3.3 常量定义 114 4.3.4 arm-linux基本数据类型综合应用实例 115 4.4 运算符与表达式 118 4.4.1 算术运算符和表达式 119 4.4.2 赋值运算符和表达式 121 4.4.3 逗号运算符和表达式 123 4.4.4 位运算符和表达式 124 4.4.5 关系运算符和表达式 126 4.4.6 逻辑运算符和表达式 127 4.4.7 sizeof操作符 129 4.4.8 条件(?)运算符 130 4.4.9 运算符优先级总结 131 4.4.10 arm-linux运算符 综合实例 133 本章小结 137 动手练练 137 第5章 嵌入式linux c语言基础——控制语句及函数 138 5.1 嵌入式linux c语言程序结构概述 138 5.1.1 嵌入式linux c语言3种程序结构 138 5.1.2 嵌入式linux c语言基本语句 139 5.2 选择语句 142 5.2.1 if语句 142 5.2.2 switch语句 145 5.2.3 arm-linux选择语句应用实例 147 5.3 循环语句 148 5.3.1 while和do-while语句 148 5.3.2 for循环语句 149 5.3.3 break和continue语句 151 5.3.4 arm-linux循环语句应用实例 152 5.4 goto语句 154 5.4.1 goto语句语法 154 5.4.2 arm-linux中goto语句应用实例 154 5.5 函数的定义与声明 155 5.5.1 c语言函数概述 155 5.5.2 函数定义 157 5.5.3 函数声明 157 5.5.4 arm-linux函数定义与声明实例 158 5.6 函数的参数、值和基本调用 160 5.6.1 函数的参数 160 5.6.2 函数的值 161 5.6.3 函数的基本调用 161 5.7 函数的嵌套、递归调用 162 5.7.1 函数的嵌套调用 162 5.7.2 函数的递归调用 162 5.7.3 arm-linux函数调用应用实例 165 本章小结 167 动手练练 ..167 第6章 嵌入式linux c语言基础——数组、指针与结构 168 6.1 数组 169 6.1.1 一维数组 169 6.1.2 字符串 172 6.1.3 二维数组 174 6.2 指针 175 6.2.1 指针的概念 175 6.2.2 指针变量的操作 177 6.2.3 指针和数组 184 6.2.4 指针高级议题 191 6.3 结构体与联合 196 6.3.1 结构体 196 6.3.2 联合 200 6.3.3 arm-linux指针、结构体使用实例 201 本章小结 203 动手练练 203 第7章 嵌入式linux c语言基础——高级议题 204 7.1 预处理 204 7.1.1 预处理的概念 204 7.1.2 预定义 205 7.1.3 文件包含 211 7.1.4 条件编译 212 7.2 c语言中的内存分配 214 7.2.1 c语言程序所占内存分类 214 7.2.2 堆和栈的区别 215 7.3 嵌入式linux可移植性考虑 216 7.3.1 字长和数据类型 216 7.3.2 数据对齐 218 7.3.3 字节顺序 218 7.4 c和汇编的接口 219 7.4.1 内嵌汇编的语法 219 7.4.2 编译器优化介绍 221 7.4.3 c语言关键字volatile 222 7.4.4 memory描述符 222 7.4.5 gcc对内嵌汇编语言的处理方式 223 本章小结 224 动手练练 224 第8章 嵌入式linux c语言基础——arm linux内核常见数据结构 225 8.1 链表 226 8.1.1 链表概述 226 8.1.2 单向链表 226 8.1.3 双向链表 233 8.1.4 循环链表 234 8.1.5 arm linux中链表使用实例 235 8.2 树、二叉树、平衡树 237 8.2.1 树 237 8.2.2 二叉树 238 8.2.3 平衡树 245 8.2.4 arm linux中红黑树使用实例 247 8.3 哈希表 249 8.3.1 哈希表的概念及作用 249 8.3.2 哈希表的构造方法 250 8.3.3 哈希表的处理冲突方法 252 8.3.4 arm linux中哈希表使用实例 253 本章小结 255 动手练练 255 第9章 文件i/o相关实例 256 9.1 linux系统调用及用户编程接口(api) 257 9.1.1 系统调用 257 9.1.2 用户编程接口(api) 257 9.1.3 系统命令 258 9.2 arm linux文件i/o系统概述 258 9.2.1 虚拟文件系统(vfs) 258 9.2.2 通用文件模型 259 9.2.3 arm linux的设备文件 264 9.3 文件i/o操作 265 9.3.1 不带缓存的文件i/o操作 265 9.3.2 标准i/o开发 276 9.4 嵌入式linux串口应用开发 279 9.4.1 串口概述 279 9.4.2 串口设置详解 280 9.4.3 串口使用详解 284 本章小结 287 动手练练 287 第10章 arm linux进程线程开发实例 288 10.1 arm linux进程线程管理 289 10.1.1 进程描述符及任务结构 289 10.1.2 进程的调度 291 10.1.3 linux中的线程 293 10.1.4 linux中进程间通信 293 10.2 arm linux进程控制相关api 294 10.3 arm linux进程间通信api 301 10.3.1 管道通信 301 10.3.2 信号通信 303 10.3.3 共享内存 308 10.3.4 消息队列 309 10.4 arm linux线程相关api 312 10.5 linux守护进程 317 10.5.1 守护进程概述 317 10.5.2 编写规则 318 10.5.3 守护进程实例 319 本章小结 321 动手练练 321 第11章 arm linux网络开发实例 322 11.1 tcp/ip协议简介 322 11.1.1 tcp/ip的分层模型 322 11.1.2 tcp/ip分层模型特点 324 11.1.3 tcp/ip核心协议 325 11.2 网络基础编程 328 11.2.1 socket概述 328 11.2.2 地址及顺序处理 328 11.2.3 socket基础编程 333 11.3 web服务器 339 11.3.1 web服务器功能 339 11.3.2 web服务器协议 341 11.3.3 web服务器协议 342 11.3.4 运行web服务器 347 11.4 traceroute程序实例 347 11.4.1 traceroute原理简介 347 11.4.2 traceroute实例分析 348 11.4.3 traceroute实例运行结果 354 本章小结 354 动手练练 354 第12章 嵌入式linux设备驱动开发 355 12.1 设备驱动概述 355 12.1.1 设备驱动简介 355 12.1.2 设备驱动程序的特点 356 12.2 模块编程 357 12.2.1 模块编程简介 357 12.2.2 模块相关命令 357 12.2.3 模块编程流程 358 12.3 字符设备驱动编写 360 12.4 块设备驱动编写 369 12.4.1 块设备驱动程序描述符 369 12.4.2 块设备驱动编写流程 369 12.5 简单的skull驱动实例 375 12.5.1 驱动简介 375 12.5.2 驱动编写流程 376 12.5.3 结果分析 379 12.6 lcd驱动编写实例 379 12.6.1 lcd工作原理 379 12.6.2 lcd驱动实例 382 本章小结 389 动手练练 389 第13章 视频监控系统  390 13.1 视频监控系统概述 390 13.1.1 系统组成 390 13.1.2 音视频服务器 391 13.1.3 音视频客户端 392 13.1.4 通信传输控制协议 393 13.2 基本数据结构 395 13.3 功能实现 398 13.3.1 传输控制 398 13.3.2 用户检验 401 13.3.3 控制命令处理 403 13.3.4 云台转动控制 404 13.3.5 线程相关 407 本章小结 408 动手练练 ...408

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值