> 文章列表 > openssl 内存泄露排查

openssl 内存泄露排查

openssl 内存泄露排查

使用VS自带宏进行排查

#define _CRTDBG_MAP_ALLOC //此宏需要在之前<crtdbg.h>
#include <crtdbg.h>
在代码的开头加上
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
这种方式只能定位到函数地址。

使用openssl自带的函数,开启内存检测时,所有的内存分配会被记录。

内存管理函数
这些函数要起作用,需要在编译openssl的时候开启debug选项

主要函数

  1. CRYPTO_mem_ctrl

本函数主要用于控制内存分配时,是否记录内存信息。如果不记录内存信息,将不能查找内存泄露。开启内存记录调用CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON),关闭内存记录调用CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF)。一旦CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON)被调用,直到用户调用CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF)前,用户 所有的opessl内存分配都会被记录。

  1. CRYPTO_is_mem_check_on

查询内存记录标记是否开启。

3) CRYPTO_dbg_malloc

本函数用于分配内存空间,如果内存记录标记开启,则记录用户申请的内存。当需要记录内存信息时,该函数本身也需要申请内存插入哈希表,为了防止递归申请错误,它申请内存记录信息前必须暂时关闭内存记录标记,申请完毕再放开。

4) CRYPTO_dbg_free

释放内存,如果内存记录标记开启,还需要删除哈希表中对应的记录。

5) CRYPTO_mem_leaks

将内存泄露输出到BIO中。

6) CRYPTO_mem_leaks_fp

将内存泄露输出到FILE中(文件或者标准输出),该函数调用了CRYPTO_mem_leaks。

7) CRYPTO_mem_leaks_cb

处理内存泄露,输入参数为用户自己实现的处理内存泄露的函数地址。该函数只需要处理一个内存泄露,openssl通过lh_doall_arg调用用户函数来处理所有记录(泄露的内存)。

使用列子


#define _CRT_SECURE_NO_WARNINGS
//#define _CRTDBG_MAP_ALLOC  //此宏需要在之前<crtdbg.h>
//#include <crtdbg.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>//#ifdef _DEBUG
//#ifndef DBG_NEW
//#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
//#define new DBG_NEW
//#endif
//
//#endif // _DEBUGvoid InitLeaks(){CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
}
void LogLeaks()
{CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);BIO* bLog = BIO_new_file("D:/leak.log", "w"); //临时关闭。CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);//leaks会自动调用CRYPTO_MEM_CHECK_OFFCRYPTO_mem_leaks(bLog);//内部有线程锁,调用此方法后,锁被释放,不能再调用CRYPTO_mem_ctrl方法了。BIO_free(bLog);
}int testcertreq2();
//https://blog.csdn.net/yinxu2008/article/details/120242857
int main() {//windows内存泄露问题//_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//OPENSSL内存泄露查找//https://www.bbsmax.com/A/1O5EZM1yd7/InitLeaks();testcertreq2();LogLeaks();return 0;
}int testcertreq2() {X509_REQ* req = NULL;RSA* rsa = NULL;BIGNUM* bn = NULL;int ret = 1;EVP_PKEY* key = NULL;X509_NAME* name = NULL;X509_NAME_ENTRY* entry2 = NULL, * entry = NULL;do {req = X509_REQ_new();X509_REQ_set_version(req, 1);//设置版本//生成RSA秘钥对rsa = RSA_new();bn = BN_new();BN_set_word(bn, RSA_3);ret = RSA_generate_key_ex(rsa, 1024, bn, NULL);if (ret != 1) {printf("RSA_generate_key_ex error");break;}key = EVP_PKEY_new();ret = EVP_PKEY_set1_RSA(key, rsa);if (ret != 1) {printf("EVP_PKEY_set1_RSA error");break;}//设置签名公钥ret = X509_REQ_set_pubkey(req, key);name = X509_NAME_new();const unsigned char cn[] = { "fdsfsdfsfsf" };entry = X509_NAME_ENTRY_create_by_NID(NULL, NID_commonName, V_ASN1_UTF8STRING, cn, sizeof(cn) - 1);if (entry == NULL) {printf("create NID_commonName entry error");break;}ret = X509_NAME_add_entry(name, entry, 0, 1);const unsigned char country[] = { "China" };entry2 = X509_NAME_ENTRY_create_by_NID(NULL, NID_countryName, V_ASN1_UTF8STRING, country, sizeof(country) - 1);if (entry2 == NULL) {printf("create NID_commonName entry error");break;}ret = X509_NAME_add_entry(name, entry2, 0, 1);//设置证书请求者信息ret = X509_REQ_set_subject_name(req, name);//成功返回签名的字节数,失败返回0int len = X509_REQ_sign(req, key, EVP_sha1());if (len == 0) {ret = -1;printf("X509_REQ_sign error");break;}//输出成PEM格式ret = PEM_write_X509_REQ(stdout, req);if (ret != 1) {printf("PEM_write_X509_REQ error");break;}//验证证书请求ret = X509_REQ_verify(req, key);if (ret != 1) {printf("X509_REQ_verify error");break;}} while (0);//释放你所有NEW的对象BN_free(bn);RSA_free(rsa);EVP_PKEY_free(key);X509_NAME_free(name);X509_REQ_free(req);X509_NAME_ENTRY_free(entry);X509_NAME_ENTRY_free(entry2);return ret;
}