---
title: C/C++进程异常结束
description: "C/C++进程未按设计要求进行执行，出现异常结束的情况。定位思路如图1所示。图1 C/C++进程异常结束问题定位思路"
url: https://www.hikunpeng.com/document/detail/zh/kunpenggrf/ref/kunpenggrffaq_10_0022.html
sourcePath: /source/zh/kunpenggrf/ref/kunpenggrffaq_10_0022.html
indexId: 6c4d63f0ef8464227d74410ded15d06e77723c580a3e9ab2451910578e96875d52
---
# C/C++进程异常结束

#### 定位思路

C/C++进程未按设计要求进行执行，出现异常结束的情况。定位思路如图1所示。图1 C/C++进程异常结束问题定位思路


1. 确认问题现象，是C/C++程序异常结束。
2. 在修改编译脚本或者打开调试宏开关，重新编译可调试版程序。
3. 打开coredump开关，以便后续重新异常结束能生成coredump文件。
4. 运行程序，当程序异常结束时，在指定路径确认生成coredump文件。
5. 使用GDB调试coredump文件，定位异常结束原因。
6. 修改相关代码，重新编译进行验证。

  - 若问题解决，则确认修改，合入原代码。
  - 若问题未解决，重新运行生成coredump文件进行定位，若缺乏定位信息，可在代码中增加定位信息，重新编译运行。


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

问题现象：

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

定位过程：

1. 使用top查看，确认相关进程已结束，确认问题是异常退出。
2. 在makefile里增加-g编译选项，重新编译代码。
3. 在操作系统打开core文件生成功能，设置core文件生成路径。
  1 2 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文件，进入调试界面，如下图所示。
  1 gdb dsa_sign_multi core.dsa_sign_multi.xxx.xxx

6. 通过info threads命令查看该进程的线程信息。
7. 通过thread命令切换想要查询的线程号，通过bt命令查看线程堆栈。
  1 2 thread $ID bt

8. 分析堆栈信息，定位到问题在于调用OpenSSL相关库，但由于系统使用的OpenSSL非调试版本，无法看到OpenSSL的堆栈信息。需要重新编译OpenSSL可调试版本。
9. 重新编译可调试版本的OpenSSL，并让程序链接，重新运行程序，生成新的coredump文件，并调试，查看相关堆栈信息。
10. 查看报错堆栈相关参数信息。
  1 2 info locals f xx

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加锁保护，重新编译代码，验证，未出现问题，确认修改合入，问题解决。
