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