openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法

概述

调用openssl接口后, 如果用到了openssl对象, 需要释放, 否则会发生内存泄漏.
即使不是新手, 也不能保证释放函数都调用了. 想想我们自己写程序, new后, 没有delete的情况就知道, 可以理解.
谁能保证自己手搓的应用实现100%没内存泄漏呢?

看资料时, 发现openssl本身有这个检查库本身发生内存泄漏的特性, 大概就是申请内存时, openssl自己记录了一下, free内存时, 将对应记录删掉.
这样, 在程序退出前, 再调用一下内存分配记录列表接口, 就知道哪里的内存没释放.
那试试, 加入crypto-mdebug特性, 模拟一下内存泄漏(调用openssl_new(), 不调用openssl_free()), 看看啥效果.

笔记

查看特性列表

perl configdata.pm --dump > my_log.txt

查看my_log.txt, 就有openssl 特性列表.
有启用的特性列表, 也有被禁掉的特性列表.
如果要加入特性, 就看禁止列表中的特性.
在这里插入图片描述
怎么打开crypto-mdebug特性呢?
看Configure可知, 只要带上参数 enable-crypto-mdebug即可.
在这里插入图片描述
结合我最后实验可用的编译脚本, 加入 enable-crypto-mdebug

openssl3.2编译脚本 - 加入enable-crypto-mdebug

解开官方源码包

打开vs2019x64本地命令行, 选择管理员身份运行

cd /d D:\3rd_prj\crypt\openssl-3.2.0

set path=c:\nasm;%path%

perl Configure VC-WIN64A --debug enable-crypto-mdebug zlib-dynamic --with-zlib-include=D:\my_dev\lib\zlib_1d3 --with-zlib-lib=.\my_zlib_1d3.dll --prefix=c:\openssl_3d2 --openssldir=c:\openssl_3d2\common

nmake

手工拷贝, 将 my_zlib_1d3.dll 拷贝到以下4个目录
.\
.\apps
.\fuzz
.\test

nmake test

nmake install

手工拷贝
D:\my_dev\lib\zlib_1d3\my_zlib_1d3.dll => C:\openssl_3d2\bin\my_zlib_1d3.dll

归档
C:\openssl_3d2 剪切到自己的库目录 => D:\my_dev\lib\openssl_3d2

写个测试程序, 调用一下内存泄漏检查的相关接口, 看看能否编译过, 然后试试接口怎么用.

/*!
* \file main.cpp
*/

#include "my_openSSL_lib.h"

#include <openssl/crypto.h> // for mem leak API

int main(int argc, char** argv)
{

	CRYPTO_mem_leaks(NULL);
	return 0;
}

/*!
编译错误
已启动重新生成…
1>------ 已启动全部重新生成: 项目: prj_template, 配置: Debug x64 ------
1>main.cpp
1>D:\my_dev\my_local_git_prj\study\openSSL\exp\call_mem_leak_API\main.cpp(12,2): error C4996: 'CRYPTO_mem_leaks': Since OpenSSL 3.0
1>已完成生成项目“prj_template.vcxproj”的操作 - 失败。
========== 全部重新生成: 成功 0 个,失败 1 个,跳过 0 个 ==========
*/


直接编译不过…
看官方说明 file:///D:/3rd_prj/crypt/openssl-3.2.0/doc/html/man3/OPENSSL_malloc.html

The following functions have been deprecated since OpenSSL 3.0, and can be hidden entirely by defining OPENSSL_API_COMPAT with a suitable version value, see openssl_user_macros(7):

 int CRYPTO_mem_leaks(BIO *b);
 int CRYPTO_mem_leaks_fp(FILE *fp);
 int CRYPTO_mem_leaks_cb(int (*cb)(const char *str, size_t len, void *u),
                         void *u);
 
 int CRYPTO_set_mem_debug(int onoff);
 int CRYPTO_mem_ctrl(int mode);
 int OPENSSL_mem_debug_push(const char *info);
 int OPENSSL_mem_debug_pop(void);
 int CRYPTO_mem_debug_push(const char *info, const char *file, int line);
 int CRYPTO_mem_debug_pop(void);
DESCRIPTION

看到官方说, 这些内存诊断函数已经弃用了, 用clang的检查代替(忘了是哪个文档了, 反正是官方文档中说的).
去看内存诊断函数的实现, 都是空的, 官方确实已经弃用了.
在这里插入图片描述

尝试将vs2019的工具链改为clang, 看不到效果, 且不能单步进入库函数内部.
在这里插入图片描述
用clang工具链编译, 可以编译过, 也可以运行, 不过无法进入调试版的pdb实现中.

已启动重新生成…
1>------ 已启动全部重新生成: 项目: prj_template, 配置: Debug x64 ------
1>main.cpp(12,2): warning : 'CRYPTO_mem_leaks' is deprecated: Since OpenSSL 3.0 [-Wdeprecated-declarations]
1>D:\my_dev\lib\openssl_3d2\include\openssl/crypto.h(411,1): message : 'CRYPTO_mem_leaks' has been explicitly marked deprecated here
1>D:\my_dev\lib\openssl_3d2\include\openssl/macros.h(194,49): message : expanded from macro 'OSSL_DEPRECATEDIN_3_0'
1>D:\my_dev\lib\openssl_3d2\include\openssl/macros.h(44,22): message : expanded from macro 'OSSL_DEPRECATED'
1>prj_template.vcxproj -> D:\my_dev\my_local_git_prj\study\openSSL\exp\call_mem_leak_API\x64\Debug\prj_template.exe
1>'pwsh.exe' 不是内部或外部命令,也不是可运行的程序
1>或批处理文件。
1>已完成生成项目“prj_template.vcxproj”的操作。
========== 全部重新生成: 成功 1 个,失败 0 个,跳过 0==========

那就不用clang来编译.

看看有没有替代内存诊断的方法?

找到一个opensslAPI CRYPTO_get_alloc_counts(), 可以取当前内存分配次数.
将这个API封装一下, 卡在openssl应用函数外边, 就可以间接的知道是否有内存泄漏, 如果有opensslAPI调用引起的内存泄漏, 可以迅速的缩小排查范围.

main.cpp

/*!
* \file main.cpp
*/

#include "my_openSSL_lib.h"

#include <openssl/crypto.h>

bool is_OSSL_mem_leak();
void test_mem_leak(bool b_have_mem_leak);

int main(int argc, char** argv)
{
	openssl_mdebug_begin();
	test_mem_leak(true);
	openssl_mdebug_end(true, false); // 如果需要断言, 参数2为true

	openssl_mdebug_begin();
	test_mem_leak(false);
	openssl_mdebug_end(true, true);

	/*
	run result

	test_mem_leak(test have mem leak)
	>> malloc_count = 0, realloc_count = 0, free_count = 0
	<< malloc_count = 1, realloc_count = 0, free_count = 0
	err : !!! mem leak happen
	test_mem_leak(test no mem leak)
	*/
	
	return 0;
}

void test_mem_leak(bool b_have_mem_leak)
{
	void* pBuf = NULL;

	printf("test_mem_leak(%s)\n", (b_have_mem_leak ? "test have mem leak" : "test no mem leak"));

	pBuf = OPENSSL_malloc(2);

	// do some task ...

	if (!b_have_mem_leak)
	{
		OPENSSL_free(pBuf);
		pBuf = NULL;
	}
}

my_openSSL_lib.h

/*!
\file my_openSSL_lib.h
*/

#ifndef __MY_OPENSSL_LIB_H__
#define __MY_OPENSSL_LIB_H__

#ifdef __cplusplus
extern "C" {
#endif


#ifdef  _WIN32
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") // for select()

#include <windows.h>

#include <stdbool.h>

#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")

#endif /* #ifdef  _WIN32 */

// --------------------------------------------------------------------------------
// 开关宏 - begin
// --------------------------------------------------------------------------------

#define MY_USE_APPLINK

// --------------------------------------------------------------------------------
// 开关宏 - END
// --------------------------------------------------------------------------------

typedef struct _tag_openssl_mem_counter{
	int malloc_count_begin;
	int realloc_count_begin;
	int free_count_begin;

	int malloc_count_end;
	int realloc_count_end;
	int free_count_end;
} TAG_OPENSSL_MEM_COUNTER;

void openssl_mdebug_begin();
bool openssl_mdebug_end(bool show_tip, bool b_assert);

#ifdef __cplusplus
}
#endif

#endif /* #ifndef __MY_OPENSSL_LIB_H__ */

my_openSSL_lib.c

/*!
* \file D:\my_dev\my_local_git_prj\study\openSSL\nmake_test\test_c\prj_005_afalgtest.c\my_openSSL_lib.c
*/

#include "my_openSSL_lib.h"
#include "openssl/crypto.h" // for CRYPTO_get_alloc_counts

#include <assert.h>

#ifdef MY_USE_APPLINK
#include <openssl/applink.c> /*! for OPENSSL_Uplink(00007FF8B7EF0FE8,08): no OPENSSL_Applink */
#endif // #ifdef MY_USE_APPLINK

static TAG_OPENSSL_MEM_COUNTER gs_openssl_mdebug;

void openssl_mdebug_begin()
{
	CRYPTO_get_alloc_counts(&gs_openssl_mdebug.malloc_count_begin, &gs_openssl_mdebug.realloc_count_begin, &gs_openssl_mdebug.free_count_begin);
}

bool openssl_mdebug_end(bool show_tip, bool b_assert)
{
	bool b_rc = false;
	long lCntBegin = 0;
	long lCntEnd = 0;

	CRYPTO_get_alloc_counts(&gs_openssl_mdebug.malloc_count_end, &gs_openssl_mdebug.realloc_count_end, &gs_openssl_mdebug.free_count_end);
	lCntBegin = gs_openssl_mdebug.malloc_count_begin + gs_openssl_mdebug.realloc_count_begin - gs_openssl_mdebug.free_count_begin;
	lCntEnd = gs_openssl_mdebug.malloc_count_end + gs_openssl_mdebug.realloc_count_end - gs_openssl_mdebug.free_count_end;
	b_rc = (lCntBegin == lCntEnd);
	if (!b_rc && show_tip)
	{
		printf(">> malloc_count = %d, realloc_count = %d, free_count = %d\n", gs_openssl_mdebug.malloc_count_begin, gs_openssl_mdebug.realloc_count_begin, gs_openssl_mdebug.free_count_begin);
		printf("<< malloc_count = %d, realloc_count = %d, free_count = %d\n", gs_openssl_mdebug.malloc_count_end, gs_openssl_mdebug.realloc_count_end, gs_openssl_mdebug.free_count_end);
		printf("err : !!! mem leak happen\n");
	}

	if (b_assert)
	{
		assert(true == b_rc);
	}

	return b_rc;
}

备注

以后有机缘再查查怎么用clang来查openssl应用是否有内存泄漏.
查资料时, 很多情况下都不是想查就能查到的.
很多时候, 是心里带着问题, 查其他资料时, 突然给了启发, 才将以前的问题搞掉.

这种调用CRYPTO_get_alloc_counts()来间接的排查是否调用opensslAPI时, 是否没有成对的分配,释放内存.
没有那么直接和方便, 不过也算是一种方法了. 有总比没有强.

希望以后能找到其他更好的方法来定位opensslAPI调用时的内存泄漏点.

备注

openssl的编译检查, 测试用例还是很严格的.
对外提供的API, 都有测试程序.
CRYPTO_get_alloc_counts()这个API的调用, 也能找到至少一处.
在这里插入图片描述

用SI将openssl源码编译的目录中的能识别的东东都包进来搜索, 必然能看到测试用例或者API调用的痕迹.
如果某个API虽然定义, 但是官方源码编译目录的实现中都没有用到, 那么咱么就不能用这个API.
在这里插入图片描述

这招不行啊

今天正好写个测试程序, 用到了这种内存泄漏检测方法. 有断言, 不好使.

int main(int argc, char** argv)
{
	BIO* bio = NULL;

	openssl_mdebug_begin();
    test_bio_mem_leak();
    // test_bio_new_mem_buf();
	openssl_mdebug_end(false, true);

	return 0;
}

void test_bio_mem_leak(void)
{
    BIO* bio = BIO_new_mem_buf("Hello World\n", 12);
    BIO_free(bio);
}

在这里插入图片描述
只能先关注这事, 以后再想办法了.
跟了一下openssl代码, 是产生默认库上下文中有很多openssl_malloc()引起的内存分配.
后续再看看, 自己显势调用产生销毁默认上下文的API, 是否可以继续用这种方法.

显势调用默认上下文也不行

int main(int argc, char** argv)
{
    OSSL_LIB_CTX* _ossl_lib_ctx = NULL;
	BIO* bio = NULL;

    do {
        openssl_mdebug_begin();
        _ossl_lib_ctx = OSSL_LIB_CTX_get0_global_default();
        if (NULL == _ossl_lib_ctx)
        {
            assert(false);
            break;
        }

        test_bio_mem_leak();
        // test_bio_new_mem_buf();

        OSSL_LIB_CTX_free(_ossl_lib_ctx);
        openssl_mdebug_end(true, false); // 到这里, 还是报错
    } while (false);

	return 0;
}

void test_bio_mem_leak(void)
{
    BIO* bio = BIO_new_mem_buf("Hello World\n", 12);
    BIO_free(bio);
}

报错原因, openssl函数调用中, 会有其他默认的上下文建立会调用内存分配.
具体是啥函数, 要去单步.
这种用内存分配计数的方法, 不能真实的间接观察到openssl内部的内存泄漏, 做了一个没用的实验…

还是要老老实实的看官方例子, 手工调用对应API的释放函数.

找到一种还可以的解决方法, 现在看来可以准确观测到openssl内存泄漏点

openssl设计的可以, 预留了很多有用的API.
翻翻openssl提供的API, 找到了以下有用的API.

CRYPTO_get_mem_functions()
CRYPTO_set_mem_functions()
OPENSSL_init_crypto()
OPENSSL_cleanup()

这些可以用于辅助观测openssl内存泄漏点, 不止是openssl API, openssl内部发生的内存泄漏也能观测到(如果有的话)

内存泄漏观测的程序实现

main.cpp

/*!
* \file main.cpp
*/

#include "my_openSSL_lib.h"
#include <openssl/crypto.h>
#include <openssl/bio.h>

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "CMemHookRec.h"

void test_bio_mem_leak(bool b_have_mem_leak);

int main(int argc, char** argv)
{
	setvbuf(stdout, NULL, _IONBF, 0); // 清掉stdout缓存, 防止调用printf时阻塞

	// mem IF hook, 必须在执行任何openssl API之前替换
	mem_hook();

	// 自己的openssl接口应用
	test_bio_mem_leak(true);
	test_bio_mem_leak(false);

	// mem IF un hook, 必须在openssl结束后, 再恢复原始函数
	mem_unhook();

	/* run result, 如果有openssl应用接口没释放引起的内存泄漏, 有断言, stdout有输出
	一个openssl API引起的内存泄漏可能对应这多条内存分配记录, 不过排查范围缩小多了, 线索也清晰多了 
	结合对自己的openssl调用代码块的注释排除法, 很容易找到自己哪里没调用正确的openssl释放API.
		
	free map, g_mem_hook_map.size() = 4
	2AAA2F36560 128 crypto\bio\bio_lib.c:83
	2AAA2F4EC40 16 crypto\bio\bss_mem.c:111
	2AAA2FEA470 32 crypto\bio\bss_mem.c:119
	2AAA2FEAA10 32 crypto\buffer\buffer.c:35
	error : !!! find openssl call memory leak
	Assertion failed: false, file D:\my_dev\my_local_git_prj\soft\xx\src\case\exp002_mem_hook\main.cpp, line 57
	*/
	
	return 0;
}

void test_bio_mem_leak(bool b_have_mem_leak)
{
	BIO* bio = BIO_new_mem_buf("Hello World\n", 12);

	if (false == b_have_mem_leak)
	{
		BIO_free(bio);
	}
}

找内存泄漏时很方便, 只要在程序入口处调用mem_hook(), 然后自己正常调用openssl API, 程序结束前调用mem_unhook()
如果有openssl 释放API没调用引起的内存泄漏, 就会有断言.
如果是控制台程序, 就可以直接看到内存泄漏点.
如果不是控制台程序, 将mem_unhook()中的内存泄漏改写到其他输出(e.g. 文件).
这排查起来就容易多了.

CMemHookRec.h

/*!
\file CMemHookRec.h
\note openssl 分配API hook 之后, 记录的内存分配记录
*/

#ifndef __CMEMHOOKREC_H__
#define __CMEMHOOKREC_H__

void mem_hook();
void mem_unhook();

#endif // #ifndef __CMEMHOOKREC_H__

CMemHookRec.cpp

/// \file CMemHookRec.cpp

#include "pch.h"

#include <cstdint>
#include <stdio.h>
#include <map>
#include <openssl/crypto.h>

#include "CMemHookRec.h"
#include <assert.h>

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) \
do { \
    if (NULL != (p)) { \
        delete (p);\
    } \
    (p)=NULL; \
} while (0);
#endif // #ifndef SAFE_DELETE

class CMemHookRec
{
public:
	void show_info();

public:
	uint64_t u64_addr;
	size_t num;
	const char* file;
	int line;

	uint64_t rec_sn;
};

static std::map<uint64_t, CMemHookRec*> g_mem_hook_map;
typedef std::pair<uint64_t, CMemHookRec*> Map_Pair_mem_hook;
typedef std::map<uint64_t, CMemHookRec*>::iterator It_mem_hook;

// 		malloc_fn_org	0x00007ffb0796614f {libcrypto-3-x64.dll!CRYPTO_malloc}	void *(*)(unsigned __int64, const char *, int)
static CRYPTO_malloc_fn malloc_fn_org = NULL;

// 		realloc_fn_org	0x00007ffb07967a1d {libcrypto-3-x64.dll!CRYPTO_realloc}	void *(*)(void *, unsigned __int64, const char *, int)
static CRYPTO_realloc_fn realloc_fn_org = NULL;

// 		free_fn_org	0x00007ffb079631b1 {libcrypto-3-x64.dll!CRYPTO_free}	void(*)(void *, const char *, int)
static CRYPTO_free_fn free_fn_org = NULL;

static OPENSSL_INIT_SETTINGS* gp_ossl_init_setting = NULL;
static uint64_t g_u64_malloc_cnt_all = 0;

static void* my_CRYPTO_malloc(size_t num, const char* file, int line);
static void* my_CRYPTO_realloc(void* addr, size_t num, const char* file, int line);
static void my_CRYPTO_free(void* ptr, const char* file, int line);
static bool free_map();
static std::wstring my_A2W(std::string str);

void CMemHookRec::show_info()
{
#ifdef _MFC_VER
	TRACE(TEXT("entry sn = [%I64d],  %I64X %zu %s:%d\n"), this->rec_sn, u64_addr, num, ((NULL != file) ? my_A2W(file).data() : TEXT("NULL")), line);
#else
	printf("entry sn = [%I64d],  %I64X %zu %s:%d\n", this->rec_sn, u64_addr, num, ((NULL != file) ? file : "NULL"), line);
#endif // #ifdef _MFC_VER
}

void mem_hook()
{
	int i_rc = 0;
	uint64_t u64_init_opt = 0;

	CRYPTO_get_mem_functions(&malloc_fn_org, &realloc_fn_org, &free_fn_org);
	CRYPTO_set_mem_functions(my_CRYPTO_malloc, my_CRYPTO_realloc, my_CRYPTO_free);

	// openssl init all
	gp_ossl_init_setting = OPENSSL_INIT_new(); // 必须用OPENSSL_INIT_free()释放, 否则有24个字节的泄漏

	u64_init_opt = OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
		OPENSSL_INIT_ADD_ALL_CIPHERS |
		OPENSSL_INIT_ADD_ALL_DIGESTS |
		OPENSSL_INIT_LOAD_CONFIG |
		OPENSSL_INIT_ASYNC |
		OPENSSL_INIT_NO_ATEXIT;

	// 将openssl库中的静态初始化函数显势调用一下, 免得在我们手工调用API时(因为有些openssl对象或变量没初始化)自动调用静态初始化函数引起多余的非我们应用产生的内存分配
	// 初始化函数在应用中只能调用一次.
	i_rc = OPENSSL_init_crypto(u64_init_opt, gp_ossl_init_setting);
	assert(1 == i_rc);
}

void mem_unhook()
{
	// openssl uninit all
	// 只能调用一次
	OPENSSL_cleanup();

	// 必须在openssl cleanup之后调用map显示, 这样才能过滤掉系统自动分配的内存.
	// 如果map不为空, 这些记录才是我们自己应用没释放的内存
	// 在free_map()时, 如果看到多条未被释放的内存, 也一定是那么多个API调用没释放, 因为一条openssl API会对应者多个openssl_mallc(), e.g. BIO_new_mem_buf() 就有4个内存分配动作.
	if (!free_map())
	{
		printf("error : !!! find openssl call memory leak\r\n");
		assert(false);
		/* run result
		free map, g_mem_hook_map.size() = 4
		2AAA2F36560 128 crypto\bio\bio_lib.c:83
		2AAA2F4EC40 16 crypto\bio\bss_mem.c:111
		2AAA2FEA470 32 crypto\bio\bss_mem.c:119
		2AAA2FEAA10 32 crypto\buffer\buffer.c:35
		error : !!! find openssl call memory leak
		Assertion failed: false, file D:\my_dev\my_local_git_prj\soft\krgy_software_protect\src\case\exp002_mem_hook\main.cpp, line 57
		*/
	}

	if (NULL != gp_ossl_init_setting)
	{
		OPENSSL_INIT_free(gp_ossl_init_setting);
		gp_ossl_init_setting = NULL;
	}

	CRYPTO_set_mem_functions(malloc_fn_org, realloc_fn_org, free_fn_org);
}

static std::wstring my_A2W(std::string str)
{
	USES_CONVERSION;
	std::wstring str_rc = A2W(str.c_str());
	return str_rc;
}

static bool free_map()
{
	It_mem_hook it = g_mem_hook_map.end();
	size_t size = g_mem_hook_map.size();

#ifdef _MFC_VER
	TRACE(TEXT("free mem_hook map, g_mem_hook_map.size() = %zd, %s openssl API call memory leak\n"), size, ((size > 0) ? TEXT("have") : TEXT("no")));
#else
	printf("free mem_hook map, g_mem_hook_map.size() = %zd, %s openssl API call memory leak\n", size, ((size > 0) ? "have" : "no"));
#endif // #ifdef _AFXDLL

	for (it = g_mem_hook_map.begin(); it != g_mem_hook_map.end(); it++) {
		if (NULL != it->second) {
			it->second->show_info();
		}
		SAFE_DELETE(it->second);
	}
	g_mem_hook_map.clear();

	return (0 == size);
}

static void* my_CRYPTO_malloc(size_t num, const char* file, int line)
{
	void* p = NULL;
	It_mem_hook it;
	CMemHookRec* rec = NULL;

	p = malloc(num);
	if (NULL != p)
	{
		it = g_mem_hook_map.find((uint64_t)p);
		if (it != g_mem_hook_map.end()) {
			// printf("find key\n");
			assert(false);
		}
		else {
			// printf("not find key\n");
			rec = new CMemHookRec();
			if (NULL != rec)
			{
				rec->rec_sn = ++g_u64_malloc_cnt_all;
				if (2225 == rec->rec_sn)
				{
					rec->rec_sn = rec->rec_sn; // for debug mem leak
				}

				rec->u64_addr = (uint64_t)p;
				rec->num = num;
				rec->file = file;
				rec->line = line;

				g_mem_hook_map.insert(Map_Pair_mem_hook(rec->u64_addr, rec));
			}
		}
	}

	return p;
}

static void* my_CRYPTO_realloc(void* addr, size_t num, const char* file, int line)
{
	void* p = NULL;
	It_mem_hook it;
	CMemHookRec* rec = NULL;

	p = realloc(addr, num);
	if (NULL != p)
	{
		it = g_mem_hook_map.find((uint64_t)addr);
		if (it != g_mem_hook_map.end()) {
			// printf("find key\n");
			SAFE_DELETE(it->second);
			g_mem_hook_map.erase(it);
		}

		rec = new CMemHookRec();
		if (NULL != rec)
		{
			rec->rec_sn = ++g_u64_malloc_cnt_all;
			rec->u64_addr = (uint64_t)p;
			rec->num = num;
			rec->file = file;
			rec->line = line;

			g_mem_hook_map.insert(Map_Pair_mem_hook(rec->u64_addr, rec));
		}
	}
	return p;
}

static void my_CRYPTO_free(void* ptr, const char* file, int line)
{
	It_mem_hook it;
	CMemHookRec* rec = NULL;

	if (NULL != ptr)
	{
		free(ptr);

		it = g_mem_hook_map.find((uint64_t)ptr);
		if (it != g_mem_hook_map.end()) {
			// printf("find key\n");
			SAFE_DELETE(it->second);
			g_mem_hook_map.erase(it);
		}
		else {
			// printf("not find key\n");
			assert(false);
		}
	}
}


备注

此笔记前面实验的CRYPTO_get_alloc_counts()方法, 在mem_hook中用不了了, 因为将内存分配函数换掉了.
不过, 可以在上面这个实验上改改, 不hook内存分配函数, 只用 OPENSSL_init_crypto() + OPENSSL_cleanup() + CRYPTO_get_alloc_counts(), 应该也能看到内存泄漏的计数, 但是看不到哪个文件哪行发生的泄漏.

因为这个想法不好, 我就没去再实验.
毕竟有多个解决方法, 只取最优的那个.

mem_hook()这个方法好, 能看到发生泄漏的具体库文件和行数, 排查起来可操作性强.

备注

将 void CMemHookRec::show_info()的输出改了一下, 上面的实验代码已经更新.

void CMemHookRec::show_info()
{
	printf("entry sn = [%I64d],  %I64X %zu %s:%d\n", this->rec_sn, u64_addr, num, ((NULL != file) ? file : "NULL"), line);
}

这样的话, 等观测到内存泄漏时, 号码连续的就是一个openssl API的调用引起的内存泄漏, 更方便调试排查.
在这里插入图片描述

备注

  • 修正了内存泄漏 add OPENSSL_INIT_free(gp_ossl_init_setting);
  • 更新了输出格式,适应控制台和MFC程序

END

GitHub 加速计划 / ope / openssl
25.13 K
9.99 K
下载
传输层安全性/安全套接层及其加密库
最近提交(Master分支:1 个月前 )
fd39d1c8 Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Shane Lontis <shane.lontis@oracle.com> (Merged from https://github.com/openssl/openssl/pull/25095) 2 个月前
ae87c488 Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Shane Lontis <shane.lontis@oracle.com> (Merged from https://github.com/openssl/openssl/pull/25095) 2 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐