定位思路

1、确认问题现象,是C/C++程序异常结束。
2、在修改编译脚本或者打开调试宏开关,重新编译可调试版程序。
3、打开coredump开关,以便后续重新异常结束能生成coredump文件。
4、运行程序,当程序异常结束时,在指定路径确认生成coredump文件。
5、使用GDB调试coredump文件,定位异常结束原因。
6、修改相关代码,重新编译进行验证。
1)若问题解决,则确认修改,合入原代码。
2)若问题未解决,重新运行生成coredump文件进行定位,若缺乏定位信息,可在代码中增加定位信息,重新编译运行。
案例:内存越界导致程序异常
问题现象:
某软件在服务器上运行出现异常退出问题。
定位过程:
1、使用top查看,确认相关进程已结束,确认问题是异常退出。
2、在makefile里增加-g编译选项,重新编译代码。
3、在操作系统打开core文件生成功能,设置core文件生成路径。
4、运行程序,程序异常结束后,在指定路径生成coredump文件core.dsa_sign_multi.xxx.xxx。
5、通过GDB调试coredump文件,进入调试界面,如下图所示。

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

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

8、分析堆栈信息,定位到问题在于调用OpenSSL相关库,但由于系统使用的OpenSSL非调试版本,无法看到OpenSSL的堆栈信息。需要重新编译OpenSSL可调试版本。
9、重新编译可调试版本的OpenSSL,并让程序链接,重新运行程序,生成新的coredump文件,并调试,查看相关堆栈信息。

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


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

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

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

该函数的第三个参数应该传入无符号整型。
第三个参数是(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。

14、正常的流程中st_idx并不会超出STATE_SIZE,但是在多线程环境下,有低概率出现state_index累加之后未来得及转换就被其他线程使用,导致超出STATE_SIZE,这导致(MD_DIGEST_LENGTH / 2 – k)计算结果为负。传入EVP_DigestUpdate()后,被按照无符号数进行计算,导致越界,触发coredump问题。
15、对静态变量state_index加锁保护,重新编译代码,验证,未出现问题,确认修改合入,问题解决。
定位思路
1、确认问题现象,是C/C++程序异常结束。
2、在修改编译脚本或者打开调试宏开关,重新编译可调试版程序。
3、打开coredump开关,以便后续重新异常结束能生成coredump文件。
4、运行程序,当程序异常结束时,在指定路径确认生成coredump文件。
5、使用GDB调试coredump文件,定位异常结束原因。
6、修改相关代码,重新编译进行验证。
1)若问题解决,则确认修改,合入原代码。
2)若问题未解决,重新运行生成coredump文件进行定位,若缺乏定位信息,可在代码中增加定位信息,重新编译运行。
案例:内存越界导致程序异常
问题现象:
某软件在服务器上运行出现异常退出问题。
定位过程:
1、使用top查看,确认相关进程已结束,确认问题是异常退出。
2、在makefile里增加-g编译选项,重新编译代码。
3、在操作系统打开core文件生成功能,设置core文件生成路径。
4、运行程序,程序异常结束后,在指定路径生成coredump文件core.dsa_sign_multi.xxx.xxx。
5、通过GDB调试coredump文件,进入调试界面,如下图所示。
6、通过info threads命令查看该进程的线程信息。
7、通过thread命令切换想要查询的线程号,通过bt命令查看线程堆栈。
8、分析堆栈信息,定位到问题在于调用OpenSSL相关库,但由于系统使用的OpenSSL非调试版本,无法看到OpenSSL的堆栈信息。需要重新编译OpenSSL可调试版本。
9、重新编译可调试版本的OpenSSL,并让程序链接,重新运行程序,生成新的coredump文件,并调试,查看相关堆栈信息。
10、查看报错堆栈相关参数信息。
11、查看相关代码源码实现。
“MD_Update()”函数是对“EVP_DigestUpdate()”函数的宏别名。
12、找到“EVP_DigestUpdate()”函数实现:
该函数的第三个参数应该传入无符号整型。
第三个参数是(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。
14、正常的流程中st_idx并不会超出STATE_SIZE,但是在多线程环境下,有低概率出现state_index累加之后未来得及转换就被其他线程使用,导致超出STATE_SIZE,这导致(MD_DIGEST_LENGTH / 2 – k)计算结果为负。传入EVP_DigestUpdate()后,被按照无符号数进行计算,导致越界,触发coredump问题。
15、对静态变量state_index加锁保护,重新编译代码,验证,未出现问题,确认修改合入,问题解决。