开发者
资源
使用GDB调试C/C++程序进程异常退出
使用GDB调试C/C++程序进程异常退出
发表于2023/10/19
1.7k29

定位思路

image.png

1、确认问题现象,是C/C++程序异常结束。

2、在修改编译脚本或者打开调试宏开关,重新编译可调试版程序。

3、打开coredump开关,以便后续重新异常结束能生成coredump文件。

4、运行程序,当程序异常结束时,在指定路径确认生成coredump文件。

5、使用GDB调试coredump文件,定位异常结束原因。

6、修改相关代码,重新编译进行验证。

      1)若问题解决,则确认修改,合入原代码。

      2)若问题未解决,重新运行生成coredump文件进行定位,若缺乏定位信息,可在代码中增加定位信息,重新编译运行。

案例:内存越界导致程序异常

问题现象:

某软件在服务器上运行出现异常退出问题。

定位过程:

1、使用top查看,确认相关进程已结束,确认问题是异常退出。

2、在makefile里增加-g编译选项,重新编译代码。

3、在操作系统打开core文件生成功能,设置core文件生成路径。

ulimit -c unlimited
echo "/home/core.%e.%p.%t" > /proc/sys/kernel/core_pattern

4、运行程序,程序异常结束后,在指定路径生成coredump文件core.dsa_sign_multi.xxx.xxx。

5、通过GDB调试coredump文件,进入调试界面,如下图所示。

gdb dsa_sign_multi core.dsa_sign_multi.xxx.xxx

image.png

6、通过info threads命令查看该进程的线程信息。

image.png

7、通过thread命令切换想要查询的线程号,通过bt命令查看线程堆栈。

thread $ID
bt

image.png

8、分析堆栈信息,定位到问题在于调用OpenSSL相关库,但由于系统使用的OpenSSL非调试版本,无法看到OpenSSL的堆栈信息。需要重新编译OpenSSL可调试版本。

9、重新编译可调试版本的OpenSSL,并让程序链接,重新运行程序,生成新的coredump文件,并调试,查看相关堆栈信息。

image.png

10、查看报错堆栈相关参数信息。

info locals
f xx

image.png

image.png

11、查看相关代码源码实现。

image.png

“MD_Update()”函数是对“EVP_DigestUpdate()”函数的宏别名。

image.png

12、找到“EVP_DigestUpdate()”函数实现:

image.png

该函数的第三个参数应该传入无符号整型。

第三个参数是(MD_DIGEST_LENGTH / 2 – k),而k由(st_idx + MD_DIGEST_LENGTH / 2)计算得到。而MD_DIGEST_LENGTH 是常量,st_idx是变量。

13、查看代码实现,分析st_idx是由全局变量state_index拷贝到st_idx,随后对state_index累加并转换防止超出STATE_SIZE。

image.png

14、正常的流程中st_idx并不会超出STATE_SIZE,但是在多线程环境下,有低概率出现state_index累加之后未来得及转换就被其他线程使用,导致超出STATE_SIZE,这导致(MD_DIGEST_LENGTH / 2 – k)计算结果为负。传入EVP_DigestUpdate()后,被按照无符号数进行计算,导致越界,触发coredump问题。

15、对静态变量state_index加锁保护,重新编译代码,验证,未出现问题,确认修改合入,问题解决。

收藏举报
Level 1
0
帖子
0
粉丝
0
获赞