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

2024-02-26 13:44

本文主要是介绍openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法
    • 概述
    • 笔记
    • 查看特性列表
    • openssl3.2编译脚本 - 加入enable-crypto-mdebug
    • 看看有没有替代内存诊断的方法?
    • main.cpp
    • my_openSSL_lib.h
    • my_openSSL_lib.c
    • 备注
    • 备注
    • END

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.0set 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\commonnmake手工拷贝, 将 my_zlib_1d3.dll 拷贝到以下4个目录
.\
.\apps
.\fuzz
.\testnmake testnmake 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 APIint 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为trueopenssl_mdebug_begin();test_mem_leak(false);openssl_mdebug_end(true, true);/*run resulttest_mem_leak(test have mem leak)>> malloc_count = 0, realloc_count = 0, free_count = 0<< malloc_count = 1, realloc_count = 0, free_count = 0err : !!! mem leak happentest_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_APPLINKstatic 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.
在这里插入图片描述

END

这篇关于openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/749067

相关文章

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

最新Spring Security的基于内存用户认证方式

《最新SpringSecurity的基于内存用户认证方式》本文讲解SpringSecurity内存认证配置,适用于开发、测试等场景,通过代码创建用户及权限管理,支持密码加密,虽简单但不持久化,生产环... 目录1. 前言2. 因何选择内存认证?3. 基础配置实战❶ 创建Spring Security配置文件

Linux系统中查询JDK安装目录的几种常用方法

《Linux系统中查询JDK安装目录的几种常用方法》:本文主要介绍Linux系统中查询JDK安装目录的几种常用方法,方法分别是通过update-alternatives、Java命令、环境变量及目... 目录方法 1:通过update-alternatives查询(推荐)方法 2:检查所有已安装的 JDK方

SQL Server安装时候没有中文选项的解决方法

《SQLServer安装时候没有中文选项的解决方法》用户安装SQLServer时界面全英文,无中文选项,通过修改安装设置中的国家或地区为中文中国,重启安装程序后界面恢复中文,解决了问题,对SQLSe... 你是不是在安装SQL Server时候发现安装界面和别人不同,并且无论如何都没有中文选项?这个问题也

Java Thread中join方法使用举例详解

《JavaThread中join方法使用举例详解》JavaThread中join()方法主要是让调用改方法的thread完成run方法里面的东西后,在执行join()方法后面的代码,这篇文章主要介绍... 目录前言1.join()方法的定义和作用2.join()方法的三个重载版本3.join()方法的工作原

java内存泄漏排查过程及解决

《java内存泄漏排查过程及解决》公司某服务内存持续增长,疑似内存泄漏,未触发OOM,排查方法包括检查JVM配置、分析GC执行状态、导出堆内存快照并用IDEAProfiler工具定位大对象及代码... 目录内存泄漏内存问题排查1.查看JVM内存配置2.分析gc是否正常执行3.导出 dump 各种工具分析4.

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

Spring Boot从main方法到内嵌Tomcat的全过程(自动化流程)

《SpringBoot从main方法到内嵌Tomcat的全过程(自动化流程)》SpringBoot启动始于main方法,创建SpringApplication实例,初始化上下文,准备环境,刷新容器并... 目录1. 入口:main方法2. SpringApplication初始化2.1 构造阶段3. 运行阶

Olingo分析和实践之ODataImpl详细分析(重要方法详解)

《Olingo分析和实践之ODataImpl详细分析(重要方法详解)》ODataImpl.java是ApacheOlingoOData框架的核心工厂类,负责创建序列化器、反序列化器和处理器等组件,... 目录概述主要职责类结构与继承关系核心功能分析1. 序列化器管理2. 反序列化器管理3. 处理器管理重要方

Python错误AttributeError: 'NoneType' object has no attribute问题的彻底解决方法

《Python错误AttributeError:NoneTypeobjecthasnoattribute问题的彻底解决方法》在Python项目开发和调试过程中,经常会碰到这样一个异常信息... 目录问题背景与概述错误解读:AttributeError: 'NoneType' object has no at